diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..ae78c113ff --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,113 @@ + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 4987ecabe8..0000000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 08663fae31..d291b3d7cd 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -3,16 +3,13 @@ diff --git a/.idea/misc.xml b/.idea/misc.xml index 13c46297a6..8a8f75bfe2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,49 +1,14 @@ - - - + + + + - + - - - - - 1.8 - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 1c811baddb..ceb399db5e 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,10 @@ - + + + + diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..35eb1ddfbb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DevLibUtils/README.md b/DevLibUtils/README.md deleted file mode 100644 index ce976bd73d..0000000000 --- a/DevLibUtils/README.md +++ /dev/null @@ -1,1007 +0,0 @@ -## 使用 - -> ##### 只需要在 Application 中调用 `DevUtils.init()` 进行初始化就行 - -## 目录结构 - -``` -- dev.utils | 根目录 - - app | app相关工具类 - - anim | 动画相关 - - assist | 辅助类, 如 Camera,ScreenSensor - - cache | 缓存工具类 - - image | 图片相关处理 - - info | App信息, PackageInfo 等 - - logger | 日志库 DevLogger - - share | SharedPreferences 封装 - - toast | Toast、Toasty - - wifi | wifi、热点 - - common | java工具类, 不依赖android api - - assist | 各种快捷辅助类 - - cipher | 编/解码工具类 - - encrypt | 加密工具类 - - thread | 线程相关 - - validator | 数据校验工具类 -``` - -## 事项 - -- 内部存在两个日志工具类 -```java -// dev.utils.app - App 打印日志工具类 -LogPrintUtils -// dev.utils.common - Java Common 日志打印工具类 -JCLogUtils -``` - -- 需要开启日志, 单独调用 -```java -// 打开 lib 内部日志 -DevUtils.openLog(); -// 标示 debug 模式 -DevUtils.openDebug(); -``` - -- 部分api更新不及时或有遗漏等,`具体以对应的工具类为准` - -## API - -### `dev.utils.app.wifi` - -* **Wifi 热点工具类(兼容到Android 8.0) ->** [WifiHotUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiHotUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| createWifiConfigToAp | 创建Wifi配置信息(无其他操作,单独返回WifiConfig) | -| stratWifiAp | 开启Wifi热点 | -| closeWifiAp | 关闭Wifi热点 | -| getWifiApState | 获取Wifi热点状态 | -| getWifiApConfiguration | 获取Wifi热点配置信息 | -| setWifiApConfiguration | 设置Wifi热点配置信息 | -| isOpenWifiAp | 判断是否打开Wifi热点 | -| closeWifiApCheck | 关闭Wifi热点(判断当前状态) | -| isConnectHot | 是否有连接热点的设备 | -| getHotspotServiceIp | 获取热点主机ip地址 | -| getHotspotAllotIp | 获取连接上的子网关热点IP(一个) | -| getHotspotSplitIpMask | 获取热点拼接后的ip网关掩码 | -| intToString | 转换ip地址 | -| getApWifiSSID | 获取Wifi 热点名 | -| getApWifiPwd | 获取Wifi 热点密码 | -| setOnWifiAPListener | 设置Android Wifi监听(Android 8.0) | - - -* **wifi工具类 ->** [WifiHotUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getWifiManager | 获取wifi管理对象 | -| isOpenWifi | 判断是否打开wifi | -| openWifi | 打开WIFI | -| closeWifi | 关闭WIFI | -| toggleWifiEnabled | 自动切换wifi开关状态 | -| getWifiState | 获取当前WIFI连接状态 | -| startScan | 开始扫描wifi | -| getConfiguration | 获取已配置的网络 | -| getWifiList | 获取wifi网络列表 | -| getWifiInfo | 获取WifiInfo对象 | -| getMacAddress | 获取MAC地址 | -| getBSSID | 获取接入点的BSSID | -| getIPAddress | 获取IP地址 | -| getNetworkId | 获取连接的ID | -| getSSID | 获取SSID | -| formatSSID | 判断是否存在\"ssid\",存在则裁剪返回 | -| getPassword | 获取密码(经过处理) | -| getWifiType | 获取加密类型(int常量) - 判断String | -| getWifiTypeInt | 获取加密类型(int常量) - 判断int(String) | -| getWifiType | 获取加密类型(int常量) | -| getWifiTypeStr | 获取加密类型(String) | -| isConnNull | 判断是否连接为null - | -| isConnectAphot | 判断是否连接上Wifi(非连接中) | -| getSecurity | 获取Wifi配置,加密类型 | -| isExsitsPwd | 获知Wifi配置,是否属于密码加密类型 | -| isExsits | 查看以前是否也配置过这个网络 | -| delWifiConfig | 删除指定的 Wifi(SSID) 配置信息 | -| quickConnWifi | 快速连接Wifi(不使用静态ip方式) | -| createWifiConfig | 创建Wifi配置信息(无其他操作,单独返回WifiConfig) | -| removeWifiConfig | 移除某个Wifi配置信息 | -| disconnectWifi | 断开指定ID的网络 | -| setStaticWifiConfig | 设置静态Wifi信息 | -| setDNS | 设置DNS | -| setGateway | 设置网关 | -| setIpAddress | 设置Ip地址 | -| setStaticIpConfig | 设置Ip地址、网关、DNS(5.0之后) | - -### `dev.utils.app` - -* **无障碍功能工具类 ->** [AccessibilityUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AccessibilityUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| checkAccessibility | 检查是否开启无障碍功能 | -| isAccessibilitySettingsOn | 判断是否开启无障碍功能 | -| printAccessibilityEvent | 打印Event 日志 | - - -* **Acitivty 工具类 ->** [ActivityUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ActivityUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| isActivityExists | 判断是否存在指定的Activity | -| startHomeActivity | 回到桌面 -> 同点击Home键效果 | -| getLauncherActivity | 跳转到桌面 | -| getActivityIcon | 返回Activity 对应的图标 | -| getActivityLogo | 返回Activity 对应的Logo | - - -* **AlarmManager (全局定时器/闹钟)指定时长或以周期形式执行某项操作 ->** [AlarmUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AlarmUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| startAlarmIntent | 开启定时器 | -| stopAlarmIntent | 关闭定时器 | -| startAlarmService | 开启轮询服务 | -| stopAlarmService | 停止启轮询服务 | - - -* **分析记录工具类 ->** [AnalysisRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AnalysisRecordUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| init | 初始化操作 | -| record | 日志记录 | -| isHandler | 是否处理日志记录 | -| setHandler | 设置是否处理日志记录 | -| getLogFolderName | 获取文件日志名 | -| setLogFolderName | 设置日志文件夹名 | -| getLogStoragePath | 获取日志存储路径 | -| setLogStoragePath | 设置日志存储路径 | -| getStoragePath | getStoragePath | -| getFileName | getFileName | -| getFileFunction | getFileFunction | -| getFileIntervalTime | getFileIntervalTime | -| getFolderName | getFolderName | -| obtain | 获取记录分析文件信息 | -| getLogPath | 获取日志地址 | -| getIntervalTimeFolder | 获取时间间隔 - 文件夹 | - - -* **App通用工具类 ->** [AppCommonUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AppCommonUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getUUID | 获取设备唯一id | -| getRandomUUID | 获取随机数 唯一id | -| isFroyo | 是否在2.2版本及以上 | -| isGingerbread | 是否在2.3版本及以上 | -| isGingerbreadMR1 | 是否在2.3.3版本及以上 | -| isHoneycomb | 是否在3.0版本及以上 | -| isHoneycombMR1 | 是否在3.1版本及以上 | -| isIceCreamSandwich | 是否在4.0版本及以上 | -| isIceCreamSandwichMR1 | 是否在4.0.3版本及以上 | -| isJellyBean | 是否在4.1版本及以上 | -| isKitkat | 是否在4.4.2版本及以上 | -| isLollipop | 是否在5.0.1版本及以上 | -| isM | 是否在6.0版本及以上 | -| isN | 是否在7.0版本及以上 | -| isN_MR1 | 是否在7.1.1版本及以上 | -| isO | 是否在8.0版本及以上 | -| convertSDKVersion | 转换SDK版本 | - - -* **App(Android 工具类) ->** [AppUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AppUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getWindowManager | 通过上下文获取 WindowManager | -| getMetaData | 获取 Manifest Meta Data | -| getView | 获取View | -| getResources | getResources | -| getString | getString | -| getTheme | getTheme | -| getAssets | getAssets | -| getDrawable | getDrawable | -| getColor | getColor | -| getColorStateList | getColorStateList | -| getSystemService | getSystemService | -| getPackageManager | getPackageManager | -| getConfiguration | getConfiguration | -| getDisplayMetrics | getDisplayMetrics | -| getContentResolver | getContentResolver | -| getAppIcon | 获取app的图标 | -| getAppPackageName | 获取app 包名 | -| getAppName | 获取app 名 | -| getAppVersionName | 获取app版本名 - 对外显示 | -| getAppVersionCode | 获取app版本号 - 内部判断 | -| setLanguage | 对内设置指定语言 (app 多语言,单独改变app语言) | -| installApp | 安装 App(支持 8.0)的意图 | -| installAppSilent | 静默安装app | -| uninstallApp | 卸载 App | -| uninstallAppSilent | 静默卸载 App | -| isAppInstalled | 判断是否安装了应用 | -| isInstalledApp | 判断是否安装指定包名的APP | -| isAppRoot | 判断是否存在root 权限 | -| isAppDebug | 判断是否app 是否debug模式 | -| isAppSystem | 判断app 是否系统app | -| isAppForeground | 判断app 是否在前台 | -| launchApp | 打开app | -| launchAppDetailsSettings | 跳转到 专门的APP 设置详情页面 | -| launchAppDetails | 跳转到 专门的APP 应用商城详情页面 | -| getAppPath | 获取app 路径 /data/data/包名/.apk | -| getAppSignature | 获取app 签名 | -| getAppSignatureSHA1 | 获取 app sha1值 | -| openPDFFile | 启动本地应用打开PDF | -| openWordFile | 启动本地应用打开PDF | -| openOfficeByWPS | 调用WPS打开office文档 | - - -* **状态栏相关工具类 ->** [BarUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/BarUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getStatusBarHeight | 获取状态栏高度 | -| setStatusBarVisibility | 设置状态栏是否显示 | -| isStatusBarVisible | 判断状态栏是否显示 | -| setStatusBarLightMode | 设置状态是否高亮模式 | -| addMarginTopEqualStatusBarHeight | 添加状态栏同等高度到View的顶部 | -| subtractMarginTopEqualStatusBarHeight | 减去状态栏同等高度 | -| setStatusBarColor | 设置状态栏颜色 | -| setStatusBarAlpha | 设置状态栏透明度 | -| setStatusBarColor4Drawer | 设置状态栏的颜色 | -| setStatusBarAlpha4Drawer | 设置状态栏透明度 | -| getActionBarHeight | 返回 ActionBase 高度 | -| setNotificationBarVisibility | 设置通知栏是否显示 | -| getNavBarHeight | 获取 NavigationView 高度 | -| setNavBarVisibility | 设置导航栏是否可见(图标显示) | -| setNavBarImmersive | 设置沉浸模式 | -| isNavBarVisible | 判断顶部状态栏(图标)是否显示 | -| setNavBar | 设置是否显示状态栏图标等 | - - -* **亮度相关工具类 ->** [BrightnessUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/BrightnessUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| isAutoBrightnessEnabled | 判断是否开启自动调节亮度 | -| setAutoBrightnessEnabled | 设置是否开启自动调节亮度 | -| getBrightness | 获取屏幕亮度 | -| setBrightness | 设置屏幕亮度 | -| setWindowBrightness | 设置窗口亮度 | -| getWindowBrightness | 获取窗口亮度 | - - -* **摄像头相关工具类 ->** [CameraUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/CameraUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| isSupportReverse | 判断是否支持反转摄像头(是否存在前置摄像头) | -| checkCameraFacing | 检查是否有摄像头 | -| isFrontCamera | 判断是否使用前置摄像头 | -| isBackCamera | 判断是否使用后置摄像头 | -| isUseCameraFacing | 判断使用的视像头 | -| freeCameraResource | 释放摄像头资源 | -| initCamera | 初始化摄像头 | -| open | 打开摄像头 | - - -* ** ->** [CleanUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/CleanUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| cleanInternalCache | 清除本应用内部缓存(/data/data/com.xxx.xxx/cache) | -| cleanDatabases | 清除本应用所有数据库(/data/data/com.xxx.xxx/databases) | -| cleanSharedPreference | 清除本应用SharedPreference(/data/data/com.xxx.xxx/shared_prefs) | -| cleanDatabaseByName | 按名字清除本应用数据库 | -| cleanFiles | 清除/data/data/com.xxx.xxx/files下的内容 | -| cleanExternalCache | 清除外部cache下的内容(/mnt/sdcard/android/data/com.xxx.xxx/cache) | -| cleanCustomCache | 清除自定义路径下的文件,使用需小心,请不要误删。而且只支持目录下的文件删除 | -| cleanApplicationData | 清除本应用所有的数据 | -| getFolderSize | 获取文件夹大小 | -| getCacheSize | 获取缓存文件大小 | -| getFormatSize | 格式化单位 | - - -* **点击工具类 ->** [ClickUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ClickUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| isFastDoubleClick | 判断两次点击的间隔 小于默认间隔时间(1秒), 则认为是多次无效点击 | -| initConfig | 初始化配置信息 | -| putConfig | 添加配置信息 | -| removeConfig | 移除配置信息 | - - -* **剪贴板相关工具类 ->** [ClipboardUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ClipboardUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| copyText | 复制文本到剪贴板 | -| getText | 获取剪贴板的文本 | -| copyUri | 复制uri到剪贴板 | -| getUri | 获取剪贴板的uri | -| copyIntent | 复制意图到剪贴板 | -| getIntent | 获取剪贴板的意图 | - - -* **ContentResolver 工具类 ->** [ContentResolverUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ContentResolverUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| notifyMediaStore | 通知刷新本地资源 | -| insertImageIntoMediaStore | 添加图片到系统相册(包含原图、相册图, 会存在两张) - 想要一张, 直接调用 notifyMediaStore() | -| insertVideoIntoMediaStore | 添加视频到系统相册 | -| insertIntoMediaStore | 保存到系统相册 | - - -* **获取CPU信息工具类 ->** [CPUUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/CPUUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getProcessorsCount | 获取处理器的Java虚拟机的数量 | -| getSysCPUSerialNum | 获取手机CPU序列号 | -| getCpuInfo | 获取CPU 信息 | -| getCpuModel | 获取CPU 型号 | -| getMaxCpuFreq | 获取 CPU 最大频率(单位KHZ) | -| getMinCpuFreq | 获取 CPU 最小频率(单位KHZ) | -| getCurCpuFreq | 实时获取 CPU 当前频率(单位KHZ) | -| getCoresNumbers | 获取 CPU 几核 | -| getCpuName | 获取CPU名字 | -| getCMDOutputString | 获取 CMD 指令回调数据 | - - -* **数据库工具类 (导入导出等) ->** [DBUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/DBUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| startExportDatabase | 导出数据库 | -| startImportDatabase | 导入数据库 | -| getDBPath | 获取数据库路径 | - - -* **设备相关工具类 ->** [DeviceUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/DeviceUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getSysLanguage | 获取当前操作系统的语言 | -| getDeviceInfo | 获取设备信息 | -| getDeviceInfo2 | 获取设备信息 | -| handleDeviceInfo | 处理设备信息 | -| getSDKVersionName | 获取设备系统版本号 | -| getSDKVersion | 获取当前SDK 版本号 | -| getAndroidId | 获取Android id | -| isDeviceRooted | 判断设备是否 root | -| getMacAddress | 获取设备 MAC 地址 | -| getManufacturer | 获取设备厂商 如 Xiaomi | -| getModel | 获取设备型号 如 RedmiNote4X | -| shutdown | 关机 需要 root 权限或者系统权限 | -| reboot | 重启 需要 root 权限或者系统权限 | -| reboot2Recovery | 重启到 recovery 需要 root 权限 | -| reboot2Bootloader | 重启到 bootloader 需要 root 权限 | -| getBaseband_Ver | BASEBAND-VER 基带版本 | -| getLinuxCore_Ver | CORE-VER 内核版本 | - - -* **Dialog 操作相关工具类 ->** [DialogUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/DialogUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| closeDialog | 关闭Dialog | -| closeDialogs | 关闭多个Dialog | -| closePopupWindow | 关闭PopupWindow | -| closePopupWindows | 关闭多个PopupWindow | -| creDialog | 创建加载 Dialog | -| creAutoCloseDialog | 创建自动关闭dialog | - - -* **EditText 工具类 ->** [EditTextUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/EditTextUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| setMaxLengthAnText | 设置长度限制,并且设置内容 | -| setMaxLength | 设置长度限制 | -| getSelectionStart | 获取光标位置 | -| getText | 获取输入的内容 | -| getTextLength | 获取输入的内容长度 | -| setText | 设置内容 | -| insert | 追加内容 - 当前光标位置追加 | -| setSelectTop | 设置光标在第一位 | -| setSelectBottom | 设置光标在最后一位 | -| setSelect | 设置光标位置 | -| setKeyListener | 设置输入限制 | -| getNumberAndEnglishKeyListener | 限制只能输入字母和数字,默认弹出英文输入法 | -| getNumberKeyListener | 限制只能输入数字,默认弹出数字列表 | -| getMarkId | getMarkId | -| isOperate | isOperate | -| setOperate | setOperate | -| getOperateState | getOperateState | -| setOperateState | setOperateState | -| beforeTextChanged | 在文本变化前调用 | -| onTextChanged | 在文本变化后调用 | -| afterTextChanged | 在文本变化后调用 | - - -* **编码工具类 ->** [EncodeUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/EncodeUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| urlEncode | url编码 - UTF-8 | -| urlDecode | url 解码 - UTF-8 | -| base64Encode | base64 编码 => 去除\n 替换 + 和 - 号 | -| base64Encode2String | base64 编码 | -| base64Decode | base64 解码 | -| base64DecodeToString | base64 解码 | -| htmlEncode | html编码 | -| htmlDecode | html解码 | - - -* **错误信息处理工具类 ->** [ErrorUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ErrorUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getErrorMsg | 获取错误信息 | -| getThrowableMsg | 将异常栈信息转为字符串 | -| getThrowableNewLinesMsg | 获取错误信息(有换行) | - - -* **App 文件记录工具类 ->** [FileRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/FileRecordUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| appInit | App初始化调用方法 | -| saveErrorLog | 保存app错误日志 | -| saveLog | 保存app日志 | -| handleVariable | 处理可变参数 | - - -* **手电筒工具类 ->** [FlashlightUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/FlashlightUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getInstance | getInstance | -| register | 注册摄像头 | -| unregister | 注销摄像头 | -| setFlashlightOn | 打开闪光灯 | -| setFlashlightOff | 关闭闪光灯 | -| isFlashlightOn | 是否打开闪光灯 | -| isFlashlightEnable | 是否支持手机闪光灯 | - - -* **Handler 工具类, 默认开启一个 Handler,方便在各个地方随时执行主线程任务 ->** [HandlerUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/HandlerUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| init | 初始化操作 | -| getMainHandler | 获取主线程 Handler | -| postRunnable | 在主线程 Handler 中执行任务 | -| removeRunnable | 在主线程 Handler 中清除任务 | - - -* **Html 工具类 ->** [HtmlUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/HtmlUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| setHtmlText | 设置内容, 最终做处理 | -| addHtmlColor | 为给定的字符串添加HTML颜色标记 | -| addHtmlBold | 为给定的字符串添加HTML加粗标记 | -| addHtmlColorAndBlod | 为给定的字符串添加HTML颜色标记,以及加粗 | -| addHtmlUnderline | 为给定的字符串添加HTML下划线 | -| addHtmlIncline | 为给定的字符串添加HTML 字体倾斜 | -| keywordMadeRed | 将给定的字符串中所有给定的关键字标色 | -| keywordReplaceAll | 将给定的字符串中所有给定的关键字进行替换内容 | - - -* **Intent(意图) 相关工具类 ->** [IntentUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/IntentUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getInstallAppIntent | 获取安装 App(支持 8.0)的意图 | -| getUninstallAppIntent | 获取卸载 App 的意图 | -| getLaunchAppIntent | 获取打开 App 的意图 | -| getLaunchAppDetailsSettingsIntent | 获取 App 具体设置的意图 | -| getlaunchAppDetailIntent | 获取 到应用商店app详情界面的意图 | -| getShareTextIntent | 获取分享文本的意图 | -| getShareImageIntent | 获取分享图片的意图 | -| getComponentIntent | 获取其他应用组件的意图 | -| getShutdownIntent | 获取关机的意图 | -| getDialIntent | 获取跳至拨号界面意图 | -| getCallIntent | 获取拨打电话意图 | -| getSendSmsIntent | 获取发送短信界面的意图 | -| getCaptureIntent | 获取拍照的意图 | -| startSysSetting | 跳转到系统设置页面 | -| openWirelessSettings | 打开网络设置界面 - 3.0以下打开设置界面 | - - -* **软键盘相关辅助类 ->** [KeyBoardUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/KeyBoardUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| setDelayMillis | 设置延迟时间 | -| openKeyboard | 打开软键盘 | -| closeKeyboard | 关闭软键盘 | -| closeKeyBoardSpecial | 关闭软键盘 - 特殊处理 | -| toggleKeyboard | 自动切换键盘状态,如果键盘显示了则隐藏,隐藏着显示 | -| judgeView | 某个View里面的子View的View判断 | -| isSoftInputVisible | 判断软键盘是否可见 | -| registerSoftInputChangedListener | 注册软键盘改变监听器 | -| registerSoftInputChangedListener2 | 注册软键盘改变监听器 | -| fixSoftInputLeaks | 修复软键盘内存泄漏 在 Activity.onDestroy() 中使用 | -| onSoftInputChanged | onSoftInputChanged | - - -* **锁屏工具类 - 锁屏管理, 锁屏、禁用锁屏,判断是否锁屏 ->** [KeyguardUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/KeyguardUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getInstance | 获取 KeyguardUtils 实例 ,单例模式 | -| isKeyguardLocked | 是否锁屏 - android 4.1以上支持 | -| isKeyguardSecure | 是否有锁屏密码 - android 4.1以上支持 | -| inKeyguardRestrictedInputMode | 是否锁屏 - android 4.1 之前 | -| getKeyguardManager | getKeyguardManager | -| setKeyguardManager | setKeyguardManager | -| disableKeyguard | 屏蔽系统的屏保 | -| reenableKeyguard | 使能显示锁屏界面,如果你之前调用了disableKeyguard()方法取消锁屏界面,那么会马上显示锁屏界面。 | -| release | 释放资源 | -| newKeyguardLock | newKeyguardLock | -| getKeyguardLock | getKeyguardLock | -| setKeyguardLock | setKeyguardLock | - - -* **事件工具类 => AppReflectUtils(可以删除) ->** [ListenerUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ListenerUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getTouchListener | 获取 View 设置的 OnTouchListener | -| getListenerInfo | 获取 View ListenerInfo 对象(内部类) | -| getListenerInfoListener | 获取 View ListenerInfo 对象内部事件对象 | -| setOnClicks | 设置点击事件 | - - -* **定位相关工具类 ->** [LocationUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/LocationUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getLocation | 获取位置, 需要先判断是否开启了定位 | -| isGpsEnabled | 判断Gps是否可用 | -| isLocationEnabled | 判断定位是否可用 | -| openGpsSettings | 打开Gps设置界面 | -| register | 注册 | -| unregister | 注销 | -| getAddress | 根据经纬度获取地理位置 | -| getCountryName | 根据经纬度获取所在国家 | -| getLocality | 根据经纬度获取所在地 | -| getStreet | 根据经纬度获取所在街道 | -| isBetterLocation | 是否更好的位置 | -| isSameProvider | 是否相同的提供者 | -| getLastKnownLocation | 获取最后一次保留的坐标 | -| onLocationChanged | 当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发 | -| onStatusChanged | provider的在可用、暂时不可用和无服务三个状态直接切换时触发此函数 | - - -* **Android Manifest工具类 ->** [ManifestUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ManifestUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getMetaData | 获取 Manifest Meta Data | -| getAppVersion | 获取app版本信息 | -| getAppVersionCode | 获取app版本号 | -| getAppVersionName | 获取app版本信息 | - - -* **获取内存信息 ->** [MemoryUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/MemoryUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getMemoryInfo | 获取内存信息 | -| printMemoryInfo | Print Memory info. | -| getAvailMemory | Get available memory info. | -| getTotalMemory | 获取总内存大小 | -| getMemoryAvailable | 获取可用内存大小 | -| getMemInfoIype | 获取 type info | - - -* **网络管理工具类 ->** [NetWorkUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/NetWorkUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getMobileDataEnabled | 获取移动网络打开状态(默认属于未打开) | -| setMobileDataEnabled | 设置移动网络开关(无判断是否已开启移动网络) - 实际无效果, 非系统应用无权限 | -| isConnect | 判断是否连接了网络 | -| getConnectType | 获取连接的网络类型 | -| isConnWifi | 判断是否连接Wifi(连接上、连接中) | -| isConnMobileData | 判断是否连接移动网络(连接上、连接中) | -| isAvailable | 判断网络是否可用 | -| is4G | 判断是否4G网络 | -| getWifiEnabled | 判断wifi是否打开 | -| isWifiAvailable | 判断wifi数据是否可用 | -| getNetworkOperatorName | 获取网络运营商名称 - 中国移动、如中国联通、中国电信 | -| getNetworkType | 获取当前网络类型 | -| getDomainAddress | 获取域名ip地址 | -| getIPAddress | 获取IP地址 | - - -* **通知栏管理类 ->** [NotificationUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/NotificationUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getNotificationManager | 获取通知栏管理类 | -| cancelAll | 移除通知 - 移除所有通知(只是针对当前Context下的Notification) | -| cancel | 移除通知 - 移除标记为id的通知 (只是针对当前Context下的所有Notification) | -| notify | 进行通知 | -| crePendingIntent | 获取跳转id | -| creNotification | 创建通知栏对象 | -| obtain | 获取 Led 配置参数 | -| isEmpty | 判断是否为null | - - -* **工具类: OS 系统相关 ->** [OSUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/OSUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getRomType | 获取 ROM 类型 | -| getBaseVersion | getBaseVersion | -| getVersion | getVersion | - - -* **路径相关工具类 ->** [PathUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/PathUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getRootPath | 获取 Android 系统根目录 path: /system | -| getDataPath | 获取 data 目录 path: /data | -| getIntDownloadCachePath | 获取缓存目录 path: data/cache | -| getAppIntCachePath | 获取此应用的缓存目录 path: /data/data/package/cache | -| getAppIntFilesPath | 获取此应用的文件目录 path: /data/data/package/files | -| getAppIntDbPath | 获取此应用的数据库文件目录 path: /data/data/package/databases/name | -| getExtStoragePath | 获取 Android 外置储存的根目录 path: /storage/emulated/0 | -| getExtAlarmsPath | 获取闹钟铃声目录 path: /storage/emulated/0/Alarms | -| getExtDcimPath | 获取相机拍摄的照片和视频的目录 path: /storage/emulated/0/DCIM | -| getExtDocumentsPath | 获取文档目录 path: /storage/emulated/0/Documents | -| getExtDownloadsPath | 获取下载目录 path: /storage/emulated/0/Download | -| getExtMoviesPath | 获取视频目录 path: /storage/emulated/0/Movies | -| getExtMusicPath | 获取音乐目录 path: /storage/emulated/0/Music | -| getExtNotificationsPath | 获取提示音目录 path: /storage/emulated/0/Notifications | -| getExtPicturesPath | 获取图片目录 path: /storage/emulated/0/Pictures | -| getExtPodcastsPath | 获取 Podcasts 目录 path: /storage/emulated/0/Podcasts | -| getExtRingtonesPath | 获取铃声目录 path: /storage/emulated/0/Ringtones | -| getAppExtCachePath | 获取此应用在外置储存中的缓存目录 path: /storage/emulated/0/Android/data/package/cache | -| getAppExtFilePath | 获取此应用在外置储存中的文件目录 path: /storage/emulated/0/Android/data/package/files | -| getAppExtAlarmsPath | 获取此应用在外置储存中的闹钟铃声目录 path: /storage/emulated/0/Android/data/package/files/Alarms | -| getAppExtDcimPath | 获取此应用在外置储存中的相机目录 path: /storage/emulated/0/Android/data/package/files/DCIM | -| getAppExtDocumentsPath | 获取此应用在外置储存中的文档目录 path: /storage/emulated/0/Android/data/package/files/Documents | -| getAppExtDownloadPath | 获取此应用在外置储存中的闹钟目录 path: /storage/emulated/0/Android/data/package/files/Download | -| getAppExtMoviesPath | 获取此应用在外置储存中的视频目录 path: /storage/emulated/0/Android/data/package/files/Movies | -| getAppExtMusicPath | 获取此应用在外置储存中的音乐目录 path: /storage/emulated/0/Android/data/package/files/Music | -| getAppExtNotificationsPath | 获取此应用在外置储存中的提示音目录 path: /storage/emulated/0/Android/data/package/files/Notifications | -| getAppExtPicturesPath | 获取此应用在外置储存中的图片目录 path: /storage/emulated/0/Android/data/package/files/Pictures | -| getAppExtPodcastsPath | 获取此应用在外置储存中的 Podcasts 目录 path: /storage/emulated/0/Android/data/package/files/Podcasts | -| getAppExtRingtonesPath | 获取此应用在外置储存中的铃声目录 path: /storage/emulated/0/Android/data/package/files/Ringtones | -| getObbPath | 获取此应用的 Obb 目录 path: /storage/emulated/0/Android/obb/package 一般用来存放游戏数据包 | -| getFilePathByUri | getFilePathByUri | - - -* **权限请求工具类 ->** [PermissionUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/PermissionUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| isGranted | 判断是否授予了权限 | -| shouldShowRequestPermissionRationale | 是否拒绝了权限 - 拒绝过一次, 再次申请时, 弹出选择不再提醒并拒绝才会触发 true | -| permission | 申请权限初始化 | -| callBack | 设置回调方法 | -| request | 请求权限 | -| onRequestPermissionsResult | 请求权限回调 - 需要在 onRequestPermissionsResult 回调里面调用 | -| onGranted | 授权通过权限 | -| onDenied | 授权未通过权限 | -| start | start | -| onCreate | onCreate | - - -* **手机相关工具类 ->** [PhoneUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/PhoneUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| isSimReady | 判断是否装载sim卡 | -| getUserCountry | 获取Sim卡所属地区,非国内地区暂不支持播放 | -| judgeArea | 判断地区,是否属于国内 | -| isPhone | 判断设备是否是手机 | -| getIMEI | 获取 IMEI 码 | -| getIMSI | 获取 IMSI 码 | -| getIMSIIDName | 获取IMSI处理过后的简称 | -| getPhoneType | 获取移动终端类型 | -| isSimCardReady | 判断 sim 卡是否准备好 | -| getSimOperatorName | 获取 Sim 卡运营商名称 => 中国移动、如中国联通、中国电信 | -| getSimOperatorByMnc | 获取 Sim 卡运营商名称 => 中国移动、如中国联通、中国电信 | -| getDeviceId | 获取设备id | -| getSerialNumber | 返回设备序列化 | -| getAndroidId | 获取Android id | -| getUUID | 获取设备唯一id | -| getPhoneStatus | 获取手机状态信息 | -| dial | 跳至拨号界面 | -| call | 拨打电话 | -| sendSms | 跳至发送短信界面 | -| sendSmsSilent | 发送短信 | -| getAllContactInfo | 获取手机联系人 | -| getAllContactInfo2 | 获取手机联系人 | -| getContactNum | 打开手机联系人界面点击联系人后便获取该号码 | -| getAllSMS | 获取手机短信并保存到 xml 中 | -| getMtkTeleInfo | MTK Phone. 获取 MTK 神机的双卡 IMSI、IMSI 信息 | -| getMtkTeleInfo2 | MTK Phone. 获取 MTK 神机的双卡 IMSI、IMSI 信息 | -| getQualcommTeleInfo | Qualcomm Phone. 获取 高通 神机的双卡 IMSI、IMSI 信息 | -| getSpreadtrumTeleInfo | Spreadtrum Phone. 获取 展讯 神机的双卡 IMSI、IMSI 信息 | -| toString | toString | - - -* **轮询工具类 ->** [PollingUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/PollingUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| startPolling | 开启轮询 | -| stopPolling | 停止轮询 | -| startPollingService | 开启轮询服务 | -| stopPollingService | 停止启轮询服务 | - - -* **电源管理工具类 ->** [PowerManagerUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/PowerManagerUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getInstance | 获取 PowerManagerUtils 实例 ,单例模式 | -| isScreenOn | 屏幕是否打开(亮屏) | -| turnScreenOn | 唤醒屏幕/点亮亮屏 | -| turnScreenOff | 释放屏幕锁, 允许休眠时间自动黑屏 | -| getWakeLock | getWakeLock | -| setWakeLock | setWakeLock | -| getPowerManager | getPowerManager | -| setPowerManager | setPowerManager | -| setBright | 设置屏幕常亮 | -| setWakeLockToBright | 设置WakeLock 常亮 | - - -* **进程相关工具类 ->** [ProcessUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ProcessUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getProcessName | 获取进程号对应的进程名 | -| getCurProcessName | 获得当前进程的名字 | -| getForegroundProcessName | 获取前台线程包名 | -| getAllBackgroundProcesses | 获取后台服务进程 | -| killAllBackgroundProcesses | 杀死所有的后台服务进程 | -| killBackgroundProcesses | 杀死后台服务进程 | - - -* **资源文件工具类 ->** [ResourceUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ResourceUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getString | 获得字符串 | -| getColor | 获得颜色 | -| getDrawable | 获得Drawable | -| getResourceId | 根据资源名获得资源id | -| getDrawableId2 | 获取资源id | -| getLayoutId | 获取 layout 布局文件 | -| getStringId | 获取 string 值 | -| getDrawableId | 获取 drawable | -| getMipmapId | 获取 mipmap | -| getStyleId | 获取 style | -| getStyleableId | 获取 styleable | -| getAnimId | 获取 anim | -| getId | 获取 id | -| getColorId | color | -| readBytesFromAssets | 获取 Assets 资源文件数据 | -| readStringFromAssets | 读取字符串 来自 Assets文件 | -| readBytesFromRaw | 从res/raw 中获取内容。 | -| readStringFromRaw | 读取字符串 来自Raw 文件 | -| geFileToListFromAssets | 获取 Assets 资源文件数据(返回ArrayList 一行的全部内容属于一个索引) | -| geFileToListFromRaw | 从res/raw 中获取内容。(返回ArrayList 一行的全部内容属于一个索引) | -| saveAssetsFormFile | 从Assets 资源中获取内容并保存到本地 | -| saveRawFormFile | 从res/raw 中获取内容并保存到本地 | - - -* **屏幕相关工具类 ->** [ScreenUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ScreenUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getScreenWidth | 获取屏幕的宽度(单位:px) | -| getScreenHeight | 获取屏幕的高度(单位:px) | -| getScreenWidthHeightToPoint | 通过上下文获取屏幕宽度高度 | -| getScreenWidthHeight | 通过上下文获取屏幕宽度高度 | -| getScreenSize | 获取屏幕分辨率 | -| getScreenSizeOfDevice | 获取屏幕英寸 例5.5英寸 | -| getDensity | 通过上下文获取屏幕密度 | -| getDensityDpi | 通过上下文获取屏幕密度Dpi | -| getScaledDensity | 通过上下文获取屏幕缩放密度 | -| getXDpi | 获取 X轴 dpi | -| getYDpi | 获取 Y轴 dpi | -| getWidthDpi | 获取 宽度比例 dpi 基准 | -| getHeightDpi | 获取 高度比例 dpi 基准 | -| getScreenInfo | 获取屏幕信息 | -| setFullScreen | 设置屏幕为全屏 | -| setLandscape | 设置屏幕为横屏 | -| setPortrait | 设置屏幕为竖屏 | -| isLandscape | 判断是否横屏 | -| isPortrait | 判断是否竖屏 | -| getScreenRotation | 获取屏幕旋转角度 | -| isScreenLock | 判断是否锁屏 | -| isTablet | 判断是否是平板 | -| getStatusHeight | 获得状态栏的高度(无关 android:theme 获取状态栏高度) | -| getStatusBarHeight | 获取应用区域 TitleBar 高度 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) | -| setSleepDuration | 设置进入休眠时长 - 需添加权限 | -| getSleepDuration | 获取进入休眠时长 | -| snapShotWithStatusBar | 获取当前屏幕截图,包含状态栏 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) | -| snapShotWithoutStatusBar | 获取当前屏幕截图,不包含状态栏 (如果 android:theme 全屏了,则截图无状态栏) | -| getNavigationBarHeight | 获取底部导航栏高度 | -| checkDeviceHasNavigationBar | 检测是否具有底部导航栏 | - - -* **SD卡相关辅助类 ->** [SDCardUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/SDCardUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| isSDCardEnable | 判断SDCard是否正常挂载 | -| getSDCardFile | 获取SD卡路径(File对象) | -| getSDCardPath | 获取SD卡路径(无添加 -> / -> File.separator) | -| isSDCardEnablePath | 判断 SD 卡是否可用 | -| getSDCardPaths | 获取 SD 卡路径 | -| getAllBlockSize | 返回对应路径的空间总大小 | -| getAvailableBlocks | 返回对应路径的空闲空间(byte 字节大小) | -| getAlreadyBlock | 返回对应路径,已使用的空间大小 | -| getBlockSizeInfos | 返回对应路径的空间大小信息 | -| getSDTotalSize | 获得 SD 卡总大小 | -| getSDAvailableSize | 获得 SD 卡剩余容量,即可用大小 | -| getRomTotalSize | 获得机身内存总大小 | -| getRomAvailableSize | 获得机身可用内存 | -| getDiskCacheDir | 获取缓存地址 | -| getCacheFile | 获取缓存资源地址 | -| getCachePath | 获取缓存资源地址 | - - -* **服务相关工具类 ->** [ServiceUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ServiceUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| isServiceRunning | 判断服务是否运行 | -| getAllRunningService | 获取所有运行的服务 | -| startService | 启动服务 | -| stopService | 停止服务 | -| bindService | 绑定服务 | -| unbindService | 解绑服务 | - - -* **Shape 工具类 ->** [ShapeUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ShapeUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getDrawable | getDrawable | -| setDrawable | setDrawable | -| newBuilder | 创建新的 Shape Builder 对象 | -| newBuilderToLeft | 创建新的 Shape Builder 对象 | -| newBuilderToRight | 创建新的 Shape Builder 对象 | -| newBuilderToGradient | 创建渐变的 Shape Builder 对象 | -| build | 获取 Shape 工具类 | -| setRadius | 设置圆角 | -| setRadiusLeft | 设置圆角 | -| setRadiusRight | 设置圆角 | -| setCornerRadii | 内部处理方法 | -| setColor | 设置背景色(填充铺满) | -| setStroke | 设置边框颜色 | -| setSize | 设置大小 | - - -* **Shell 相关工具类 ->** [ShellUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ShellUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| execCmd | 是否是在 root 下执行命令 | - - -* **创建删除快捷图标工具类 ->** [ShortCutUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ShortCutUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| hasShortcut | 检测是否存在桌面快捷方式 | -| addShortcut | 为程序创建桌面快捷方式 | -| delShortcut | 删除程序的快捷方式 | - - -* **签名工具类(获取app,签名信息) ->** [SignaturesUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/SignaturesUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| toHexString | 进行转换 | -| signatureMD5 | 返回MD5 | -| signatureSHA1 | SHA1 | -| signatureSHA256 | SHA256 | -| isDebuggable | 判断签名是debug签名还是release签名 | -| getX509Certificate | 获取App 证书对象 | -| printSignatureName | 打印签名信息 | -| getSignaturesFromApk | 从APK中读取签名 | -| getCertificateFromApk | 从APK中读取签名 | - - -* **dp,px,sp转换、View获取宽高等 ->** [SizeUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/SizeUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| dipConvertPx | 根据手机的分辨率从 dp 的单位 转成为 px(像素) | -| pxConvertDip | 根据手机的分辨率从 px(像素) 的单位 转成为 dp | -| pxConvertSp | 根据手机的分辨率从 px(像素) 的单位 转成为 sp | -| spConvertPx | 根据手机的分辨率从 sp 的单位 转成为 px | -| dipConvertPx2 | 根据手机的分辨率从 dp 的单位 转成为 px(像素) 第二种 | -| spConvertPx2 | 根据手机的分辨率从 sp 的单位 转成为 px 第二种 | -| applyDimension | 各种单位转换 - 该方法存在于 TypedValue | -| forceGetViewSize | 获取视图的尺寸 | -| measureView | 测量视图尺寸 | -| getMeasuredWidth | 获取测量视图宽度 | -| getMeasuredHeight | 获取测量视图高度 | -| onGetSize | onGetSize | - - -* **颜色状态列表 工具类 ->** [StateListUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/StateListUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getColorStateList | 通过上下文获取 | -| createColorStateList | 创建 颜色状态列表 => createColorStateList("#ffffffff", "#ff44e6ff") | -| newSelector | 创建 Drawable选择切换 list => view.setBackground(Drawable) | - - -* **TextView 工具类 ->** [TextViewUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/TextViewUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getTextView | 获取TextView | -| getText | 获取文本 | -| setTextColor | 设置字体颜色 | -| setText | 设置内容 | -| setHtmlText | 设置 Html 内容 | -| setTVUnderLine | 给TextView设置下划线 | -| getTextHeight | 获取字体高度 | -| getTextTopOffsetHeight | 获取字体顶部偏移高度 | -| getTextWidth | 计算字体宽度 | -| getCenterRectY | 获取画布中间居中位置 | -| reckonTextSize | 通过需要的高度, 计算字体大小 | -| calcTextWidth | 计算第几位超过宽度 | - - -* **Uri 工具类 ->** [UriUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/UriUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getUriForFileToName | 返回处理后的Uri, 单独传递名字, 自动添加包名 ${applicationId} | -| getUriForFile | Return a content URI for a given file. | - - -* **震动相关工具类 ->** [VibrationUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/VibrationUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| vibrate | 震动 | -| cancel | 取消振动 | - - -* **View 操作相关工具类 ->** [ViewUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ViewUtils.java) - -| 方法 | 注释 | -| :-: | :-: | -| getContext | 获取上下文 | -| isEmpty | 判断View 是否为null | -| isVisibility | 判断View 是否显示 | -| isVisibilitys | 判断 View 是否都显示显示 | -| isVisibilityIN | 判断View 是否隐藏占位 | -| isVisibilityGone | 判断View 是否隐藏 | -| getVisibility | 获取显示的状态 (View.VISIBLE : View.GONE) | -| getVisibilityIN | 获取显示的状态 (View.VISIBLE : View.INVISIBLE) | -| setVisibility | 设置View显示状态 | -| setVisibilitys | 设置View 显示的状态 | -| toggleVisibilitys | 切换View 显示的状态 | -| toogleView | 切换View状态 | -| setViewImageRes | 设置View 图片资源 | -| findViewById | 初始化View | -| removeSelfFromParent | 把自身从父View中移除 | -| isTouchInView | 判断触点是否落在该View上 | -| requestLayoutParent | View 改变请求 | -| measureView | 测量 view | -| getViewWidth | 获取view的宽度 | -| getViewHeight | 获取view的高度 | -| getActivity | 获取view的上下文 | -| calcListViewItemHeight | 计算ListView Item 高度 | -| calcGridViewItemHeight | 计算GridView Item 高度 | -| getItemHeighet | 获取单独一个Item 高度 | \ No newline at end of file diff --git a/DevLibUtils/build.gradle b/DevLibUtils/build.gradle deleted file mode 100644 index 7084b5e1df..0000000000 --- a/DevLibUtils/build.gradle +++ /dev/null @@ -1,115 +0,0 @@ -apply plugin: 'com.android.library' - -def dev_utils_version = "1.0.0"; - -android { - compileSdkVersion 27 - buildToolsVersion "27.0.3" - - defaultConfig { - minSdkVersion 19 - targetSdkVersion 27 - versionCode 1 - versionName dev_utils_version - - /** - - - */ - manifestPlaceholders = [ dev_utils_version : dev_utils_version ] - } - - - buildTypes { - - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - debug { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - lintOptions { - abortOnError false - } -} - -dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:27.1.1' - implementation 'com.android.support:support-v4:27.1.1' -} - -// == 打包, 包含注释 == - -// 指定编码 -tasks.withType(JavaCompile) { - options.encoding = "UTF-8" -} - -// 打包源码 -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -task javadoc(type: Javadoc) { - failOnError false - source = android.sourceSets.main.java.sourceFiles - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - classpath += configurations.compile -} - -// 制作文档(Javadoc) -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives javadocJar -} - -// https://www.jianshu.com/p/44f2d0ee32c8 - -// https://www.jianshu.com/p/1777a634db5e - -// https://www.cnblogs.com/xinaixia/p/7660173.html - -// ==== 项目打包 aar ===== - -// 将被嵌入的应用打包成 aar 包,这也是 Android Studio 的一种新特性,可以将应用所使用的资源性文件一起打包。编译即可打包生成 aar 包,而且也不用担心漏掉资源问题。 -// 直接使用aar, 内部包含 jar -// build\outputs\aar\DevLibUtils-debug.aar, DevLibUtils-release.aar - -// ==== 项目打包 jar ===== - -// Terminal 输入 gradlew makeJar - -// 方式 A :点击 Android Studio 右侧(一般在这个位置)的 Gradle 面板,在项目或者该类库的目录中找到 Tasks -> other -> makeJar 命令,双击这个makeJar之后等一会就会编译好jar包。 - -// DevLibUtils.jar -// DevLibUtils_v1.0.jar - -def _BASENAME = "DevLibUtils"; -def _VERSION = "_v" + android.defaultConfig.versionName; -def _DestinationPath = "build"; //生成jar包的位置 -def zipFile = file('build/intermediates/bundles/debug/classes.jar'); //待打包文件位置 - -task deleteBuild(type:Delete){ - delete _DestinationPath + _BASENAME + _VERSION + ".jar" -} - -task makeJar(type:Jar){ - from zipTree(zipFile) - from fileTree(dir:'src/main',includes:['assets/**']) //将assets目录打入jar包 - baseName = _BASENAME + _VERSION - destinationDir = file(_DestinationPath) -} - -makeJar.dependsOn(deleteBuild, build) \ No newline at end of file diff --git a/DevLibUtils/src/main/AndroidManifest.xml b/DevLibUtils/src/main/AndroidManifest.xml deleted file mode 100644 index 50c5b4e4d4..0000000000 --- a/DevLibUtils/src/main/AndroidManifest.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/DevLibUtils/src/main/java/dev/DevUtils.java b/DevLibUtils/src/main/java/dev/DevUtils.java deleted file mode 100644 index b8fcb54e16..0000000000 --- a/DevLibUtils/src/main/java/dev/DevUtils.java +++ /dev/null @@ -1,298 +0,0 @@ -package dev; - -import android.app.Activity; -import android.app.Application; -import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; - -import dev.utils.BuildConfig; -import dev.utils.JCLogUtils; -import dev.utils.LogPrintUtils; -import dev.utils.app.AnalysisRecordUtils; -import dev.utils.app.FileRecordUtils; -import dev.utils.app.HandlerUtils; -import dev.utils.app.cache.DevCache; -import dev.utils.app.logger.DevLoggerUtils; -import dev.utils.app.share.SharedUtils; - -/** - * detail: 开发工具类 - * Created by Ttt - */ -public final class DevUtils { - - /** 禁止构造对象,保证只有一个实例 */ - private DevUtils() { - } - -// // DevUtils 实例 -// private static DevUtils INSTANCE = new DevUtils(); -// -// /** 获取 DevUtils 实例 ,单例模式 */ -// public static DevUtils getInstance() { -// return INSTANCE; -// } - - // --- - - /** 全局 Application 对象 */ - private static Application sApplication; - /** 全局上下文 - getApplicationContext() */ - private static Context sContext; - /** 获取当前线程,主要判断是否属于主线程 */ - private static Thread sUiThread; - /** 全局Handler,便于子线程快捷操作等 */ - private static Handler sHandler; - /** 是否内部debug模式 */ - private static boolean debug = false; - - /** - * 默认初始化方法 - 必须调用 - Application.onCreate 中调用 - * @param context 上下文 - */ - public static void init(Context context) { - // 设置全局上下文 - initContext(context); - // 初始化全局 Application - initApplication(context); - // 初始化Shared 工具类 - SharedUtils.init(context); - // 初始化记录文件配置 - FileRecordUtils.appInit(); - // 初始化记录工具类 - AnalysisRecordUtils.init(context); - // 初始化 DevLogger 配置 - DevLoggerUtils.appInit(context); - // 初始化Handler工具类 - HandlerUtils.init(context); - // 初始化缓存类 - DevCache.get(context); - // 保存当前线程信息 - sUiThread = Thread.currentThread(); - // 初始化全局Handler - 主线程 - sHandler = new Handler(Looper.getMainLooper()); - // 注册 Activity 生命周期监听 - registerActivityLifecycleCallbacks(sApplication); - } - - /** - * 初始化全局上下文 - * @param context - */ - private static void initContext(Context context) { - // 如果为null, 才进行判断处理 - if (DevUtils.sContext == null){ - // 防止传进来的为null - if (context == null) { - return; - } - DevUtils.sContext = context.getApplicationContext(); - } - } - - /** - * 初始化全局 Application - * @param context - */ - private static void initApplication(Context context) { - // 如果为null, 才进行判断处理 - if (DevUtils.sApplication == null){ - if (context == null){ - return; - } - Application mApplication = null; - try { - mApplication = (Application) context.getApplicationContext(); - } catch (Exception e){ - } - // 防止传进来的为null - if (mApplication == null) { - return; - } - DevUtils.sApplication = mApplication; - } - } - - /** - * 获取全局上下文 - * @return - */ - public static Context getContext() { - return DevUtils.sContext; - } - - /** - * 获取上下文(判断null,视情况返回全局上下文) - * @param context - */ - public static Context getContext(Context context) { - // 进行判断 - if (context != null){ - return context; - } - return DevUtils.sContext; - } - - /** - * 获取全局 Application - * @return - */ - public static Application getApplication(){ - return DevUtils.sApplication; - } - - /** - * 获取Handler - * @return - */ - public static Handler getHandler(){ - if (sHandler == null){ - // 初始化全局Handler - 主线程 - sHandler = new Handler(Looper.getMainLooper()); //Looper.myLooper(); - } - return sHandler; - } - - /** - * 执行UI 线程任务 => Activity 的 runOnUiThread(Runnable) - * @param action 若当前非UI线程则切换到UI线程执行 - */ - public static void runOnUiThread(Runnable action) { - if (Thread.currentThread() != sUiThread) { - sHandler.post(action); - } else { - action.run(); - } - } - - /** - * 执行UI 线程任务 => 延时执行 - * @param action - * @param delayMillis - */ - public static void runOnUiThread(Runnable action, long delayMillis){ - sHandler.postDelayed(action, delayMillis); - } - - /** - * 打开日志 - */ - public static void openLog() { - // 专门打印 Android 日志信息 - LogPrintUtils.setPrintLog(true); - // 专门打印 Java 日志信息 - JCLogUtils.setPrintLog(true); - } - - /** - * 标记debug模式 - */ - public static void openDebug() { - DevUtils.debug = true; - } - - /** - * 判断是否Debug模式 - * @return - */ - public static boolean isDebug() { - return debug; - } - - // ================== - // ==== Activity ==== - // ================== - - /** - * 注册绑定Activity 生命周期事件处理 - * @param application - */ - private static void registerActivityLifecycleCallbacks(Application application){ - if (application != null){ - // 先移除旧的监听 - application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks); - // 绑定新的监听 - application.registerActivityLifecycleCallbacks(lifecycleCallbacks); - } - } - - /** 保留当前(前台) Activity */ - private static Activity sCurActivity = null; - - /** - * 对Activity的生命周期事件进行集中处理。 - * http://blog.csdn.net/tongcpp/article/details/40344871 - */ - private static Application.ActivityLifecycleCallbacks lifecycleCallbacks = new Application.ActivityLifecycleCallbacks() { - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - - } - - @Override - public void onActivityStarted(Activity activity) { - - } - - @Override - public void onActivityResumed(Activity activity) { - // 保存当前Activity - DevUtils.sCurActivity = activity; - } - - @Override - public void onActivityPaused(Activity activity) { - - } - - @Override - public void onActivityStopped(Activity activity) { - - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) { - - } - - @Override - public void onActivityDestroyed(Activity activity) { - - } - }; - - /** - * 获取当前Activity - * @return - */ - public static Activity getCurActivity(){ - return DevUtils.sCurActivity; - } - - /** - * 判断是否相同的 Activity - * @param activity - * @return - */ - public static boolean isSameActivity(Activity activity){ - if (activity != null && DevUtils.sCurActivity != null){ - try { - return DevUtils.sCurActivity.getClass().getName().equals(activity.getClass().getName()); - } catch (Exception e){ - } - } - return false; - } - - // == 工具类版本 == - - /** - * 获取工具类版本 - * @return - */ - public static String getUtilsVersion(){ - return BuildConfig.VERSION_NAME; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/JCLogUtils.java b/DevLibUtils/src/main/java/dev/utils/JCLogUtils.java deleted file mode 100644 index eb43dfb6d1..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/JCLogUtils.java +++ /dev/null @@ -1,289 +0,0 @@ -package dev.utils; - -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.StringReader; -import java.io.StringWriter; - -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -/** - * detail: Java Common 日志打印工具类(简化版) - 项目内部使用 - 主要打印非 Android 日志 - * Created by Ttt - */ -public final class JCLogUtils { - - private JCLogUtils(){ - } - - // 普通信息模式 - public static final int INFO = 0; - // DEBUG模式 - public static final int DEBUG = 1; - // ERROR模式 - public static final int ERROR = 2; - - // = - - /** JSON格式内容缩进 */ - private static final int JSON_INDENT = 4; - /** 是否打印日志 上线 = false,开发、debug = true*/ - private static boolean JUDGE_PRINT_LOG = false; - /** 默认DEFAULT_TAG */ - private static final String DEFAULT_DEFAULT_TAG = JCLogUtils.class.getSimpleName(); - - /** - * 判断是否打印日志 - * @return - */ - public static boolean isPrintLog() { - return JUDGE_PRINT_LOG; - } - - /** - * 设置是否打印日志 - * @param judgePrintLog - */ - public static void setPrintLog(boolean judgePrintLog) { - JUDGE_PRINT_LOG = judgePrintLog; - } - - /** - * 判断是否为null - * @param str - * @return - */ - private static boolean isEmpty(String str) { - return (str == null || str.length() == 0); - } - - // = - - /** - * 最终打印日志方法(全部调用此方法) - * @param logType 打印日志类型 - * @param tag 打印Tag - * @param msg 打印消息 - */ - private static void printLog(int logType, String tag, String msg) { - switch (logType) { - case INFO: - LogPrintUtils.iTag(tag, msg); - case ERROR: - LogPrintUtils.eTag(tag, msg); - break; - case DEBUG: - default: - LogPrintUtils.dTag(tag, msg); - break; - } - // 打印信息 - if (isEmpty(tag)){ - System.out.println(msg); - } else { - System.out.println(tag + " : " + msg); - } - } - - /** - * 处理信息 - * @param message 打印信息 - * @param args 占位符替换 - * @return - */ - private static String createMessage(String message, Object... args) { - String result = null; - try { - if(message != null){ - if(args == null){ - // 动态参数为null - result = "params is null"; - } else { - // 格式化字符串 - result = (args.length == 0 ? message : String.format(message, args)); - } - } else { - // 打印内容为null - result = "message is null"; - } - } catch (Exception e) { - // 出现异常 - result = e.toString(); - } - return result; - } - - /** - * 拼接错误信息 - * @param throwable 错误异常 - * @param message 需要打印的消息 - * @param args 动态参数 - * @return - */ - private static String splitErrorMessage(Throwable throwable, String message, Object... args) { - String result = null; - try { - if(throwable != null){ - if(message != null){ - result = createMessage(message, args) + " : " + throwable.toString(); - } else { - result = throwable.toString(); - } - } else { - result = createMessage(message, args); - } - } catch (Exception e) { - result = e.toString(); - } - return result; - } - - // =================== 对外公开方法 ========================= - - // ========= 使用默认TAG ========= - - public static void d(String message, Object... args) { - dTag(DEFAULT_DEFAULT_TAG, message, args); - } - - public static void e(Throwable throwable) { - eTag(DEFAULT_DEFAULT_TAG, throwable); - } - - public void e(String message, Object... args) { - e(null, message, args); - } - - public static void e(Throwable throwable, String message, Object... args) { - eTag(DEFAULT_DEFAULT_TAG, throwable, message, args); - } - - public static void i(String message, Object... args) { - iTag(DEFAULT_DEFAULT_TAG, message, args); - } - - public static void json(String json) { - jsonTag(DEFAULT_DEFAULT_TAG, json); - } - - public static void xml(String xml) { - xmlTag(DEFAULT_DEFAULT_TAG, xml); - } - - // -- 日志打印方法 -- - - public static void dTag(String tag, String message, Object... args) { - if (JUDGE_PRINT_LOG){ - printLog(DEBUG, tag, createMessage(message, args)); - } - } - - public static void eTag(String tag, String message, Object... args) { - if(JUDGE_PRINT_LOG){ - printLog(ERROR, tag, createMessage(message, args)); - } - } - - public static void eTag(String tag, Throwable throwable) { - if(JUDGE_PRINT_LOG){ - printLog(ERROR, tag, splitErrorMessage(throwable, null)); - } - } - - public static void eTag(String tag, Throwable throwable, String message, Object... args) { - if(JUDGE_PRINT_LOG){ - printLog(ERROR, tag, splitErrorMessage(throwable, message, args)); - } - } - - public static void iTag(String tag, String message, Object... args) { - if(JUDGE_PRINT_LOG){ - printLog(INFO, tag, createMessage(message, args)); - } - } - - public static void jsonTag(String tag, String json) { - if (JUDGE_PRINT_LOG){ - // 判断传入JSON格式信息是否为null - if (isEmpty(json)) { - printLog(ERROR, tag, "Empty/Null json content"); - return; - } - try { - // 属于对象的JSON格式信息 - if (json.startsWith("{")) { - JSONObject jsonObject = new JSONObject(json); - // 进行缩进 - String message = jsonObject.toString(JSON_INDENT); - // 打印信息 - printLog(DEBUG, tag, message); - } else if (json.startsWith("[")) { - // 属于数据的JSON格式信息 - JSONArray jsonArray = new JSONArray(json); - // 进行缩进 - String message = jsonArray.toString(JSON_INDENT); - // 打印信息 - printLog(DEBUG, tag, message); - } - } catch (Exception e) { - String eHint = "null"; - if (e != null){ - Throwable throwable = e.getCause(); - if (throwable != null){ - eHint = throwable.getMessage(); - } else { - try { - eHint = e.getMessage(); - } catch (Exception e1){ - eHint = e1.getMessage(); - } - } - } - printLog(ERROR, tag, eHint + "\n" + json); - } - } - } - - public static void xmlTag(String tag, String xml) { - if (JUDGE_PRINT_LOG){ - // 判断传入XML格式信息是否为null - if (isEmpty(xml)) { - printLog(ERROR, tag, "Empty/Null xml content"); - return; - } - try { - Source xmlInput = new StreamSource(new StringReader(xml)); - StreamResult xmlOutput = new StreamResult(new StringWriter()); - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - transformer.transform(xmlInput, xmlOutput); - // 获取打印消息 - String message = xmlOutput.getWriter().toString().replaceFirst(">", ">\n"); - // 打印信息 - printLog(DEBUG, tag, message); - } catch (Exception e) { - String eHint = "null"; - if (e != null){ - Throwable throwable = e.getCause(); - if (throwable != null){ - eHint = throwable.getMessage(); - } else { - try { - eHint = e.getMessage(); - } catch (Exception e1){ - eHint = e1.getMessage(); - } - } - } - printLog(ERROR, tag, eHint + "\n" + xml); - } - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/LogPrintUtils.java b/DevLibUtils/src/main/java/dev/utils/LogPrintUtils.java deleted file mode 100644 index f5daf7b262..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/LogPrintUtils.java +++ /dev/null @@ -1,310 +0,0 @@ -package dev.utils; - -import android.text.TextUtils; -import android.util.Log; - -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.StringReader; -import java.io.StringWriter; - -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -/** - * detail: 日志打印工具类(简化版) - 项目内部使用 - * Created by Ttt - */ -public final class LogPrintUtils { - - private LogPrintUtils(){ - } - - /** JSON格式内容缩进 */ - private static final int JSON_INDENT = 4; - /** 是否打印日志 上线 = false,开发、debug = true*/ - private static boolean JUDGE_PRINT_LOG = false; - /** 默认DEFAULT_TAG */ - private static final String DEFAULT_DEFAULT_TAG = LogPrintUtils.class.getSimpleName(); - - /** - * 判断是否打印日志 - * @return - */ - public static boolean isPrintLog() { - return JUDGE_PRINT_LOG; - } - - /** - * 设置是否打印日志 - * @param judgePrintLog - */ - public static void setPrintLog(boolean judgePrintLog) { - JUDGE_PRINT_LOG = judgePrintLog; - } - - // = - - /** - * 最终打印日志方法(全部调用此方法) - * @param logType 打印日志类型 - * @param tag 打印Tag - * @param msg 打印消息 - */ - private static void printLog(int logType, String tag, String msg) { - switch (logType) { - case Log.ERROR: - Log.e(tag, msg); - break; - case Log.INFO: - Log.i(tag, msg); - break; - case Log.VERBOSE: - Log.v(tag, msg); - break; - case Log.WARN: - Log.w(tag, msg); - break; - case Log.ASSERT: - Log.wtf(tag, msg); - break; - case Log.DEBUG: - Log.d(tag, msg); - break; - default: - Log.d(tag, msg); - break; - } - } - - /** - * 处理信息 - * @param message 打印信息 - * @param args 占位符替换 - * @return - */ - private static String createMessage(String message, Object... args) { - String result = null; - try { - if(message != null){ - if(args == null){ - // 动态参数为null - result = "params is null"; - } else { - // 格式化字符串 - result = (args.length == 0 ? message : String.format(message, args)); - } - } else { - // 打印内容为null - result = "message is null"; - } - } catch (Exception e) { - // 出现异常 - result = e.toString(); - } - return result; - } - - /** - * 拼接错误信息 - * @param throwable 错误异常 - * @param message 需要打印的消息 - * @param args 动态参数 - * @return - */ - private static String splitErrorMessage(Throwable throwable, String message, Object... args) { - String result = null; - try { - if(throwable != null){ - if(message != null){ - result = createMessage(message, args) + " : " + throwable.toString(); - } else { - result = throwable.toString(); - } - } else { - result = createMessage(message, args); - } - } catch (Exception e) { - result = e.toString(); - } - return result; - } - - // =================== 对外公开方法 ========================= - - // ========= 使用默认TAG ========= - - public static void d(String message, Object... args) { - dTag(DEFAULT_DEFAULT_TAG, message, args); - } - - public static void e(Throwable throwable) { - eTag(DEFAULT_DEFAULT_TAG, throwable, null); - } - - public void e(String message, Object... args) { - e(null, message, args); - } - - public static void e(Throwable throwable, String message, Object... args) { - eTag(DEFAULT_DEFAULT_TAG, throwable, message, args); - } - - public static void w(String message, Object... args) { - wTag(DEFAULT_DEFAULT_TAG, message, args); - } - - public static void i(String message, Object... args) { - iTag(DEFAULT_DEFAULT_TAG, message, args); - } - - public static void v(String message, Object... args) { - vTag(DEFAULT_DEFAULT_TAG, message, args); - } - - public static void wtf(String message, Object... args) { - wtfTag(DEFAULT_DEFAULT_TAG, message, args); - } - - public static void json(String json) { - jsonTag(DEFAULT_DEFAULT_TAG, json); - } - - public static void xml(String xml) { - xmlTag(DEFAULT_DEFAULT_TAG, xml); - } - - // -- 日志打印方法 -- - - public static void dTag(String tag, String message, Object... args) { - if (JUDGE_PRINT_LOG){ - printLog(Log.DEBUG, tag, createMessage(message, args)); - } - } - - public static void eTag(String tag, String message, Object... args) { - if(JUDGE_PRINT_LOG){ - printLog(Log.ERROR, tag, createMessage(message, args)); - } - } - - public static void eTag(String tag, Throwable throwable) { - if(JUDGE_PRINT_LOG){ - printLog(Log.ERROR, tag, splitErrorMessage(throwable, null)); - } - } - - public static void eTag(String tag, Throwable throwable, String message, Object... args) { - if(JUDGE_PRINT_LOG){ - printLog(Log.ERROR, tag, splitErrorMessage(throwable, message, args)); - } - } - - public static void wTag(String tag, String message, Object... args) { - if(JUDGE_PRINT_LOG){ - printLog(Log.WARN, tag, createMessage(message, args)); - } - } - - public static void iTag(String tag, String message, Object... args) { - if(JUDGE_PRINT_LOG){ - printLog(Log.INFO, tag, createMessage(message, args)); - } - } - - public static void vTag(String tag, String message, Object... args) { - if(JUDGE_PRINT_LOG){ - printLog(Log.VERBOSE, tag, createMessage(message, args)); - } - } - - public static void wtfTag(String tag, String message, Object... args) { - if(JUDGE_PRINT_LOG){ - printLog(Log.ASSERT, tag, createMessage(message, args)); - } - } - - public static void jsonTag(String tag, String json) { - if (JUDGE_PRINT_LOG){ - // 判断传入JSON格式信息是否为null - if (TextUtils.isEmpty(json)) { - printLog(Log.ERROR, tag, "Empty/Null json content"); - return; - } - try { - // 属于对象的JSON格式信息 - if (json.startsWith("{")) { - JSONObject jsonObject = new JSONObject(json); - // 进行缩进 - String message = jsonObject.toString(JSON_INDENT); - // 打印信息 - printLog(Log.DEBUG, tag, message); - } else if (json.startsWith("[")) { - // 属于数据的JSON格式信息 - JSONArray jsonArray = new JSONArray(json); - // 进行缩进 - String message = jsonArray.toString(JSON_INDENT); - // 打印信息 - printLog(Log.DEBUG, tag, message); - } - } catch (Exception e) { - String eHint = "null"; - if (e != null){ - Throwable throwable = e.getCause(); - if (throwable != null){ - eHint = throwable.getMessage(); - } else { - try { - eHint = e.getMessage(); - } catch (Exception e1){ - eHint = e1.getMessage(); - } - } - } - printLog(Log.ERROR, tag, eHint + "\n" + json); - } - } - } - - public static void xmlTag(String tag, String xml) { - if (JUDGE_PRINT_LOG){ - // 判断传入XML格式信息是否为null - if (TextUtils.isEmpty(xml)) { - printLog(Log.ERROR, tag, "Empty/Null xml content"); - return; - } - try { - Source xmlInput = new StreamSource(new StringReader(xml)); - StreamResult xmlOutput = new StreamResult(new StringWriter()); - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - transformer.transform(xmlInput, xmlOutput); - // 获取打印消息 - String message = xmlOutput.getWriter().toString().replaceFirst(">", ">\n"); - // 打印信息 - printLog(Log.DEBUG, tag, message); - } catch (Exception e) { - String eHint = "null"; - if (e != null){ - Throwable throwable = e.getCause(); - if (throwable != null){ - eHint = throwable.getMessage(); - } else { - try { - eHint = e.getMessage(); - } catch (Exception e1){ - eHint = e1.getMessage(); - } - } - } - printLog(Log.ERROR, tag, eHint + "\n" + xml); - } - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/AccessibilityUtils.java b/DevLibUtils/src/main/java/dev/utils/app/AccessibilityUtils.java deleted file mode 100644 index 5f81c2ae9a..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/AccessibilityUtils.java +++ /dev/null @@ -1,246 +0,0 @@ -package dev.utils.app; - -import android.accessibilityservice.AccessibilityService; -import android.content.Intent; -import android.provider.Settings; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; - -import java.util.ArrayList; -import java.util.List; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 无障碍功能工具类 - * Created by Ttt - * https://www.jianshu.com/p/981e7de2c7be - * https://www.jianshu.com/p/65afab3d1e2a - * https://www.jianshu.com/p/f67e950d84f7 - * https://blog.csdn.net/nishitouzhuma/article/details/51584606 - * https://blog.csdn.net/jw_66666/article/details/76571897 - * https://blog.csdn.net/dd864140130/article/details/51794318 - * ==== - * AccessibilityService 在 API < 18 的时候使用 AccessibilityService - * - */ -public final class AccessibilityUtils { - - private AccessibilityUtils() { - } - - // 日志TAG - private static final String TAG = AccessibilityUtils.class.getSimpleName(); - - /** - * 检查是否开启无障碍功能 - * @return - */ - public static boolean checkAccessibility(){ - return checkAccessibility(DevUtils.getContext().getPackageName()); - } - - /** - * 检查是否开启无障碍功能 - * @param pkgName - * @return - */ - public static boolean checkAccessibility(String pkgName) { - // 判断辅助功能是否开启 - if (!AccessibilityUtils.isAccessibilitySettingsOn(pkgName)) { - // 引导至辅助功能设置页面 - DevUtils.getContext().startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - return false; - } - return true; - } - - /** - * 判断是否开启无障碍功能 - * @param pkgName - * @return - */ - public static boolean isAccessibilitySettingsOn(String pkgName) { - int accessibilityEnabled = 0; - try { - accessibilityEnabled = Settings.Secure.getInt(DevUtils.getContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED); - } catch (Settings.SettingNotFoundException e) { - LogPrintUtils.eTag(TAG, e, "isAccessibilitySettingsOn - Settings.Secure.ACCESSIBILITY_ENABLED"); - } - if (accessibilityEnabled == 1) { - try { - String services = Settings.Secure.getString(DevUtils.getContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); - if (services != null) { - return services.toLowerCase().contains(pkgName.toLowerCase()); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "isAccessibilitySettingsOn - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES"); - } - } - return false; - } - - // == 快捷方法 == - - /** - * 打印Event 日志 - * @param event - */ - public static void printAccessibilityEvent(AccessibilityEvent event){ - printAccessibilityEvent(event, TAG); - } - - /** - * 打印Event 日志 - * @param event - * @param tag - */ - public static void printAccessibilityEvent(AccessibilityEvent event, String tag){ - if (!LogPrintUtils.isPrintLog()){ - return; - } - LogPrintUtils.dTag(tag,"-------------------------------------------------------------"); - - int eventType = event.getEventType();//事件类型 - LogPrintUtils.dTag(tag, "packageName:" + event.getPackageName() + "");//响应事件的包名,也就是哪个应用才响应了这个事件 - LogPrintUtils.dTag(tag, "source:" + event.getSource() + "");//事件源信息 - LogPrintUtils.dTag(tag, "source class:" + event.getClassName() + "");//事件源的类名,比如android.widget.TextView - LogPrintUtils.dTag(tag, "event type(int):" + eventType + ""); - - switch (eventType) { - case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:// 通知栏事件 - LogPrintUtils.dTag(tag, "event type:TYPE_NOTIFICATION_STATE_CHANGED"); - break; - case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://窗体状态改变 - LogPrintUtils.dTag(tag, "event type:TYPE_WINDOW_STATE_CHANGED"); - break; - case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED://View获取到焦点 - LogPrintUtils.dTag(tag, "event type:TYPE_VIEW_ACCESSIBILITY_FOCUSED"); - break; - case AccessibilityEvent.TYPE_GESTURE_DETECTION_START: - LogPrintUtils.dTag(tag, "event type:TYPE_VIEW_ACCESSIBILITY_FOCUSED"); - break; - case AccessibilityEvent.TYPE_GESTURE_DETECTION_END: - LogPrintUtils.dTag(tag, "event type:TYPE_GESTURE_DETECTION_END"); - break; - case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: - LogPrintUtils.dTag(tag, "event type:TYPE_WINDOW_CONTENT_CHANGED"); - break; - case AccessibilityEvent.TYPE_VIEW_CLICKED: - LogPrintUtils.dTag(tag, "event type:TYPE_VIEW_CLICKED"); - break; - case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: - LogPrintUtils.dTag(tag, "event type:TYPE_VIEW_TEXT_CHANGED"); - break; - case AccessibilityEvent.TYPE_VIEW_SCROLLED: - LogPrintUtils.dTag(tag, "event type:TYPE_VIEW_SCROLLED"); - break; - case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: - LogPrintUtils.dTag(tag, "event type:TYPE_VIEW_TEXT_SELECTION_CHANGED"); - break; - } - - for (CharSequence txt : event.getText()) { - LogPrintUtils.dTag(tag, "text:" + txt);//输出当前事件包含的文本信息 - } - LogPrintUtils.dTag(tag, "-------------------------------------------------------------"); - } - - // === 其他处理 === - - /** - * 查找符合条件的节点 - * @param service - * @param text - * @return - */ - private List findAccessibilityNodeInfosByText(AccessibilityService service, String text) { - // 获取根节点 - AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow(); - // 取得当前激活窗体的根节点 - if (accessibilityNodeInfo == null) - return null; - // 通过文字找到当前的节点 - return accessibilityNodeInfo.findAccessibilityNodeInfosByText(text); - } - - /** - * 查找符合条件的节点 - * @param service - * @param id - * @return - */ - private List findAccessibilityNodeInfosByViewId(AccessibilityService service, String id) { - // 获取根节点 - AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow(); - // 取得当前激活窗体的根节点 - if (accessibilityNodeInfo == null) - return null; - // 通过文字找到当前的节点 - return accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id); - } - - /** - * 查找符合条件的节点 - * @param service - * @param text - * @param claName - * @return - */ - private ArrayList findAccessibilityNodeInfosByText(AccessibilityService service, String text, String claName) { - ArrayList lists = new ArrayList<>(); - // 获取根节点 - AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow(); - // 取得当前激活窗体的根节点 - if (accessibilityNodeInfo == null) - return lists; - // 通过文字找到当前的节点 - List nodes = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text); - for (int i = 0; i < nodes.size(); i++) { - AccessibilityNodeInfo node = nodes.get(i); - // 判断是否符合的类型 - if (node.getClassName().equals(claName) && node.isEnabled()) { - // 保存符合条件 - lists.add(node); - } - } - return lists; - } - -// //获取根节点 -// AccessibilityNodeInfo rootNode = getRootInActiveWindow(); -// //匹配Text获取节点 -// List list1 = rootNode.findAccessibilityNodeInfosByText("match_text"); -// //匹配id获取节点 -// List list2 = rootNode.findAccessibilityNodeInfosByViewId("match_id"); -// //获取子节点 -// AccessibilityNodeInfo infoNode = rootNode.getChild(index); - -// //模拟点击事件 -// target.performAction(AccessibilityNodeInfo.ACTION_CLICK); -// //模拟输入内容 -// ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); -// ClipData clip = ClipData.newPlainText("label", ""); -// clipboard.setPrimaryClip(clip); -// target.performAction(AccessibilityNodeInfo.ACTION_PASTE); - -// //后退键 -// performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); -// //Home键 -// performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME); -// //模拟左滑 -// performGlobalAction(AccessibilityService.GESTURE_SWIPE_LEFT); - - -// disableSelf() 禁用当前服务,也就是在服务可以通过该方法停止运行 -// findFoucs(int falg) 查找拥有特定焦点类型的控件 -// getRootInActiveWindow() 如果配置能够获取窗口内容,则会返回当前活动窗口的根结点 -// getSeviceInfo() 获取当前服务的配置信息 -// onAccessibilityEvent(AccessibilityEvent event) 有关AccessibilityEvent事件的回调函数.系统通过sendAccessibiliyEvent()不断的发送AccessibilityEvent到此处 -// performGlobalAction(int action) 执行全局操作,比如返回,回到主页,打开最近等操作 -// setServiceInfo(AccessibilityServiceInfo info) 设置当前服务的配置信息 -// getSystemService(String name) 获取系统服务 -// onKeyEvent(KeyEvent event) 如果允许服务监听按键操作,该方法是按键事件的回调,需要注意,这个过程发生了系统处理按键事件之前 -// onServiceConnected() 系统成功绑定该服务时被触发,也就是当你在设置中开启相应的服务,系统成功的绑定了该服务时会触发,通常我们可以在这里做一些初始化操作 -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/ActivityUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ActivityUtils.java deleted file mode 100644 index 45d88a48bd..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ActivityUtils.java +++ /dev/null @@ -1,178 +0,0 @@ -package dev.utils.app; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.ActivityOptionsCompat; -import android.support.v4.util.Pair; -import android.view.View; - -import java.util.List; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: Acitivty 工具类 - * Created by Ttt - */ -public final class ActivityUtils { - - private ActivityUtils() { - } - - // 日志TAG - private static final String TAG = ActivityUtils.class.getSimpleName(); - - /** - * 判断是否存在指定的Activity - * @param context 上下文 - * @param packageName 包名 - * @param className activity全路径类名 - * @return - */ - public static boolean isActivityExists(Context context, String packageName, String className) { - boolean result = true; - try { - Intent intent = new Intent(); - intent.setClassName(packageName, className); - if (context.getPackageManager().resolveActivity(intent, 0) == null) { - result = false; - } else if (intent.resolveActivity(context.getPackageManager()) == null) { - result = false; - } else { - List list = context.getPackageManager().queryIntentActivities(intent, 0); - if (list.size() == 0) { - result = false; - } - } - } catch (Exception e){ - result = false; - LogPrintUtils.eTag(TAG, e, "isActivityExists"); - } - return result; - } - - /** - * 回到桌面 -> 同点击Home键效果 - */ - public static void startHomeActivity() { - Intent homeIntent = new Intent(Intent.ACTION_MAIN); - homeIntent.addCategory(Intent.CATEGORY_HOME); - DevUtils.getContext().startActivity(homeIntent); - } - - /** - * 跳转到桌面 - * @return - */ - public static String getLauncherActivity() { - return getLauncherActivity(DevUtils.getContext().getPackageName()); - } - - /** - * 跳转到桌面 - * @param pkg - * @return - */ - public static String getLauncherActivity(@NonNull final String pkg) { - Intent intent = new Intent(Intent.ACTION_MAIN, null); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - PackageManager pm = DevUtils.getContext().getPackageManager(); - List info = pm.queryIntentActivities(intent, 0); - for (ResolveInfo aInfo : info) { - if (aInfo.activityInfo.packageName.equals(pkg)) { - return aInfo.activityInfo.name; - } - } - return null; - } - - /** - * 返回Activity 对应的图标 - * @param clz - * @return - */ - public static Drawable getActivityIcon(final Class clz) { - return getActivityIcon(new ComponentName(DevUtils.getContext(), clz)); - } - - /** - * 返回Activity 对应的图标 - * @param activityName - * @return - */ - public static Drawable getActivityIcon(final ComponentName activityName) { - try { - return DevUtils.getContext().getPackageManager().getActivityIcon(activityName); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getActivityIcon"); - return null; - } - } - - /** - * 返回Activity 对应的Logo - * @param clz - * @return - */ - public static Drawable getActivityLogo(final Class clz) { - return getActivityLogo(new ComponentName(DevUtils.getContext(), clz)); - } - - /** - * 返回Activity 对应的Logo - * @param activityName - * @return - */ - public static Drawable getActivityLogo(final ComponentName activityName) { - try { - return DevUtils.getContext().getPackageManager().getActivityLogo(activityName); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getActivityLogo"); - return null; - } - } - - // == 以下方法使用介绍 == - // https://www.cnblogs.com/tianzhijiexian/p/4087917.html - // ActivityOptionsCompat.makeScaleUpAnimation(source, startX, startY, startWidth, startHeight) - - /** - * 设置跳转动画 - * @param context - * @param enterAnim - * @param exitAnim - * @return - */ - private static Bundle getOptionsBundle(final Context context, final int enterAnim, final int exitAnim) { - return ActivityOptionsCompat.makeCustomAnimation(context, enterAnim, exitAnim).toBundle(); - } - - /** - * 设置跳转动画 - * @param activity - * @param sharedElements - * @return - */ - private static Bundle getOptionsBundle(final Activity activity, final View[] sharedElements) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - int len = sharedElements.length; - @SuppressWarnings("unchecked") - Pair[] pairs = new Pair[len]; - for (int i = 0; i < len; i++) { - pairs[i] = Pair.create(sharedElements[i], sharedElements[i].getTransitionName()); - } - return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, pairs).toBundle(); - } - return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, null, null).toBundle(); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/AlarmUtils.java b/DevLibUtils/src/main/java/dev/utils/app/AlarmUtils.java deleted file mode 100644 index 672d675e5a..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/AlarmUtils.java +++ /dev/null @@ -1,60 +0,0 @@ -package dev.utils.app; - -import android.annotation.TargetApi; -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.Build; - -/** - * detail: AlarmManager (全局定时器/闹钟)指定时长或以周期形式执行某项操作 - * @author MaTianyu - * https://www.cnblogs.com/zyw-205520/p/4040923.html - */ -public final class AlarmUtils { - - private AlarmUtils(){ - } - - /** - * 开启定时器 - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static void startAlarmIntent(Context context, int triggerAtMillis, PendingIntent pendingIntent) { - AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - manager.set(AlarmManager.RTC_WAKEUP,triggerAtMillis, pendingIntent); - } - - /** - * 关闭定时器 - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static void stopAlarmIntent(Context context, PendingIntent pendingIntent) { - AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - manager.cancel(pendingIntent); - } - - /** - * 开启轮询服务 - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static void startAlarmService(Context context, int triggerAtMillis, Class cls, String action) { - Intent intent = new Intent(context, cls); - intent.setAction(action); - PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - startAlarmIntent(context, triggerAtMillis,pendingIntent); - } - - /** - * 停止启轮询服务 - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static void stopAlarmService(Context context, Class cls, String action) { - Intent intent = new Intent(context, cls); - intent.setAction(action); - PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - stopAlarmIntent(context, pendingIntent); - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/AnalysisRecordUtils.java b/DevLibUtils/src/main/java/dev/utils/app/AnalysisRecordUtils.java deleted file mode 100644 index 7488589a10..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/AnalysisRecordUtils.java +++ /dev/null @@ -1,706 +0,0 @@ -package dev.utils.app; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Environment; -import android.support.annotation.IntDef; -import android.text.TextUtils; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.reflect.Field; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import dev.utils.LogPrintUtils; - -/** - * detail: 分析记录工具类 - * Created by Ttt - */ -public final class AnalysisRecordUtils { - - private AnalysisRecordUtils() { - } - - // 日志TAG - private static final String TAG = AnalysisRecordUtils.class.getSimpleName(); - - // 上下文 - private static Context sContext; - // 日志文件夹名字(目录名) - private static String logFolderName = "LogRecord"; - // 日志存储路径 - private static String logStoragePath; - // 是否处理保存 - private static boolean isHandler = true; - - /** - * 初始化操作 - * @param context - */ - public static void init(Context context) { - if (context != null) { - sContext = context.getApplicationContext(); - } - // 初始化设备信息 - getDeviceInfo(); - // 初始化 App 信息 - getAppInfo(); - // 如果为null, 才设置 - if (TextUtils.isEmpty(logStoragePath)){ - // 获取根路径 - logStoragePath = FileInfo.getDiskCacheDir(sContext); - } - } - - // === 对外提供方法 === - - /** - * 日志记录 - * @param fileInfo - * @param args - * @return log 直接打印 - */ - public static String record(FileInfo fileInfo, String... args) { - // 如果不处理, 则直接跳过 - if (!isHandler){ - return "record not handler"; - } - if (fileInfo != null) { - if (args != null && args.length != 0) { - return saveLogRecord(fileInfo, args); - } - // 无数据记录 - return "no data record"; - } - // 信息为null - return "info is null"; - } - - /** - * 是否处理日志记录 - * @return - */ - public static boolean isHandler() { - return isHandler; - } - - /** - * 设置是否处理日志记录 - * @param isHandler - */ - public static void setHandler(boolean isHandler) { - AnalysisRecordUtils.isHandler = isHandler; - } - - /** - * 获取文件日志名 - * @return - */ - public static String getLogFolderName() { - return logFolderName; - } - - /** - * 设置日志文件夹名 - * @param logFolderName - */ - public static void setLogFolderName(String logFolderName) { - AnalysisRecordUtils.logFolderName = logFolderName; - } - - /** - * 获取日志存储路径 - * @return - */ - public static String getLogStoragePath() { - return logStoragePath; - } - - /** - * 设置日志存储路径 - * @param logStoragePath - */ - public static void setLogStoragePath(String logStoragePath) { - AnalysisRecordUtils.logStoragePath = logStoragePath; - } - - // ===== 内部方法 ===== - - /** - * 最终保存方法 - * @param fileInfo - * @param args - */ - private static String saveLogRecord(FileInfo fileInfo, String... args) { - // 如果不处理, 则直接跳过 - if (!isHandler){ - return "record not handler"; - } - // 文件信息为null, 则不处理 - if (fileInfo == null){ - return "info is null"; - } - // 如果文件地址为null, 则不处理 - if (TextUtils.isEmpty(fileInfo.getFileName())){ - // 文件名为null - return "fileName is null"; - } - // 获取文件名 - String fName = fileInfo.getFileName(); - // 获取文件提示 - String fHint = fileInfo.getFileFunction(); - try { - // 获取处理的日志 - String logContent = splitLog(args); - // 日志保存路径 - String logPath = fileInfo.getLogPath(); - // 获取日志地址 - String logFile = logPath + File.separator + fName; - // 返回地址 - File file = new File(logFile); - // 判断是否存在 - if (file.exists()) { - // 追加内容 - appendFile(logFile, logContent); - } else { - // ==== 首次则保存设备、App 信息 ==== - StringBuffer sBuffer = new StringBuffer(); - sBuffer.append("【设备信息】"); - sBuffer.append(NEW_LINE_STR); - sBuffer.append("==========================="); - sBuffer.append(NEW_LINE_STR); - sBuffer.append(getDeviceInfo()); - sBuffer.append("==========================="); - sBuffer.append(NEW_LINE_STR); - - sBuffer.append(NEW_LINE_STR); - sBuffer.append(NEW_LINE_STR); - sBuffer.append("【版本信息】"); - sBuffer.append(NEW_LINE_STR); - sBuffer.append("==========================="); - sBuffer.append(NEW_LINE_STR); - sBuffer.append(getAppInfo()); - sBuffer.append(NEW_LINE_STR); - sBuffer.append("==========================="); - sBuffer.append(NEW_LINE_STR); - - sBuffer.append(NEW_LINE_STR); - sBuffer.append(NEW_LINE_STR); - sBuffer.append("【文件信息】"); - sBuffer.append(NEW_LINE_STR); - sBuffer.append("==========================="); - sBuffer.append(NEW_LINE_STR); - sBuffer.append(fHint); - sBuffer.append(NEW_LINE_STR); - sBuffer.append("==========================="); - sBuffer.append(NEW_LINE_STR); - // 创建文件夹,并且进行处理 - saveFile(sBuffer.toString(), logPath, fName); - // 追加内容 - appendFile(logFile, logContent); - } - // 返回打印日志 - return logContent; - } catch (Exception ignore) { - // 捕获异常 - return "catch error"; - } - } - - /** - * 拼接日志 - * @param args - * @return - */ - private static String splitLog(String... args) { - StringBuffer sBuffer = new StringBuffer(); - // 增加换行 - sBuffer.append(NEW_LINE_STR); - sBuffer.append(NEW_LINE_STR); - // 获取保存时间 - sBuffer.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); - // 追加个边距 - sBuffer.append(" => "); - // 循环追加内容 - for (int i = 0, c = args.length; i < c; i++) { - // 追加保存内容 - sBuffer.append(args[i]); - } - return sBuffer.toString(); - } - - // === 设备信息统计 ==== - - // app 信息 - private static String APP_INFO_STR = null; - // 设备信息 - private static String DEVICE_INFO_STR = null; - // 用来存储设备信息 - private static HashMap DEVICE_INFO_MAPS = new HashMap<>(); - // 换行字符串 - private static final String NEW_LINE_STR = System.getProperty("line.separator"); - - /** - * 获取设备信息 - * @return - */ - private static String getDeviceInfo() { - if (DEVICE_INFO_STR != null) { - return DEVICE_INFO_STR; - } - // 获取设备信息 - getDeviceInfo(DEVICE_INFO_MAPS); - // 转换设备信息 - handleDeviceInfo("获取设备信息失败"); - // 返回设备信息 - return DEVICE_INFO_STR; - } - - /** - * 获取设备信息 - * @param dInfoMaps 传入设备信息传出HashMap - */ - private static void getDeviceInfo(HashMap dInfoMaps) { - // 获取设备信息类的所有申明的字段,即包括public、private和proteced, 但是不包括父类的申明字段。 - Field[] fields = Build.class.getDeclaredFields(); - // 遍历字段 - for (Field field : fields) { - try { - // 取消java的权限控制检查 - field.setAccessible(true); - // 获取类型对应字段的数据,并保存 - dInfoMaps.put(field.getName(), field.get(null).toString()); - } catch (Exception e) { - } - } - } - - /** - * 处理设备信息 - * @param eHint 错误提示,如获取设备信息失败 - */ - private static String handleDeviceInfo(String eHint) { - try { - // 如果不为null,则直接返回之前的信息 - if (!TextUtils.isEmpty(DEVICE_INFO_STR)) { - return DEVICE_INFO_STR; - } - // 初始化 StringBuffer,拼接字符串 - StringBuffer sBuffer = new StringBuffer(); - // 获取设备信息 - Iterator> mapIter = DEVICE_INFO_MAPS.entrySet().iterator(); - // 遍历设备信息 - while (mapIter.hasNext()) { - // 获取对应的key-value - Map.Entry rnEntry = mapIter.next(); - String rnKey = rnEntry.getKey(); // key - String rnValue = rnEntry.getValue(); // value - // 保存设备信息 - sBuffer.append(rnKey); - sBuffer.append(" = "); - sBuffer.append(rnValue); - sBuffer.append(NEW_LINE_STR); - } - // 保存设备信息 - DEVICE_INFO_STR = sBuffer.toString(); - // 返回设备信息 - return DEVICE_INFO_STR; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleDeviceInfo"); - } - return eHint; - } - - /** - * 获取app 信息 - * @return - */ - private static String getAppInfo() { - // 如果不为null,则直接返回之前的信息 - if (!TextUtils.isEmpty(APP_INFO_STR)) { - return APP_INFO_STR; - } - try { - StringBuffer sBuffer = new StringBuffer(); - // - - PackageManager pm = sContext.getPackageManager(); - PackageInfo pi = pm.getPackageInfo(sContext.getPackageName(), PackageManager.GET_ACTIVITIES); - if (pi != null) { - String versionName = pi.versionName == null ? "null" : pi.versionName; - String versionCode = pi.versionCode + ""; - // 保存版本信息 - sBuffer.append("versionName: " + versionName); - sBuffer.append("\nversionCode: " + versionCode); - // 保存其他信息 - sBuffer.append("\npackageName: " + pi.packageName); // 保存包名 - // 赋值版本信息 - APP_INFO_STR = sBuffer.toString(); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppInfo"); - } - return APP_INFO_STR; - } - - // == 日志保存时间 == - // DEFAULT - 默认天,在根目录下 - public static final int DEFAULT = 0; - // 小时 - public static final int HH = 1; - // 分钟 - public static final int MM = 2; - // 秒 - public static final int SS = 3; - - //用 @IntDef "包住" 常量; - // @Retention 定义策略 - // 声明构造器 - @IntDef({ DEFAULT, HH, MM, SS}) - @Retention(RetentionPolicy.SOURCE) - public @interface TIME {} - - /** - * detail: 记录文件信息实体类 - * Created by Ttt - */ - public static class FileInfo { - - // 存储路径 - private String storagePath; - - // 文件夹名 - private String folderName; - - // 文件名 如: xxx.txt - private String fileName; - - // 文件记录的功能 - private String fileFunction; - - // 文件记录间隔时间 如: HH - private @TIME int fileIntervalTime = DEFAULT; - - // = 构造函数 = - - private FileInfo(String storagePath, String folderName, String fileName, String fileFunction, @TIME int fileIntervalTime) { - this.storagePath = storagePath; - this.folderName = folderName; - this.fileName = fileName; - this.fileFunction = fileFunction; - this.fileIntervalTime = fileIntervalTime; - } - - // = get/set 方法 = - - public String getStoragePath() { - if (TextUtils.isEmpty(storagePath)){ - return storagePath = getLogStoragePath(); - } - return storagePath; - } - - public String getFileName() { - return fileName; - } - - public String getFileFunction() { - return fileFunction; - } - - public int getFileIntervalTime() { - return fileIntervalTime; - } - - public String getFolderName(){ - if (TextUtils.isEmpty(folderName)){ - return folderName = getLogFolderName(); - } - return folderName; - } - - /** - * 获取记录分析文件信息 - * @param fileName - * @param fileFunction - * @return - */ - public static FileInfo obtain(String fileName, String fileFunction) { - return new FileInfo(null, null, fileName, fileFunction, DEFAULT); - } - - /** - * 获取记录分析文件信息 - * @param folderName - * @param fileName - * @param fileFunction - * @return - */ - public static FileInfo obtain(String folderName, String fileName, String fileFunction) { - return new FileInfo(null, folderName, fileName, fileFunction, DEFAULT); - } - - /** - * 获取记录分析文件信息 - * @param storagePath - * @param folderName - * @param fileName - * @param fileFunction - * @return - */ - public static FileInfo obtain(String storagePath, String folderName, String fileName, String fileFunction) { - return new FileInfo(storagePath, folderName, fileName, fileFunction, DEFAULT); - } - - // == - - /** - * 获取记录分析文件信息 - * @param fileName - * @param fileFunction - * @param fileIntervalTime - * @return - */ - public static FileInfo obtain(String fileName, String fileFunction, @TIME int fileIntervalTime) { - return new FileInfo(null, null, fileName, fileFunction, fileIntervalTime); - } - - /** - * 获取记录分析文件信息 - * @param folderName - * @param fileName - * @param fileFunction - * @param fileIntervalTime - * @return - */ - public static FileInfo obtain(String folderName, String fileName, String fileFunction, @TIME int fileIntervalTime) { - return new FileInfo(null, folderName, fileName, fileFunction, fileIntervalTime); - } - - /** - * 获取记录分析文件信息 - * @param storagePath - * @param folderName - * @param fileName - * @param fileFunction - * @param fileIntervalTime - * @return - */ - public static FileInfo obtain(String storagePath, String folderName, String fileName, String fileFunction, @TIME int fileIntervalTime) { - return new FileInfo(storagePath, folderName, fileName, fileFunction, fileIntervalTime); - } - - // = 内部处理方法 = - - /** - * 获取日志地址 - * @return - */ - public String getLogPath() { - // 返回拼接后的路径 - return getSavePath(getStoragePath(), logFolderName + File.separator + getDateNow("yyyy_MM_dd")) + getIntervalTimeFolder(); - } - - /** - * 获取时间间隔 - 文件夹 - * @return - */ - public String getIntervalTimeFolder() { - // 文件夹 - String folder = File.separator + getFolderName() + File.separator; - // 获取间隔时间 - int iTime = getFileIntervalTime(); - // 进行判断 - switch (iTime) { - case DEFAULT: - return folder; - case HH: - case MM: - case SS: - // 小时格式 - String hh_Foramt = getDateNow("HH"); - // 判断属于小时格式 - if (iTime == HH){ - // /folder/HH/HH_小时/ => /LogSpace/HH/HH_15/ - return folder + "HH/HH_" + hh_Foramt + File.separator; - } else { - // 分钟格式 - String mm_Foramt = getDateNow("mm"); - // 判断是否属于分钟 - if (iTime == MM){ - // /folder/HH/HH_小时/MM_分钟/ => /LogSpace/HH/HH_15/MM/MM_55/ - return folder + "HH/HH_" + hh_Foramt + "/MM/MM_" + mm_Foramt + File.separator; - } else { // 属于秒 - // 秒格式 - String ss_Foramt = getDateNow("ss"); - // /folder/HH/HH_小时/MM_分钟/ => /LogSpace/HH/HH_15/MM/MM_55/SS_12/ - return folder + "HH/HH_" + hh_Foramt + "/MM/MM_" + mm_Foramt + "/SS_" + ss_Foramt + File.separator; - } - } - } - // 放到未知目录下 - return "/Unknown/"; - } - - /** - * 获取当前日期的字符串 - * @param format 日期格式,譬如:HH, mm, ss - * @return 字符串 - */ - private String getDateNow(String format) { - try { - Calendar cld = Calendar.getInstance(); - DateFormat df = new SimpleDateFormat(format); - return df.format(cld.getTime()); - } catch (Exception e) { - } - return null; - } - - // - - - /** - * 获取保存地址 - * @param storagePath 存储路径 - * @param fPath 文件地址 - * @return - */ - private String getSavePath(String storagePath, String fPath) { - // 获取保存地址 - File file = new File(storagePath, fPath); - // 防止不存在目录文件,自动创建 - createFolder(file); - // 返回头像地址 - return file.getAbsolutePath(); - } - - /** - * 获取缓存地址 - * @return - */ - private static String getDiskCacheDir(Context context) { - String cachePath; - // 判断SDCard是否挂载 - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - cachePath = context.getExternalCacheDir().getPath(); - } else { - cachePath = context.getCacheDir().getPath(); - } - // 防止不存在目录文件,自动创建 - createFolder(new File(cachePath)); - // 返回文件存储地址 - return cachePath; - } - - /** - * 判断某个文件夹是否创建,未创建则创建(纯路径 - 无文件名) - * @param file 文件夹路径 (无文件名字.后缀) - */ - private static boolean createFolder(final File file) { - if (file != null) { - try { - // 当这个文件夹不存在的时候则创建文件夹 - if (!file.exists()) { - // 允许创建多级目录 - return file.mkdirs(); - // 这个无法创建多级目录 - // rootFile.mkdir(); - } - return true; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "createFolder"); - } - } - return false; - } - } - - // = 内部处理方法 = - - /** - * 追加文件:使用FileWriter - * @param fPath 文件地址 - * @param text 追加内容 - */ - private static void appendFile(String fPath, String text) { - if (fPath == null || text == null) { - return; - } - File file = new File(fPath); - if (!file.exists()) { // 如果文件不存在,则跳过 - return; - } - FileWriter writer = null; - try { - // 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件 - writer = new FileWriter(file, true); - writer.write(text); - } catch (IOException e) { - LogPrintUtils.eTag(TAG, e, "appendFile"); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException e) { - } - } - } - } - - /** - * 保存文件 - * @param txt 保存内容 - * @param fPath 保存路径 - * @param fName 文件名.后缀 - * @return 是否保存成功 - */ - private static boolean saveFile(String txt, String fPath, String fName) { - try { - // 防止文件没创建 - createFile(fPath); - // 保存路径 - File sFile = new File(fPath, fName); - // 保存内容到一个文件 - FileOutputStream fos = new FileOutputStream(sFile); - fos.write(txt.getBytes()); - fos.close(); - return true; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "saveFile"); - } - return false; - } - - /** - * 判断某个文件夹是否创建,未创建则创建(纯路径 - 无文件名) - * @param fPath 文件夹路径 - */ - private static File createFile(String fPath) { - try { - File file = new File(fPath); - // 当这个文件夹不存在的时候则创建文件夹 - if(!file.exists()) { - // 允许创建多级目录 - file.mkdirs(); - // 这个无法创建多级目录 - // rootFile.mkdir(); - } - return file; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "createFile"); - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/AppCommonUtils.java b/DevLibUtils/src/main/java/dev/utils/app/AppCommonUtils.java deleted file mode 100644 index 7b7231c51d..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/AppCommonUtils.java +++ /dev/null @@ -1,219 +0,0 @@ -package dev.utils.app; - -import android.os.Build; - -import java.util.Random; -import java.util.UUID; - -/** - * detail: App通用工具类 - * Created by Ttt - */ -public final class AppCommonUtils { - - private AppCommonUtils(){ - } - - /** - * 获取设备唯一id - * @return - */ - public String getUUID(){ - return PhoneUtils.getUUID(); - } - - /** - * 获取随机数 唯一id - * @return - */ - public String getRandomUUID(){ - // 获取随机数 - String random1 = (900000 + new Random().nextInt(10000)) + ""; - // 获取随机数 - String random2 = (900000 + new Random().nextInt(10000)) + ""; - // 获取当前时间 - String cTime = Long.toString(System.currentTimeMillis()) + random1 + random2; - // 生成唯一随机uuid cTime.hashCode(), random1.hashCode() | random2.hashCode() - UUID randomUUID = new UUID(cTime.hashCode(), ((long) random1.hashCode() << 32) | random2.hashCode()); - // 获取uid - return randomUUID.toString(); - } - - // == 版本判断处理 == - - /** - * 是否在2.2版本及以上 - * @return 是否在2.2版本及以上 - */ - public static boolean isFroyo() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO; - } - - /** - * 是否在2.3版本及以上 - * @return 是否在2.3版本及以上 - */ - public static boolean isGingerbread() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; - } - - /** - * 是否在2.3.3版本及以上 - * @return 是否在2.3.3版本及以上 - */ - public static boolean isGingerbreadMR1() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1; - } - - /** - * 是否在3.0版本及以上 - * @return 是否在3.0版本及以上 - */ - public static boolean isHoneycomb() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; - } - - /** - * 是否在3.1版本及以上 - * @return 是否在3.1版本及以上 - */ - public static boolean isHoneycombMR1() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1; - } - - /** - * 是否在4.0版本及以上 - * @return 是否在4.0版本及以上 - */ - public static boolean isIceCreamSandwich() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; - } - - /** - * 是否在4.0.3版本及以上 - * @return 是否在4.0.3版本及以上 - */ - public static boolean isIceCreamSandwichMR1() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1; - } - - /** - * 是否在4.1版本及以上 - * @return 是否在4.1版本及以上 - */ - public static boolean isJellyBean() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; - } - - /** - * 是否在4.4.2版本及以上 - * @return 是否在4.4.2版本及以上 - */ - public static boolean isKitkat() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - } - - /** - * 是否在5.0.1版本及以上 - * @return 是否在5.0.1版本及以上 - */ - public static boolean isLollipop() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; - } - - /** - * 是否在6.0版本及以上 - * @return 是否在6.0版本及以上 - */ - public static boolean isM(){ - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; - } - - /** - * 是否在7.0版本及以上 - * @return 是否在7.0版本及以上 - */ - public static boolean isN(){ - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; - } - - /** - * 是否在7.1.1版本及以上 - * @return 是否在7.1.1版本及以上 - */ - public static boolean isN_MR1(){ - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1; - } - - /** - * 是否在8.0版本及以上 - * @return 是否在8.0版本及以上 - */ - public static boolean isO(){ - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; - } - - /** - * 转换SDK版本 - * @param sdkVersion - * @return - */ - public static String convertSDKVersion(int sdkVersion){ - // https://www.cnblogs.com/maogefff/p/7819076.html - switch (sdkVersion){ - case 1: - return "Android 1.0"; - case 2: - return "Android 1.1"; - case 3: - return "Android 1.5"; - case 4: - return "Android 1.6"; - case 5: - return "Android 2.0"; - case 6: - return "Android 2.0.1"; - case 7: - return "Android 2.1.x"; - case 8: - return "Android 2.2.x"; - case 9: - return "Android 2.3.0-2"; - case 10: - return "Android 2.3.3-4"; - case 11: - return "Android 3.0.x"; - case 12: - return "Android 3.1.x"; - case 13: - return "Android 3.2"; - case 14: - return "Android 4.0.0-2"; - case 15: - return "Android 4.0.3-4"; - case 16: - return "Android 4.1.x"; - case 17: - return "Android 4.2.x"; - case 18: - return "Android 4.3"; - case 19: - return "Android 4.4"; - case 20: - return "Android 4.4W"; - case 21: - return "Android 5.0"; - case 22: - return "Android 5.1"; - case 23: - return "Android 6.0"; - case 24: - return "Android 7.0"; - case 25: - return "Android 7.1.1"; - case 26: - return "Android 8.0"; - } - return "unknown"; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/AppUtils.java b/DevLibUtils/src/main/java/dev/utils/app/AppUtils.java deleted file mode 100644 index ae1b009e28..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/AppUtils.java +++ /dev/null @@ -1,970 +0,0 @@ -package dev.utils.app; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.ActivityManager; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.Signature; -import android.content.res.AssetManager; -import android.content.res.ColorStateList; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.support.annotation.ColorRes; -import android.support.annotation.DrawableRes; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.StringRes; -import android.support.v4.content.ContextCompat; -import android.util.DisplayMetrics; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager; - -import java.io.File; -import java.security.MessageDigest; -import java.util.List; -import java.util.Locale; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: App(Android 工具类) - * Created by Ttt - */ -public final class AppUtils { - - private AppUtils() { - } - - // 日志TAG - private static final String TAG = AppUtils.class.getSimpleName(); - - /** - * 通过上下文获取 WindowManager - * @return - */ - public static WindowManager getWindowManager() { - try { - return (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getWindowManager"); - } - return null; - } - -// /** -// * 通过上下文获取 DisplayMetrics (获取关于显示的通用信息,如显示大小,分辨率和字体) -// * @return -// */ -// public static DisplayMetrics getDisplayMetrics() { -// try { -// WindowManager wManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); -// if (wManager != null) { -// DisplayMetrics dMetrics = new DisplayMetrics(); -// wManager.getDefaultDisplay().getMetrics(dMetrics); -// return dMetrics; -// } -// } catch (Exception e) { -// LogPrintUtils.eTag(TAG, e, "getDisplayMetrics"); -// } -// return null; -// } - - /** - * 获取 Manifest Meta Data - * @param metaKey - * @return - */ - public static String getMetaData(String metaKey) { - try { - ApplicationInfo appInfo = DevUtils.getContext().getPackageManager().getApplicationInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_META_DATA); - String data = appInfo.metaData.getString(metaKey); - return data; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getMetaData"); - } - return null; - } - - // == 快捷获取方法 == - - /** - * 获取View - * @param resource - * @return - */ - public static View getView(@LayoutRes int resource){ - return getView(DevUtils.getContext(), resource); - } - - /** - * 获取View - * @param context - * @param resource - * @return - */ - public static View getView(Context context, @LayoutRes int resource){ - try { - return ((LayoutInflater) DevUtils.getContext(context).getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(resource, null); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getView"); - } - return null; - } - - public static Resources getResources() { - try { - return DevUtils.getContext().getResources(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getResources"); - } - return null; - } - - public static String getString(@StringRes int id) { - try { - return DevUtils.getContext().getResources().getString(id); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getString"); - } - return null; - } - - public static Resources.Theme getTheme() { - try { - return DevUtils.getContext().getTheme(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getTheme"); - } - return null; - } - - public static AssetManager getAssets() { - try { - return DevUtils.getContext().getAssets(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getAssets"); - } - return null; - } - - public static Drawable getDrawable(@DrawableRes int id) { - try { - return ContextCompat.getDrawable(DevUtils.getContext(), id); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getDrawable"); - } - return null; - } - - public static int getColor( @ColorRes int id) { - try { - return ContextCompat.getColor(DevUtils.getContext(), id); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getColor"); - } - return -1; - } - - public static ColorStateList getColorStateList(int id) { - try { - return ContextCompat.getColorStateList(DevUtils.getContext(), id); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getColorStateList"); - } - return null; - } - - public static T getSystemService(String name){ - try { - return (T) DevUtils.getContext().getSystemService(name); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getSystemService"); - } - return null; - } - - public static PackageManager getPackageManager(){ - try { - return DevUtils.getContext().getPackageManager(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getPackageManager"); - } - return null; - } - - public static Configuration getConfiguration() { - try { - return DevUtils.getContext().getResources().getConfiguration(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getConfiguration"); - } - return null; - } - - public static DisplayMetrics getDisplayMetrics() { - try { - return DevUtils.getContext().getResources().getDisplayMetrics(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getDisplayMetrics"); - } - return null; - } - - public static ContentResolver getContentResolver() { - try { - return DevUtils.getContext().getContentResolver(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getContentResolver"); - } - return null; - } - - /** - * 获取app的图标 - * @return - */ - public static Drawable getAppIcon() { - return getAppIcon(DevUtils.getContext().getPackageName()); - } - - /** - * 获取app的图标 - * @param packageName - * @return - */ - public static Drawable getAppIcon(final String packageName) { - if (isSpace(packageName)) return null; - try { - PackageManager pm = DevUtils.getContext().getPackageManager(); - PackageInfo pi = pm.getPackageInfo(packageName, 0); - return pi == null ? null : pi.applicationInfo.loadIcon(pm); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppIcon"); - return null; - } - } - - /** - * 获取app 包名 - * @return - */ - public static String getAppPackageName() { - return DevUtils.getContext().getPackageName(); - } - - /** - * 获取app 名 - * @return - */ - public static String getAppName() { - return getAppName(DevUtils.getContext().getPackageName()); - } - - /** - * 获取app 名 - * @param packageName - * @return - */ - public static String getAppName(final String packageName) { - if (isSpace(packageName)) return null; - try { - PackageManager pm = DevUtils.getContext().getPackageManager(); - PackageInfo pi = pm.getPackageInfo(packageName, 0); - return pi == null ? null : pi.applicationInfo.loadLabel(pm).toString(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppName"); - return null; - } - } - - /** - * 获取app版本名 - 对外显示 - * @return - */ - public static String getAppVersionName() { - return getAppVersionName(DevUtils.getContext().getPackageName()); - } - - /** - * 获取app版本名 - 对外显示 - * @param packageName The name of the package. - * @return - */ - public static String getAppVersionName(final String packageName) { - if (isSpace(packageName)) return null; - try { - PackageInfo pi = DevUtils.getContext().getPackageManager().getPackageInfo(packageName, 0); - return pi == null ? null : pi.versionName; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppVersionName"); - return null; - } - } - - /** - * 获取app版本号 - 内部判断 - * @return - */ - public static int getAppVersionCode() { - return getAppVersionCode(DevUtils.getContext().getPackageName()); - } - - /** - * 获取app版本号 - 内部判断 - * @param packageName The name of the package. - * @return - */ - public static int getAppVersionCode(final String packageName) { - if (isSpace(packageName)) return -1; - try { - PackageInfo pi = DevUtils.getContext().getPackageManager().getPackageInfo(packageName, 0); - return pi == null ? -1 : pi.versionCode; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppVersionCode"); - return -1; - } - } - - // = - - /** - * 对内设置指定语言 (app 多语言,单独改变app语言) - * @param locale - */ - public static void setLanguage(Locale locale) { - try { - // 获得res资源对象 - Resources resources = DevUtils.getContext().getResources(); - // 获得设置对象 - Configuration config = resources.getConfiguration(); - // 获得屏幕参数:主要是分辨率,像素等。 - DisplayMetrics dm = resources.getDisplayMetrics(); - // 语言 - config.locale = locale; - // 更新语言 - resources.updateConfiguration(config, dm); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "setLanguage"); - } - } - - // = - - /** - * 安装 App(支持 8.0)的意图 - * @param filePath The path of file. - * @param authority 7.0 及以上安装需要传入清单文件中的}的 authorities 属性 - * @return 是否可以跳转 - */ - public static boolean installApp(final String filePath, final String authority) { - return installApp(getFileByPath(filePath), authority); - } - - /** - * 安装 App(支持 8.0)的意图 - * @param file The file. - * @param authority 7.0 及以上安装需要传入清单文件中的}的 authorities 属性 - * @return 是否可以跳转 - */ - public static boolean installApp(final File file, final String authority) { - if (!isFileExists(file)) return false; - try { - DevUtils.getContext().startActivity(IntentUtils.getInstallAppIntent(file, authority, true)); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "installApp"); - return false; - } - return true; - } - - /** - * 安装 App(支持 8.0)的意图 - 回传 - * @param activity - * @param filePath - * @param authority - * @param requestCode - * @return - */ - public static boolean installApp(final Activity activity, final String filePath, final String authority, final int requestCode) { - return installApp(activity, getFileByPath(filePath), authority, requestCode); - } - - /** - * 安装 App(支持 8.0)的意图 - 回传 - * @param activity - * @param file - * @param authority - * @param requestCode - * @return - */ - public static boolean installApp(final Activity activity, final File file, final String authority, final int requestCode) { - if (!isFileExists(file)) return false; - try { - activity.startActivityForResult(IntentUtils.getInstallAppIntent(file, authority), requestCode); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "installApp"); - return false; - } - return true; - } - - /** - * 静默安装app - * @param filePath - * @return - */ - public static boolean installAppSilent(final String filePath) { - return installAppSilent(getFileByPath(filePath)); - } - - /** - * 静默安装app - * - * @param file The file. - * @return true : success, false : fail - */ - public static boolean installAppSilent(final File file) { - if (!isFileExists(file)) return false; - boolean isRoot = isDeviceRooted(); - String filePath = file.getAbsolutePath(); - String command = "LD_LIBRARY_PATH=/vendor/lib:/system/lib pm install " + filePath; - ShellUtils.CommandResult commandResult = ShellUtils.execCmd(command, isRoot); - if (commandResult.successMsg != null && commandResult.successMsg.toLowerCase().contains("success")) { - return true; - } else { - command = "LD_LIBRARY_PATH=/vendor/lib:/system/lib64 pm install " + filePath; - commandResult = ShellUtils.execCmd(command, isRoot, true); - return commandResult.successMsg != null && commandResult.successMsg.toLowerCase().contains("success"); - } - } - - /** - * 卸载 App - * @param packageName - * @return 卸载 App 的意图 - * @param - */ - public static boolean uninstallApp(final String packageName) { - if (isSpace(packageName)) return false; - try { - DevUtils.getContext().startActivity(IntentUtils.getUninstallAppIntent(packageName, true)); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "uninstallApp"); - return false; - } - return true; - } - - /** - * 卸载 App - * @param activity - * @param packageName - * @param requestCode - */ - public static boolean uninstallApp(final Activity activity, final String packageName, final int requestCode) { - if (isSpace(packageName)) return false; - try { - activity.startActivityForResult(IntentUtils.getUninstallAppIntent(packageName), requestCode); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "uninstallApp"); - return false; - } - return true; - } - - /** - * 静默卸载 App - * @param packageName - * @return - */ - public static boolean uninstallAppSilent(final String packageName) { - return uninstallAppSilent(packageName, false); - } - - /** - * 静默卸载 app - * @param packageName - * @param isKeepData - * @return - */ - public static boolean uninstallAppSilent(final String packageName, final boolean isKeepData) { - if (isSpace(packageName)) return false; - boolean isRoot = isDeviceRooted(); - String command = "LD_LIBRARY_PATH=/vendor/lib:/system/lib pm uninstall " + (isKeepData ? "-k " : "") + packageName; - ShellUtils.CommandResult commandResult = ShellUtils.execCmd(command, isRoot, true); - if (commandResult.successMsg != null && commandResult.successMsg.toLowerCase().contains("success")) { - return true; - } else { - command = "LD_LIBRARY_PATH=/vendor/lib:/system/lib64 pm uninstall " + (isKeepData ? "-k " : "") + packageName; - commandResult = ShellUtils.execCmd(command, isRoot, true); - return commandResult.successMsg != null && commandResult.successMsg.toLowerCase().contains("success"); - } - } - - /** - * 判断是否安装了应用 - * @param action The Intent action, such as ACTION_VIEW. - * @param category The desired category. - * @return true : yes, false : no - */ - public static boolean isAppInstalled(@NonNull final String action, @NonNull final String category) { - try { - Intent intent = new Intent(action); - intent.addCategory(category); - PackageManager pm = DevUtils.getContext().getPackageManager(); - ResolveInfo info = pm.resolveActivity(intent, 0); - return info != null; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "isAppInstalled"); - return false; - } - } - - /** - * 判断是否安装了应用 - * @param packageName - * @return true : yes, false : no - */ - public static boolean isAppInstalled(@NonNull final String packageName) { - return !isSpace(packageName) && IntentUtils.getLaunchAppIntent(packageName) != null; - } - - /** - * 判断是否安装指定包名的APP - * @param context 上下文 - * @param packageName 包路径 - * @return - */ - @SuppressWarnings("unused") - public static boolean isInstalledApp(Context context, String packageName) { - if (packageName == null || "".equals(packageName)) { - return false; - } - try { - ApplicationInfo info = context.getPackageManager().getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); - return true; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "isInstalledApp"); - return false; - } - } - - /** - * 判断是否存在root 权限 - * @return true : yes, false : no - */ - public static boolean isAppRoot() { - ShellUtils.CommandResult result = ShellUtils.execCmd("echo root", true); - if (result.result == 0) return true; // result.errorMsg => 失败错误消息 - return false; - } - - /** - * 判断是否app 是否debug模式 - * @return true : yes, false : no - */ - public static boolean isAppDebug() { - return isAppDebug(DevUtils.getContext().getPackageName()); - } - - /** - * 判断是否app 是否debug模式 - * @param packageName - * @return true : yes, false : no - */ - public static boolean isAppDebug(final String packageName) { - if (isSpace(packageName)) return false; - try { - ApplicationInfo ai = DevUtils.getContext().getPackageManager().getApplicationInfo(packageName, 0); - return ai != null && (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "isAppDebug"); - return false; - } - } - - /** - * 判断app 是否系统app - * @return true : yes, false : no - */ - public static boolean isAppSystem() { - return isAppSystem(DevUtils.getContext().getPackageName()); - } - - /** - * 判断app 是否系统app - * @param packageName - * @return true : yes, false : no - */ - public static boolean isAppSystem(final String packageName) { - if (isSpace(packageName)) return false; - try { - ApplicationInfo ai = DevUtils.getContext().getPackageManager().getApplicationInfo(packageName, 0); - return ai != null && (ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "isAppSystem"); - return false; - } - } - - /** - * 判断app 是否在前台 - * @return true : yes, false : no - */ - public static boolean isAppForeground() { - try { - ActivityManager manager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); - List info = manager.getRunningAppProcesses(); - if (info == null || info.size() == 0) return false; - for (ActivityManager.RunningAppProcessInfo aInfo : info) { - if (aInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { - return aInfo.processName.equals(DevUtils.getContext().getPackageName()); - } - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "isAppForeground"); - } - return false; - } - - /** - * 判断app 是否在前台 - * => 属于系统权限 - * @param packageName - * @return - */ - public static boolean isAppForeground(@NonNull final String packageName) { - return !isSpace(packageName) && packageName.equals(ProcessUtils.getForegroundProcessName()); - } - - /** - * 打开app - * @param packageName - */ - public static boolean launchApp(final String packageName) { - if (isSpace(packageName)) return false; - try { - DevUtils.getContext().startActivity(IntentUtils.getLaunchAppIntent(packageName, true)); - return true; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "launchApp"); - } - return false; - } - - /** - * 打开app, 并且回传 - * @param activity The activity. - * @param packageName - * @param requestCode - */ - public static boolean launchApp(final Activity activity, final String packageName, final int requestCode) { - if (isSpace(packageName)) return false; - try { - activity.startActivityForResult(IntentUtils.getLaunchAppIntent(packageName), requestCode); - return true; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "launchApp"); - } - return false; - } - - /** 跳转到 专门的APP 设置详情页面 */ - public static boolean launchAppDetailsSettings() { - return launchAppDetailsSettings(DevUtils.getContext().getPackageName()); - } - - /** - * 跳转到 专门的APP 设置详情页面 - * @param packageName - */ - public static boolean launchAppDetailsSettings(final String packageName) { - if (isSpace(packageName)) return false; - try { - DevUtils.getContext().startActivity(IntentUtils.getLaunchAppDetailsSettingsIntent(packageName, true)); - return true; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "launchAppDetailsSettings"); - } - return false; - } - - /** - * 跳转到 专门的APP 应用商城详情页面 - * @param marketPkg - * @return - */ - public static boolean launchAppDetails(final String marketPkg) { - return launchAppDetails(DevUtils.getContext().getPackageName(), marketPkg); - } - - /** - * 跳转到 专门的APP 应用商城详情页面 - * @param marketPkg - * @param marketPkg - * @return - */ - public static boolean launchAppDetails(final String packageName, final String marketPkg) { - if (isSpace(packageName)) return false; - try { - DevUtils.getContext().startActivity(IntentUtils.getlaunchAppDetailIntent(packageName, marketPkg, true)); - return true; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "launchAppDetails"); - } - return false; - } - - /** - * 获取app 路径 /data/data/包名/.apk - * @return - */ - public static String getAppPath() { - return getAppPath(DevUtils.getContext().getPackageName()); - } - - /** - * 获取app 路径 /data/data/包名/.apk - * @param packageName - * @return - */ - public static String getAppPath(final String packageName) { - if (isSpace(packageName)) return null; - try { - PackageManager pm = DevUtils.getContext().getPackageManager(); - PackageInfo pi = pm.getPackageInfo(packageName, 0); - return pi == null ? null : pi.applicationInfo.sourceDir; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppPath"); - return null; - } - } - - // == - - /** - * 获取app 签名 - * @return - */ - public static Signature[] getAppSignature() { - return getAppSignature(DevUtils.getContext().getPackageName()); - } - - /** - * 获取app 签名 - * @param packageName - * @return - */ - public static Signature[] getAppSignature(final String packageName) { - if (isSpace(packageName)) return null; - try { - PackageManager pm = DevUtils.getContext().getPackageManager(); - @SuppressLint("PackageManagerGetSignatures") - PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); - return pi == null ? null : pi.signatures; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppSignature"); - return null; - } - } - - /** - * 获取 app sha1值 - * @return - */ - public static String getAppSignatureSHA1() { - return getAppSignatureSHA1(DevUtils.getContext().getPackageName()); - } - - /** - * 获取 app sha1值 - * @param packageName - * @return - */ - public static String getAppSignatureSHA1(@NonNull final String packageName) { - Signature[] signature = getAppSignature(packageName); - if (signature == null || signature.length <= 0) return null; - return encryptSHA1ToString(signature[0].toByteArray()).replaceAll("(?<=[0-9A-F]{2})[0-9A-F]{2}", ":$0"); - } - - // == - - /** - * 检查是否存在某个文件 - * @param file 文件路径 - * @return 是否存在文件 - */ - private static boolean isFileExists(final File file){ - return file != null && file.exists(); - } - - /** - * 获取文件 - * @param filePath - * @return - */ - private static File getFileByPath(final String filePath){ - return filePath != null ? new File(filePath) : null; - } - - /** - * 判断字符串是否为 null 或全为空白字符 - * @param str 待校验字符串 - * @return - */ - private static boolean isSpace(final String str) { - if (str == null) return true; - for (int i = 0, len = str.length(); i < len; ++i) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } - - /** - * 判断设备是否 root - * @return - */ - private static boolean isDeviceRooted() { - String su = "su"; - String[] locations = { "/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/", - "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/" }; - for (String location : locations) { - if (new File(location + su).exists()) { - return true; - } - } - return false; - } - - // 十六进制大写转换 - private static final char HEX_DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - - /** - * SHA1加密 - * @param data - * @return - */ - private static String encryptSHA1ToString(final byte[] data) { - return bytes2HexString(encryptSHA1(data)); - } - - /** - * SHA1加密 - * @param data - * @return - */ - private static byte[] encryptSHA1(final byte[] data) { - if (data == null || data.length <= 0) return null; - try { - MessageDigest md = MessageDigest.getInstance("SHA1"); - md.update(data); - return md.digest(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "encryptSHA1"); - return null; - } - } - - /** - * 数据加密转换16进制 - * @param bytes - * @return - */ - private static String bytes2HexString(final byte[] bytes) { - if (bytes == null) return null; - int len = bytes.length; - if (len <= 0) return null; - char[] ret = new char[len << 1]; - for (int i = 0, j = 0; i < len; i++) { - ret[j++] = HEX_DIGITS[bytes[i] >>> 4 & 0x0f]; - ret[j++] = HEX_DIGITS[bytes[i] & 0x0f]; - } - return new String(ret); - } - - // == 其他功能 == - - /** - * 启动本地应用打开PDF - * @param context 上下文 - * @param filePath 文件路径 - */ - public static boolean openPDFFile(Context context, String filePath) { - try { - File file = new File(filePath); - if (file.exists()) { - Uri path = Uri.fromFile(file); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(path, "application/pdf"); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - context.startActivity(intent); - return true; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "openPDFFile"); - } - return false; - } - - /** - * 启动本地应用打开PDF - * @param context 上下文 - * @param filePath 文件路径 - */ - public static boolean openWordFile(Context context, String filePath) { - try { - File file = new File(filePath); - if (file.exists()) { - Uri path = Uri.fromFile(file); - Intent intent = new Intent("android.intent.action.VIEW"); - intent.addCategory("android.intent.category.DEFAULT"); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setDataAndType(path, "application/msword"); - context.startActivity(intent); - return true; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "openWordFile"); - } - return false; - } - - /** - * 调用WPS打开office文档 - * @param context 上下文 - * @param filePath 文件路径 - */ - public static boolean openOfficeByWPS(Context context, String filePath) { - try { - // 检查是否安装WPS - String wpsPackageEng = "cn.wps.moffice_eng";// 普通版与英文版一样 - // String wpsActivity = "cn.wps.moffice.documentmanager.PreStartActivity"; - String wpsActivity2 = "cn.wps.moffice.documentmanager.PreStartActivity2";// 默认第三方程序启动 - - Intent intent = new Intent(); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setClassName(wpsPackageEng, wpsActivity2); - - Uri uri = Uri.fromFile(new File(filePath)); - intent.setData(uri); - context.startActivity(intent); - return true; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "openOfficeByWPS"); - } - return false; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/CameraUtils.java b/DevLibUtils/src/main/java/dev/utils/app/CameraUtils.java deleted file mode 100644 index 0dd1fe628e..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/CameraUtils.java +++ /dev/null @@ -1,216 +0,0 @@ -package dev.utils.app; - -import android.hardware.Camera; - -import dev.utils.LogPrintUtils; - -/** - * detail: 摄像头相关工具类 - * Created by Ttt - */ -public final class CameraUtils { - - private CameraUtils(){ - } - - // 日志Tag - private static final String TAG = CameraUtils.class.getSimpleName(); - - // == 摄像头快速处理 == - - /** - * 判断是否支持反转摄像头(是否存在前置摄像头) - * @return - */ - public static boolean isSupportReverse(){ - try { - // 默认是不支持 - int isSupportReverse = 0; - // 判断是否支持前置,支持则使用前置 - if (checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_FRONT)) { - isSupportReverse += 1; - // - - LogPrintUtils.dTag(TAG,"支持前置摄像头(手机屏幕)"); - } - // 判断是否支持后置,是则使用后置 - if (checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_BACK)) { - isSupportReverse += 1; - // - - LogPrintUtils.dTag(TAG,"支持后置摄像头(手机背面)"); - } - // 如果都支持才表示支持反转 - return isSupportReverse == 2; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "isSupportReverse"); - } - // 默认不支持反转摄像头 - return false; - } - - /** - * 检查是否有摄像头 - * @param facing 前置还是后置 - * @return - */ - public static boolean checkCameraFacing(int facing) { - try { - int cameraCount = Camera.getNumberOfCameras(); - Camera.CameraInfo info = new Camera.CameraInfo(); - for (int i = 0; i < cameraCount; i++) { - Camera.getCameraInfo(i, info); - if (facing == info.facing) { - return true; - } - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "checkCameraFacing"); - } - return false; - } - - /** - * 判断是否使用前置摄像头 - * @param facing - * @return - */ - public static boolean isFrontCamera(int facing){ - return facing == Camera.CameraInfo.CAMERA_FACING_FRONT; - } - - /** - * 判断是否使用后置摄像头 - * @param facing - * @return - */ - public static boolean isBackCamera(int facing){ - return facing == Camera.CameraInfo.CAMERA_FACING_BACK; - } - - /** - * 判断使用的视像头 - * @param isFrontCamera 是否前置摄像头 - * @return - */ - public static int isUseCameraFacing(boolean isFrontCamera){ - // 默认使用后置摄像头 - int cameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK; - try { - // 支持的摄像头 - 前置, 后置 - boolean[] cFacingArys = new boolean[] { false, false}; - // 判断是否支持前置 - cFacingArys[0] = checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_FRONT); - // 判断是否支持后置 - cFacingArys[1] = checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_BACK); - // 进行判断想要使用的是前置,还是后置 - if (isFrontCamera && cFacingArys[0]){ // 使用前置, 必须也支持前置 - // 表示使用前置摄像头 - cameraFacing = Camera.CameraInfo.CAMERA_FACING_FRONT; - } else { - // 表示使用后置摄像头 - cameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "isUseCameraFacing"); - } - return cameraFacing; - } - - // == - - /** - * 释放摄像头资源 - * @param mCamera 摄像头对象 - */ - public static void freeCameraResource(android.hardware.Camera mCamera) { - try { - if (mCamera != null) { - mCamera.setPreviewCallback(null); - mCamera.stopPreview(); - mCamera.lock(); - mCamera.release(); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "freeCameraResource"); - } finally { - mCamera = null; - } - } - - /** - * 初始化摄像头 - * @param mCamera - * @param isFrontCamera 是否前置摄像头 = true = 前置(屏幕面), false = 后置(手机背面) = 正常默认使用背面 - * @return 使用的摄像头 - */ - public static Camera initCamera(Camera mCamera, boolean isFrontCamera) { - // 如果之前存在摄像头数据, 则释放资源 - if (mCamera != null) { - freeCameraResource(mCamera); - } - try { - // 进行判断想要使用的是前置,还是后置 - if (isFrontCamera && checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_FRONT)){ // 使用前置, 必须也支持前置 - // 初始化前置摄像头 - mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT); - } else { - // 初始化后置摄像头 - mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "initCamera"); - // 释放资源 - freeCameraResource(mCamera); - } - return mCamera; - } - - /** - * 打开摄像头 - * @param cameraId {@link Camera.CameraInfo } CAMERA_FACING_FRONT(前置), CAMERA_FACING_BACK(后置) - * @return - */ - public static Camera open(int cameraId){ - // 判断支持的摄像头数量 - int numCameras = Camera.getNumberOfCameras(); - if (numCameras == 0) { - return null; - } - // 判断是否指定哪个摄像头 - boolean explicitRequest = cameraId >= 0; - - // 如果没指定, 则进行判断处理 - if (!explicitRequest) { // 默认使用 后置摄像头, 没有后置才用其他(前置) - int index = 0; - while (index < numCameras) { - Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); - Camera.getCameraInfo(index, cameraInfo); - if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { - break; - } - index++; - } - cameraId = index; - } - - Camera camera; - if (cameraId < numCameras) { - camera = Camera.open(cameraId); - } else { - if (explicitRequest) { - camera = null; - } else { - // 默认使用后置 - camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); - } - } - return camera; - } - - /** - * 打开摄像头(默认后置摄像头) - * @return - */ - public static Camera open() { - return open(-1); // Camera.CameraInfo.CAMERA_FACING_BACK - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/CleanUtils.java b/DevLibUtils/src/main/java/dev/utils/app/CleanUtils.java deleted file mode 100644 index d7e6e7c79b..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/CleanUtils.java +++ /dev/null @@ -1,233 +0,0 @@ -package dev.utils.app; - -import android.os.Environment; - -import java.io.File; -import java.math.BigDecimal; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 本应用数据清除管理器 - * Created by Ttt - * 主要功能有清除内/外缓存,清除数据库,清除sharedPreference,清除files和清除自定义目录 - */ -public final class CleanUtils { - - private CleanUtils(){ - } - - // 日志TAG - private static final String TAG = CleanUtils.class.getSimpleName(); - - - /** - * 清除本应用内部缓存(/data/data/com.xxx.xxx/cache) - */ - public static void cleanInternalCache() { - try { - deleteFilesByDirectory(DevUtils.getContext().getCacheDir()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "cleanInternalCache"); - } - } - - /** - * 清除本应用所有数据库(/data/data/com.xxx.xxx/databases) - */ - public static void cleanDatabases() { - try { - deleteFilesByDirectory(new File(DevUtils.getContext().getFilesDir().getPath() + DevUtils.getContext().getPackageName() + "/databases")); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "cleanDatabases"); - } - } - - /** - * 清除本应用SharedPreference(/data/data/com.xxx.xxx/shared_prefs) - */ - public static void cleanSharedPreference() { - try { - deleteFilesByDirectory(new File(DevUtils.getContext().getFilesDir().getPath() + DevUtils.getContext().getPackageName() + "/shared_prefs")); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "cleanSharedPreference"); - } - } - - /** - * 按名字清除本应用数据库 - * @param dbName 数据库名称 - */ - public static void cleanDatabaseByName(String dbName) { - try { - DevUtils.getContext().deleteDatabase(dbName); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "cleanDatabaseByName"); - } - } - - /** - * 清除/data/data/com.xxx.xxx/files下的内容 - */ - public static void cleanFiles() { - try { - deleteFilesByDirectory(DevUtils.getContext().getFilesDir()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "cleanFiles"); - } - } - - /** - * 清除外部cache下的内容(/mnt/sdcard/android/data/com.xxx.xxx/cache) - */ - public static void cleanExternalCache() { - try { - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - deleteFilesByDirectory(DevUtils.getContext().getExternalCacheDir()); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "cleanExternalCache"); - } - } - - /** - * 清除自定义路径下的文件,使用需小心,请不要误删。而且只支持目录下的文件删除 - * @param filePath 文件路径 - */ - public static void cleanCustomCache(String filePath) { - deleteFilesByDirectory(getFileByPath(filePath)); - } - - /** - * 清除本应用所有的数据 - * @param filePaths 文件路径 - */ - public static void cleanApplicationData(String... filePaths) { - cleanInternalCache(); - cleanExternalCache(); - cleanDatabases(); - cleanSharedPreference(); - cleanFiles(); - try { - for (String fPath : filePaths) { - cleanCustomCache(fPath); - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "cleanApplicationData"); - } - } - - /** - * 删除方法 这里只会删除某个文件夹下的文件,如果传入的directory是个文件,将不做处理 - * @param directory 文件夹File对象 - */ - private static void deleteFilesByDirectory(File directory) { - // 存在并且属于文件夹 - if (directory != null && directory.exists() && directory.isDirectory()) { - try { - // 获取文件列表 - File[] files = directory.listFiles(); - // 遍历删除文件 - for (File item : files) { - // 删除文件 - item.delete(); - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "deleteFilesByDirectory"); - } - } - } - - /** - * 获取文件夹大小 - * Context.getExternalFilesDir() --> SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据 - * Context.getExternalCacheDir() --> SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据 - * @param file - * @return - */ - public static long getFolderSize(File file) { - long size = 0; - try { - File[] files = file.listFiles(); - for (int i = 0, len = files.length; i < len; i++) { - // 如果下面还有文件 - if (files[i].isDirectory()) { - size = size + getFolderSize(files[i]); - } else { - size = size + files[i].length(); - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getFolderSize"); - } - return size; - } - - /** - * 获取缓存文件大小 - * @param file - * @return - */ - public static String getCacheSize(File file){ - return getFormatSize(getFolderSize(file)); - } - - /** - * 格式化单位 - * @param size - * @return - */ - public static String getFormatSize(double size) { - double kiloByte = size / 1024; - if (kiloByte < 1) { - return size + "Byte"; - } - - 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"; - } - - // == - - /** - * 获取文件 - * @param filePath - * @return - */ - private static File getFileByPath(final String filePath){ - return filePath != null ? new File(filePath) : null; - } - - /** - * 判断字符串是否为 null 或全为空白字符 - * @param str 待校验字符串 - * @return - */ - private static boolean isSpace(final String str) { - if (str == null) return true; - for (int i = 0, len = str.length(); i < len; ++i) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } -} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/ClickUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ClickUtils.java deleted file mode 100644 index e014e55d2c..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ClickUtils.java +++ /dev/null @@ -1,185 +0,0 @@ -package dev.utils.app; - -import java.util.HashMap; - -import dev.utils.LogPrintUtils; - -/** - * detail: 点击工具类 - * Created by Ttt - */ -public final class ClickUtils { - - private ClickUtils(){ - } - - // 日志TAG - private static final String TAG = ClickUtils.class.getSimpleName(); - - // 上一次点击的标识id = viewId 等 - private static int lastTagId = -1; - /** 上次点击时间 */ - private static long lastClickTime = 0l; // 局限性是, 全局统一事件,如果上次点击后,立刻点击其他就无法点 - /** 默认间隔时间 */ - private static long DF_DIFF = 1000l; // 点击间隔1秒内 - /** 配置数据 */ - private static final HashMap mapConfig = new HashMap<>(); - /** 点击记录数据 */ - private static final HashMap mapRecords = new HashMap<>(); - - // === - - /** - * 判断两次点击的间隔 小于默认间隔时间(1秒), 则认为是多次无效点击 - * @return - */ - public static boolean isFastDoubleClick() { - return isFastDoubleClick(-1, DF_DIFF); - } - - /** - * 判断两次点击的间隔 小于默认间隔时间(1秒), 则认为是多次无效点击 - * @param tagId - * @return - */ - public static boolean isFastDoubleClick(int tagId) { - return isFastDoubleClick(tagId, DF_DIFF); - } - - /** - * 判断两次点击的间隔 小于间隔时间(diff), 则认为是多次无效点击 - * @param tagId - * @param diff - * @return - */ - public static boolean isFastDoubleClick(int tagId, long diff) { - long cTime = System.currentTimeMillis(); - long dTime = cTime - lastClickTime; - // 判断时间是否超过 - if (lastTagId == tagId && lastClickTime > 0 && dTime < diff) { - LogPrintUtils.dTag(TAG, "isFastDoubleClick 无效点击 => tagId: " + tagId + ", diff: " + diff); - return true; - } - LogPrintUtils.dTag(TAG, "isFastDoubleClick 有效点击 => tagId: " + tagId + ", diff: " + diff); - lastTagId = tagId; - lastClickTime = cTime; - return false; - } - - // === - - /** - * 判断两次点击的间隔(根据默认Tag判断) 小于指定间隔时间, 则认为是多次无效点击 - * @param tag - * @return - */ - public static boolean isFastDoubleClick(String tag){ - // 获取配置时间 - Long config_time = mapConfig.get(tag); - // 如果等于null, 则传入默认时间 - if (config_time == null){ - return isFastDoubleClick(tag, DF_DIFF); - } - return isFastDoubleClick(tag, config_time); - } - - /** - * 判断两次点击的间隔 小于间隔时间(diff), 则认为是多次无效点击 - * @param tag - * @param diff - * @return - */ - public static boolean isFastDoubleClick(String tag, long diff){ - // 获取上次点击的时间 - Long lastTime = mapRecords.get(tag); - if (lastTime == null){ - lastTime = 0l; - } - long cTime = System.currentTimeMillis(); - long dTime = cTime - lastTime; - // 判断时间是否超过 - if (lastTime > 0 && dTime < diff) { - LogPrintUtils.dTag(TAG, "isFastDoubleClick 无效点击 => tag: " + tag + ", diff: " + diff); - return true; - } - LogPrintUtils.dTag(TAG, "isFastDoubleClick 有效点击 => tag: " + tag + ", diff: " + diff); - // 保存上次点击时间 - mapRecords.put(tag, cTime); - return false; - } - - // === - - /** - * 判断两次点击的间隔(根据默认Tag判断) 小于指定间隔时间, 则认为是多次无效点击 - * @param obj - * @return - */ - public static boolean isFastDoubleClick(Object obj){ - // 获取TAG - String tag = ((obj != null) ? ("obj_" + obj.hashCode()) : "obj_null"); - // 获取配置时间 - Long config_time = mapConfig.get(tag); - // 如果等于null, 则传入默认时间 - if (config_time == null){ - return isFastDoubleClick(tag, DF_DIFF); - } - return isFastDoubleClick(tag, config_time); - } - - /** - * 判断两次点击的间隔 小于间隔时间(diff), 则认为是多次无效点击 - * @param obj - * @param diff - * @return - */ - public static boolean isFastDoubleClick(Object obj, long diff){ - // 获取TAG - String tag = ((obj != null) ? ("obj_" + obj.hashCode()) : "obj_null"); - // 获取上次点击的时间 - Long lastTime = mapRecords.get(tag); - if (lastTime == null){ - lastTime = 0l; - } - long cTime = System.currentTimeMillis(); - long dTime = cTime - lastTime; - // 判断时间是否超过 - if (lastTime > 0 && dTime < diff) { - LogPrintUtils.dTag(TAG, "isFastDoubleClick 无效点击 => obj: " + obj + ", diff: " + diff); - return true; - } - LogPrintUtils.dTag(TAG, "isFastDoubleClick 有效点击 => obj: " + obj + ", diff: " + diff); - // 保存上次点击时间 - mapRecords.put(tag, cTime); - return false; - } - - // === - - /** - * 初始化配置信息 - * @param mapConfig - */ - public static void initConfig(HashMap mapConfig){ - if (mapConfig != null){ - mapConfig.putAll(mapConfig); - } - } - - /** - * 添加配置信息 - * @param key - * @param val - */ - public static void putConfig(String key, Long val){ - mapConfig.put(key, val); - } - - /** - * 移除配置信息 - * @param key - */ - public static void removeConfig(String key){ - mapConfig.remove(key); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/DBUtils.java b/DevLibUtils/src/main/java/dev/utils/app/DBUtils.java deleted file mode 100644 index 8684b62fe6..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/DBUtils.java +++ /dev/null @@ -1,111 +0,0 @@ -package dev.utils.app; - -import android.os.Environment; - -import java.io.InputStream; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; -import dev.utils.common.FileUtils; - -/** - * detail: 数据库工具类 (导入导出等) - * Created by Ttt - */ -public final class DBUtils { - - private DBUtils() { - } - - // 日志TAG - private static final String TAG = DBUtils.class.getSimpleName(); - - /** - * 导出数据库 - * @param targetFile 目标文件 - * @param dbName 数据库文件名 - * @return 是否倒出成功 - */ - public static boolean startExportDatabase(String targetFile, String dbName) { - // 判断 SDCard 是否挂载 - if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { - return false; - } - try { - // Database 文件地址 - String sourceFilePath = getDBPath() + dbName; - // 获取结果 - boolean result = FileUtils.copyFile(sourceFilePath, targetFile, true); - // 返回结果 - return result; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "startExportDatabase"); - } - return false; - } - - // ==== - - /** - * 导入数据库 - * @param srcFilePath 待复制的文件地址 - * @param destFilePath 目标文件地址 - * @return 是否倒出成功 - */ - public static boolean startImportDatabase(String srcFilePath, String destFilePath){ - // 判断 SDCard 是否挂载 - if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { - return false; - } - try { - // 获取结果 - boolean result = FileUtils.copyFile(srcFilePath, destFilePath, true); - // 返回结果 - return result; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "startImportDatabase"); - } - return false; - } - - /** - * 导入数据库 - * @param inputStream 文件流(被复制) - * @param destFilePath 目标文件地址 - * @return 是否倒出成功 - */ - public static boolean startImportDatabase(InputStream inputStream, String destFilePath){ - // 判断 SDCard 是否挂载 - if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { - return false; - } - try { - // 获取结果 - boolean result = FileUtils.copyFile(inputStream, destFilePath, true); - // 返回结果 - return result; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "startImportDatabase"); - } - return false; - } - - // == - - /** - * 获取数据库路径 - * @return - */ - public static String getDBPath(){ - try { - // Database 文件地址 - String dbPath = Environment.getDataDirectory() + "/data/" + DevUtils.getContext().getPackageName() + "/databases/"; - // 返回数据库路径 - return dbPath; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDBPath"); - } - return null; - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/DeviceUtils.java b/DevLibUtils/src/main/java/dev/utils/app/DeviceUtils.java deleted file mode 100644 index dc3ed1e512..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/DeviceUtils.java +++ /dev/null @@ -1,504 +0,0 @@ -package dev.utils.app; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Build; -import android.os.PowerManager; -import android.provider.Settings; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Locale; -import java.util.Map; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 设备相关工具类 - * Created by Ttt - */ -public final class DeviceUtils { - - private DeviceUtils() { - } - - // 日志TAG - private static final String TAG = DBUtils.class.getSimpleName(); - -// // http://blog.csdn.net/zhangcanyan/article/details/52817866 -// android.os.Build.BOARD:获取设备基板名称 -// android.os.Build.BOOTLOADER:获取设备引导程序版本号 -// android.os.Build.BRAND:获取设备品牌 -// android.os.Build.CPU_ABI:获取设备指令集名称(CPU的类型) -// android.os.Build.CPU_ABI2:获取第二个指令集名称 -// android.os.Build.DEVICE:获取设备驱动名称 -// android.os.Build.DISPLAY:获取设备显示的版本包(在系统设置中显示为版本号)和ID一样 -// android.os.Build.FINGERPRINT:设备的唯一标识。由设备的多个信息拼接合成。 -// android.os.Build.HARDWARE:设备硬件名称,一般和基板名称一样(BOARD) -// android.os.Build.HOST:设备主机地址 -// android.os.Build.ID:设备版本号。 -// android.os.Build.MODEL :获取手机的型号 设备名称。 -// android.os.Build.MANUFACTURER:获取设备制造商 -// android:os.Build.PRODUCT:整个产品的名称 -// android:os.Build.RADIO:无线电固件版本号,通常是不可用的 显示unknown -// android.os.Build.TAGS:设备标签。如release-keys 或测试的 test-keys -// android.os.Build.TIME:时间 -// android.os.Build.TYPE:设备版本类型 主要为"user" 或"eng". -// android.os.Build.USER:设备用户名 基本上都为android-build -// android.os.Build.VERSION.RELEASE:获取系统版本字符串。如4.1.2 或2.2 或2.3等 -// android.os.Build.VERSION.CODENAME:设备当前的系统开发代号,一般使用REL代替 -// android.os.Build.VERSION.INCREMENTAL:系统源代码控制值,一个数字或者git hash值 -// android.os.Build.VERSION.SDK:系统的API级别 一般使用下面大的SDK_INT 来查看 -// android.os.Build.VERSION.SDK_INT:系统的API级别 数字表示 - - /** 换行字符串 */ - private static final String NEW_LINE_STR = System.getProperty("line.separator"); - - /** - * 获取当前操作系统的语言 - * @return String 系统语言 - */ - public static String getSysLanguage() { - String language = Locale.getDefault().getLanguage(); - return language; - } - - /** - * 获取设备信息 - * @param dInfoMaps 传入设备信息传出HashMap - */ - public static void getDeviceInfo(HashMap dInfoMaps) { - // 获取设备信息类的所有申明的字段,即包括public、private和proteced, 但是不包括父类的申明字段。 - Field[] fields = Build.class.getDeclaredFields(); - // 遍历字段 - for (Field field : fields) { - try { - // 取消java的权限控制检查 - field.setAccessible(true); - // 获取类型对应字段的数据,并保存 - dInfoMaps.put(field.getName(), field.get(null).toString()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); - } - } - } - - /** - * 获取设备信息 - * @param dInfoMaps 传入设备信息传出HashMap - */ - public static void getDeviceInfo2(HashMap dInfoMaps) { - // 获取设备信息类的所有申明的字段,即包括public、private和proteced, 但是不包括父类的申明字段。 - Field[] fields = Build.class.getDeclaredFields(); - // 遍历字段 - for (Field field : fields) { - try { - // 取消java的权限控制检查 - field.setAccessible(true); - - if (field.getName().toLowerCase().equals("SUPPORTED_ABIS".toLowerCase())) { - Object object = field.get(null); - // 判断是否数组 - if (object instanceof String[]) { - // 获取类型对应字段的数据,并保存 - 保存支持的指令集 [arm64-v8a, armeabi-v7a, armeabi] - dInfoMaps.put(field.getName().toLowerCase(), Arrays.toString((String[]) object)); - continue; - } - } - // 获取类型对应字段的数据,并保存 - dInfoMaps.put(field.getName().toLowerCase(), field.get(null).toString()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDeviceInfo2"); - } - } - } - - /** - * 处理设备信息 - * @param dInfoMaps 设备信息 - * @param eHint 错误提示,如获取设备信息失败 - */ - public static String handleDeviceInfo(HashMap dInfoMaps, String eHint) { - try { - // 初始化StringBuilder,拼接字符串 - StringBuilder sBuilder = new StringBuilder(); - // 获取设备信息 - Iterator> mapIter = dInfoMaps.entrySet().iterator(); - // 遍历设备信息 - while (mapIter.hasNext()) { - // 获取对应的key-value - Map.Entry rnEntry = (Map.Entry) mapIter.next(); - String rnKey = (String) rnEntry.getKey(); // key - String rnValue = (String) rnEntry.getValue(); // value - // 保存设备信息 - sBuilder.append(rnKey); - sBuilder.append(" = "); - sBuilder.append(rnValue); - sBuilder.append(NEW_LINE_STR); - } - return sBuilder.toString(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleDeviceInfo"); - } - return eHint; - } - - /** - * 获取设备系统版本号 - * @return 设备系统版本号 - */ - public static String getSDKVersionName() { - return Build.VERSION.RELEASE; - } - - /** - * 获取当前SDK 版本号 - * @return - */ - public static int getSDKVersion(){ - return Build.VERSION.SDK_INT; - } - - /** - * 获取Android id - * hint: - * 在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,当设备被wipe后该值会被重置。 - * @return - */ - public static String getAndroidId(){ - // Android id 默认为null - String androidId = null; - try { - androidId = Settings.Secure.getString(DevUtils.getContext().getContentResolver(), Settings.Secure.ANDROID_ID); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAndroidId"); - } - return androidId; - } - - /** - * 判断设备是否 root - * @return - */ - public static boolean isDeviceRooted() { - String su = "su"; - String[] locations = { "/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/", - "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/" }; - for (String location : locations) { - if (new File(location + su).exists()) { - return true; - } - } - return false; - } - - // == - - /** 特殊mac地址用于判断是否获取失败 */ - private static final String CUSTOM_MAC = "02:00:00:00:00:00"; - - /** - * 获取设备 MAC 地址 - * hint: - * 需添加权限 - * 需添加权限 - * @return - */ - public static String getMacAddress() { - String macAddress = getMacAddressByWifiInfo(); - if (!CUSTOM_MAC.equals(macAddress)) { - return macAddress; - } - macAddress = getMacAddressByNetworkInterface(); - if (!CUSTOM_MAC.equals(macAddress)) { - return macAddress; - } - macAddress = getMacAddressByInetAddress(); - if (!CUSTOM_MAC.equals(macAddress)) { - return macAddress; - } - macAddress = getMacAddressByFile(); - if (!CUSTOM_MAC.equals(macAddress)) { - return macAddress; - } - // 没有打开wifi, 获取 WLAN MAC 地址失败 - return null; - } - - /** - * 获取设备 MAC 地址 - * hint: - * 需添加权限 - * @return - */ - @SuppressLint({"HardwareIds", "MissingPermission"}) - private static String getMacAddressByWifiInfo() { - try { - @SuppressLint("WifiManagerLeak") - WifiManager wifiManager = (WifiManager) DevUtils.getContext().getSystemService(Context.WIFI_SERVICE); - if (wifiManager != null) { - WifiInfo wifiInfo = wifiManager.getConnectionInfo(); - if (wifiInfo != null) return wifiInfo.getMacAddress(); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getMacAddressByWifiInfo"); - } - return CUSTOM_MAC; - } - - /** - * 获取设备 MAC 地址 - * hint: - * 需添加权限 - * @return - */ - private static String getMacAddressByNetworkInterface() { - try { - Enumeration nis = NetworkInterface.getNetworkInterfaces(); - while (nis.hasMoreElements()) { - NetworkInterface ni = nis.nextElement(); - if (ni == null || !ni.getName().equalsIgnoreCase("wlan0")) continue; - byte[] macBytes = ni.getHardwareAddress(); - if (macBytes != null && macBytes.length > 0) { - StringBuilder sb = new StringBuilder(); - for (byte b : macBytes) { - sb.append(String.format("%02x:", b)); - } - return sb.substring(0, sb.length() - 1); - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getMacAddressByNetworkInterface"); - } - return CUSTOM_MAC; - } - - private static String getMacAddressByInetAddress() { - try { - InetAddress inetAddress = getInetAddress(); - if (inetAddress != null) { - NetworkInterface ni = NetworkInterface.getByInetAddress(inetAddress); - if (ni != null) { - byte[] macBytes = ni.getHardwareAddress(); - if (macBytes != null && macBytes.length > 0) { - StringBuilder sb = new StringBuilder(); - for (byte b : macBytes) { - sb.append(String.format("%02x:", b)); - } - return sb.substring(0, sb.length() - 1); - } - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getMacAddressByInetAddress"); - } - return "02:00:00:00:00:00"; - } - - private static InetAddress getInetAddress() { - try { - Enumeration nis = NetworkInterface.getNetworkInterfaces(); - while (nis.hasMoreElements()) { - NetworkInterface ni = nis.nextElement(); - // To prevent phone of xiaomi return "10.0.2.15" - if (!ni.isUp()) continue; - Enumeration addresses = ni.getInetAddresses(); - while (addresses.hasMoreElements()) { - InetAddress inetAddress = addresses.nextElement(); - if (!inetAddress.isLoopbackAddress()) { - String hostAddress = inetAddress.getHostAddress(); - if (hostAddress.indexOf(':') < 0) return inetAddress; - } - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getInetAddress"); - } - return null; - } - - /** - * 获取设备 MAC 地址 - * @return MAC 地址 - */ - private static String getMacAddressByFile() { - ShellUtils.CommandResult result = ShellUtils.execCmd("getprop wifi.interface", false); - if (result.result == 0) { - String name = result.successMsg; - if (name != null) { - result = ShellUtils.execCmd("cat /sys/class/net/" + name + "/address", false); - if (result.result == 0) { - String address = result.successMsg; - if (address != null && address.length() > 0) { - return address; - } - } - } - } - return CUSTOM_MAC; - } - - // == - - /** - * 获取设备厂商 如 Xiaomi - * @return 设备厂商 - */ - public static String getManufacturer() { - return Build.MANUFACTURER; - } - - /** - * 获取设备型号 如 RedmiNote4X - * @return 设备型号 - */ - public static String getModel() { - String model = Build.MODEL; - if (model != null) { - model = model.trim().replaceAll("\\s*", ""); - } else { - model = ""; - } - return model; - } - - /** - * 关机 需要 root 权限或者系统权限 - * @return - */ - public static boolean shutdown() { - try { - ShellUtils.execCmd("reboot -p", true); - Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN"); - intent.putExtra("android.intent.extra.KEY_CONFIRM", false); - DevUtils.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - return true; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "shutdown"); - } - return false; - } - - /** - * 重启 需要 root 权限或者系统权限 - * @return - */ - public static boolean reboot() { - try { - ShellUtils.execCmd("reboot", true); - Intent intent = new Intent(Intent.ACTION_REBOOT); - intent.putExtra("nowait", 1); - intent.putExtra("interval", 1); - intent.putExtra("window", 0); - DevUtils.getContext().sendBroadcast(intent); - return true; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "reboot"); - } - return false; - } - - /** - * 重启 - * 需系统权限 - * @param reason 传递给内核来请求特殊的引导模式,如"recovery" - */ - public static void reboot(final String reason) { - try { - PowerManager mPowerManager = (PowerManager) DevUtils.getContext().getSystemService(Context.POWER_SERVICE); - if (mPowerManager == null) - return; - mPowerManager.reboot(reason); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "reboot"); - } - } - - /** 重启到 recovery 需要 root 权限 */ - public static void reboot2Recovery() { - ShellUtils.execCmd("reboot recovery", true); - } - - /** 重启到 bootloader 需要 root 权限 */ - public static void reboot2Bootloader() { - ShellUtils.execCmd("reboot bootloader", true); - } - - /** - * BASEBAND-VER - * 基带版本 - * return String - */ - public static String getBaseband_Ver() { - String Version = ""; - try { - Class cl = Class.forName("android.os.SystemProperties"); - Object invoker = cl.newInstance(); - Method m = cl.getMethod("get", new Class[]{String.class, String.class}); - Object result = m.invoke(invoker, new Object[]{"gsm.version.baseband", "no message"}); - Version = (String) result; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getBaseband_Ver"); - } - return Version; - } - - /** - * CORE-VER - * 内核版本 - * return String - */ - public static String getLinuxCore_Ver() { - String kernelVersion = ""; - try { - Process process = null; - try { - process = Runtime.getRuntime().exec("cat /proc/version"); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getLinuxCore_Ver - Process"); - } - InputStream outs = process.getInputStream(); - InputStreamReader isrout = new InputStreamReader(outs); - BufferedReader brout = new BufferedReader(isrout, 8 * 1024); - - String result = ""; - String line; - try { - while ((line = brout.readLine()) != null) { - result += line; - } - } catch (IOException e) { - LogPrintUtils.eTag(TAG, e, "getLinuxCore_Ver - readLine"); - } - try { - if (result != "") { - String Keyword = "version "; - int index = result.indexOf(Keyword); - line = result.substring(index + Keyword.length()); - index = line.indexOf(" "); - kernelVersion = line.substring(0, index); - } - } catch (IndexOutOfBoundsException e) { - LogPrintUtils.eTag(TAG, e, "getLinuxCore_Ver - substring"); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getLinuxCore_Ver"); - } - return kernelVersion; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/DialogUtils.java b/DevLibUtils/src/main/java/dev/utils/app/DialogUtils.java deleted file mode 100644 index f67edc7a4b..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/DialogUtils.java +++ /dev/null @@ -1,129 +0,0 @@ -package dev.utils.app; - -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.Context; -import android.widget.PopupWindow; - -/** - * detail: Dialog 操作相关工具类 - * Created by Ttt - */ -public final class DialogUtils { - - private DialogUtils(){ - } - - // ======== Dialog 相关 ======== - - /** - * 关闭Dialog - * @param dialog - */ - public static void closeDialog(Dialog dialog){ - if (dialog != null && dialog.isShowing()){ - dialog.dismiss(); - } - } - - /** - * 关闭多个Dialog - * @param dialogs - */ - public static void closeDialogs(Dialog... dialogs){ - if (dialogs != null && dialogs.length != 0){ - for (int i = 0, len = dialogs.length; i < len; i++){ - // 获取Dialog - Dialog dialog = dialogs[i]; - // 关闭Dialog - if (dialog != null && dialog.isShowing()){ - dialog.dismiss(); - } - } - } - } - - /** - * 关闭PopupWindow - * @param popupWindow - */ - public static void closePopupWindow(PopupWindow popupWindow){ - if (popupWindow != null && popupWindow.isShowing()){ - popupWindow.dismiss(); - } - } - - /** - * 关闭多个PopupWindow - * @param popupWindows - */ - public static void closePopupWindows(PopupWindow... popupWindows){ - if (popupWindows != null && popupWindows.length != 0){ - for (int i = 0, len = popupWindows.length; i < len; i++){ - // 获取Dialog - PopupWindow popupWindow = popupWindows[i]; - // 关闭Dialog - if (popupWindow != null && popupWindow.isShowing()){ - popupWindow.dismiss(); - } - } - } - } - - // == - - /** - * 创建加载 Dialog - * @param context - * @param title - * @param content - * @return - */ - public static ProgressDialog creDialog(Context context, String title, String content){ - return creDialog(context, title, content, false);// 不可以使用返回键 - } - - /** - * 创建加载 Dialog - * @param context - * @param title - * @param content - * @param isCancel - * @return - */ - public static ProgressDialog creDialog(Context context, String title, String content, boolean isCancel){ - ProgressDialog progressDialog = android.app.ProgressDialog.show(context, title, content); - progressDialog.setCancelable(isCancel); - return progressDialog; - } - - /** - * 创建自动关闭dialog - * @param context - * @param title - * @param content - * @return - */ - public static ProgressDialog creAutoCloseDialog(Context context, String title, String content){ - final ProgressDialog progressDialog = android.app.ProgressDialog.show(context, title, content); - progressDialog.setCancelable(false); - Thread thread = new Thread(new Runnable() { - @Override - public void run() { - try { - // 显示10秒后,取消 ProgressDialog - Thread.sleep(10000); - } catch (InterruptedException e) { - } - try { - if (progressDialog != null) { - progressDialog.dismiss(); - } - } catch (Exception e) { - } - } - }); - thread.start(); - return progressDialog; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/EditTextUtils.java b/DevLibUtils/src/main/java/dev/utils/app/EditTextUtils.java deleted file mode 100644 index 7745d80fb1..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/EditTextUtils.java +++ /dev/null @@ -1,374 +0,0 @@ -package dev.utils.app; - -import android.text.Editable; -import android.text.InputFilter; -import android.text.InputType; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.text.method.DigitsKeyListener; -import android.text.method.KeyListener; -import android.widget.EditText; - -import java.util.UUID; - -import dev.utils.LogPrintUtils; - -/** - * detail: EditText 工具类 - * Created by Ttt - */ -public final class EditTextUtils { - - private EditTextUtils(){ - } - - // 日志TAG - private static final String TAG = EditTextUtils.class.getSimpleName(); - - // https://blog.csdn.net/zhoujn90/article/details/44983905 - - // 设置是否显示光标 - // editText.setCursorVisible(true); - - /** - * 设置长度限制,并且设置内容 - * @param editText - * @param content - * @param maxLength - * @return - */ - public static EditText setMaxLengthAnText(EditText editText, String content, int maxLength){ - if (editText != null){ - // 设置长度限制, 并且设置内容 - setText(setMaxLength(editText, maxLength), content); - } - return editText; - } - - /** - * 设置长度限制 - * @param editText - * @param maxLength - * @return - */ - public static EditText setMaxLength(EditText editText, int maxLength){ - if (editText != null){ - if (maxLength > 0){ - // 设置最大长度限制 - InputFilter[] filters = { new InputFilter.LengthFilter(maxLength) }; - editText.setFilters(filters); - } - } - return editText; - } - - /** - * 获取光标位置 - * @param editText - * @return - */ - public static int getSelectionStart(EditText editText){ - if (editText != null){ - return editText.getSelectionStart(); - } - return 0; - } - - /** - * 获取输入的内容 - * @param editText - * @return - */ - public static String getText(EditText editText){ - if (editText != null){ - return editText.getText().toString(); - } - return ""; - } - - /** - * 获取输入的内容长度 - * @param editText - * @return - */ - public static int getTextLength(EditText editText){ - return getText(editText).length(); - } - - // = - - /** - * 设置内容 - * @param editText - * @param content - */ - public static EditText setText(EditText editText, String content){ - return setText(editText, content, true); - } - - /** - * 设置内容 - * @param editText - * @param content - * @param isSelect 是否设置光标 - */ - public static EditText setText(EditText editText, String content, boolean isSelect){ - if (editText != null){ - if (content != null){ - // 设置文本 - editText.setText(content); - if (isSelect){ - // 设置光标 - editText.setSelection(editText.getText().toString().length()); - } - } - } - return editText; - } - - /** - * 追加内容 - 当前光标位置追加 - * @param editText - * @param content - * @param isSelect - * @return - */ - public static EditText insert(EditText editText, String content, boolean isSelect){ - if (editText != null){ - return insert(editText, content, editText.getSelectionStart(), isSelect); - } - return editText; - } - - /** - * 追加内容 - * @param editText - * @param content - * @param start 开始添加的位置 - * @param isSelect - * @return - */ - public static EditText insert(EditText editText, String content, int start, boolean isSelect){ - if (editText != null){ - if (!TextUtils.isEmpty(content)){ - try { - Editable editable = editText.getText(); - // 在指定位置 追加内容 - editable.insert(start, content); - // 判断是否选中 - if (isSelect){ - // 设置光标 - editText.setSelection(editText.getText().toString().length()); - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "insert"); - } - } - } - return editText; - } - - // == - - /** - * 设置光标在第一位 - * @param editText - * @return - */ - public static EditText setSelectTop(EditText editText){ - return setSelect(editText, 0); - } - - /** - * 设置光标在最后一位 - * @param editText - * @return - */ - public static EditText setSelectBottom(EditText editText){ - if (editText != null){ - // 设置光标 - editText.setSelection(editText.getText().toString().length()); - } - return editText; - } - - /** - * 设置光标位置 - * @param editText - * @param select - * @return - */ - public static EditText setSelect(EditText editText, int select){ - if (editText != null){ - if (select >= 0){ - // 判断是否超过限制 - int length = editText.getText().toString().length(); - // 如果超过长度, 则设置最后 - if (select > length){ - // 设置光标 - editText.setSelection(length); - } else { - // 设置光标 - editText.setSelection(select); - } - } - } - return editText; - } - - // = - - /** - * 设置输入限制 - * @param editText - * @param keyListener - * @return - * setKeyListener(editText, getNumberAndEnglishKeyListener()); - */ - public static EditText setKeyListener(EditText editText, KeyListener keyListener){ - if (editText != null){ - editText.setKeyListener(keyListener); - } - return editText; - } - - /** - * 设置输入限制 - * @param editText - * @param digits 只能输入的内容 -> 1234567890 - * @return - * setKeyListener(editText, "1234567890"); - */ - public static EditText setKeyListener(EditText editText, String digits){ - if (editText != null){ - if (TextUtils.isEmpty(digits)){ - editText.setKeyListener(null); - } else { - editText.setKeyListener(DigitsKeyListener.getInstance(digits)); - } - } - return editText; - } - - - // =============== 输入法Key Listener 快捷处理 ========================== - - /** - * 限制只能输入字母和数字,默认弹出英文输入法 - * @return - */ - public static DigitsKeyListener getNumberAndEnglishKeyListener() { - /** 限制只能输入字母和数字,默认弹出英文输入法 */ - DigitsKeyListener digitsKeyListener = new DigitsKeyListener() { - @Override - public int getInputType() { - return InputType.TYPE_TEXT_VARIATION_PASSWORD; - } - - @Override - protected char[] getAcceptedChars() { - char[] data = "qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPLKJHGFDSAZXCVBNM1234567890".toCharArray(); - return data; - } - }; - return digitsKeyListener; - } - - /** - * 限制只能输入数字,默认弹出数字列表 - * @return - */ - public static DigitsKeyListener getNumberKeyListener() { - /** 限制只能输入数字,默认弹出数字列表 */ - DigitsKeyListener digitsKeyListener = new DigitsKeyListener() { - @Override - public int getInputType() { - return InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL; - } - - @Override - protected char[] getAcceptedChars() { - char[] data = "1234567890".toCharArray(); - return data; - } - }; - return digitsKeyListener; - } - - // == - - // https://blog.csdn.net/zhuwentao2150/article/details/51546773 - - // editText.addTextChangedListener(DevTextWatcher); - - /** - * detail: 开发输入监听抽象类 - * Created by Ttt - */ - public static abstract class DevTextWatcher implements TextWatcher { - - // 标记id - 一定程度上唯一 - private final int markId; - // 判断是否操作中 - 操作状态 -> 如果是否使用搜索数据等 - private boolean isOperate = false; - // 标记状态, 特殊需求处理 - private int operateState = -1; - - public DevTextWatcher(){ - // 初始化id - this.markId = UUID.randomUUID().hashCode(); - } - - public final int getMarkId() { - return markId; - } - - public final boolean isOperate() { - return isOperate; - } - - public final void setOperate(boolean operate) { - isOperate = operate; - } - - public final int getOperateState() { - return operateState; - } - - public final void setOperateState(int operateState) { - this.operateState = operateState; - } - - // == 回调接口 == - - /** - * 在文本变化前调用 - * @param s - * @param start - * @param count - * @param after - */ - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - /** - * 在文本变化后调用 - * @param s - * @param start - * @param before - * @param count - */ - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - /** - * 在文本变化后调用 - * @param s - */ - @Override - public void afterTextChanged(Editable s) { - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/EncodeUtils.java b/DevLibUtils/src/main/java/dev/utils/app/EncodeUtils.java deleted file mode 100644 index 403426b8e8..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/EncodeUtils.java +++ /dev/null @@ -1,192 +0,0 @@ -package dev.utils.app; - -import android.os.Build; -import android.text.Html; -import android.util.Base64; - -import java.net.URLDecoder; -import java.net.URLEncoder; - -import dev.utils.LogPrintUtils; - -/** - * detail: 编码工具类 - * Created by Ttt - */ -public final class EncodeUtils { - - private EncodeUtils() { - } - - // 日志TAG - private static final String TAG = EncodeUtils.class.getSimpleName(); - - /** - * url编码 - UTF-8 - * @param input The input. - * @return the urlencoded string - */ - public static String urlEncode(final String input) { - return urlEncode(input, "UTF-8"); - } - - /** - * url编码 - * @param input The input. - * @param charsetName The name of charset. - * @return the urlencoded string - */ - public static String urlEncode(final String input, final String charsetName) { - try { - return URLEncoder.encode(input, charsetName); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "urlEncode"); - return null; - } - } - - /** - * url 解码 - UTF-8 - * @param input The input. - * @return the string of decode urlencoded string - */ - public static String urlDecode(final String input) { - return urlDecode(input, "UTF-8"); - } - - /** - * url 解码 - * @param input The input. - * @param charsetName The name of charset. - * @return the string of decode urlencoded string - */ - public static String urlDecode(final String input, final String charsetName) { - try { - return URLDecoder.decode(input, charsetName); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "urlDecode"); - return null; - } - } - -// CRLF 这个参数看起来比较眼熟,它就是Win风格的换行符,意思就是使用CR LF这一对作为一行的结尾而不是Unix风格的LF -// -// DEFAULT 这个参数是默认,使用默认的方法来加密 -// -// NO_PADDING 这个参数是略去加密字符串最后的”=” -// -// NO_WRAP 这个参数意思是略去所有的换行符(设置后CRLF就没用了) -// -// URL_SAFE 这个参数意思是加密时不使用对URL和文件名有特殊意义的字符来作为加密字符,具体就是以-和_取代+和/ - - /** - * base64 编码 => 去除\n 替换 + 和 - 号 - * @param input The input. - * @return Base64-encode bytes - */ - public static byte[] base64Encode(final String input) { - return base64Encode(input.getBytes()); - } - - /** - * base64 编码 - * @param input The input. - * @return Base64-encode bytes - */ - public static byte[] base64Encode(final byte[] input) { - return Base64.encode(input, Base64.NO_WRAP); - } - - /** - * base64 编码 - * @param input The input. - * @return Base64-encode string - */ - public static String base64Encode2String(final byte[] input) { - return Base64.encodeToString(input, Base64.NO_WRAP); - } - - /** - * base64 解码 - * @param input The input. - * @return the string of decode Base64-encode string - */ - public static byte[] base64Decode(final String input) { - return Base64.decode(input, Base64.NO_WRAP); - } - - /** - * base64 解码 - * @param input The input. - * @return the bytes of decode Base64-encode bytes - */ - public static byte[] base64Decode(final byte[] input) { - return Base64.decode(input, Base64.NO_WRAP); - } - - /** - * base64 解码 - * @param input - * @return - */ - public static String base64DecodeToString(final byte[] input) { - try { - return new String(base64Decode(input)); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "base64DecodeToString"); - } - return null; - } - - /** - * html编码 - * @param input The input. - * @return html-encode string - */ - public static String htmlEncode(final CharSequence input) { - StringBuilder sb = new StringBuilder(); - char c; - for (int i = 0, len = input.length(); i < len; i++) { - c = input.charAt(i); - switch (c) { - case '<': - sb.append("<"); //$NON-NLS-1$ - break; - case '>': - sb.append(">"); //$NON-NLS-1$ - break; - case '&': - sb.append("&"); //$NON-NLS-1$ - break; - case '\'': - //http://www.w3.org/TR/xhtml1 - // The named character reference ' (the apostrophe, U+0027) was - // introduced in XML 1.0 but does not appear in HTML. Authors should - // therefore use ' instead of ' to work as expected in HTML 4 - // user agents. - sb.append("'"); //$NON-NLS-1$ - break; - case '"': - sb.append("""); //$NON-NLS-1$ - break; - default: - sb.append(c); - } - } - return sb.toString(); - } - - /** - * html解码 - * @param input The input. - * @return the string of decode html-encode string - */ - @SuppressWarnings("deprecation") - public static CharSequence htmlDecode(final String input) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - return Html.fromHtml(input, Html.FROM_HTML_MODE_LEGACY); - } else { - return Html.fromHtml(input); - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/ErrorUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ErrorUtils.java deleted file mode 100644 index ff8ca24f5a..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ErrorUtils.java +++ /dev/null @@ -1,133 +0,0 @@ -package dev.utils.app; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; - -/** - * detail: 错误信息处理工具类 - * Created by Ttt - */ -public final class ErrorUtils { - - private ErrorUtils(){ - } - - /** 换行字符串 */ - private static final String NEW_LINE_STR = System.getProperty("line.separator"); - /** 换行字符串 - 两行 */ - private static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; - - /** - * 获取错误信息 - * @param e - * @return - */ - public static String getErrorMsg(Exception e){ - try { - if (e != null){ - return e.getMessage(); - } - } catch (Exception ex){ - return ex.getMessage(); - } - return "e(null)"; - } - - /** - * 将异常栈信息转为字符串 - * @param e 字符串 - * @return 异常栈 - */ - public static String getThrowableMsg(Throwable e) { - try { - if (e != null) { - StringWriter writer = new StringWriter(); - e.printStackTrace(new PrintWriter(writer)); - return writer.toString(); - } - } catch (Exception ex){ - return ex.getMessage(); - } - return "e(null)"; - } - - // ================== 异常信息处理 =================== - - /** - * 获取错误信息(无换行) - * @param eHint 获取失败提示 - * @param ex 错误信息 - * @return - */ - public static String getThrowableMsg(String eHint, Throwable ex) { - PrintWriter printWriter = null; - try { - if(ex != null) { - // 初始化Writer,PrintWriter打印流 - Writer writer = new StringWriter(); - printWriter = new PrintWriter(writer); - // 写入错误栈信息 - ex.printStackTrace(printWriter); - // 关闭流 - printWriter.close(); - return writer.toString(); - } - } catch (Exception e) { - } finally { - if(printWriter != null) { - printWriter.close(); - } - } - return eHint; - } - - /** - * 获取错误信息(有换行) - * @param ex 错误信息 - * @return 默认返回 "" - */ - public static String getThrowableNewLinesMsg(Throwable ex) { - return getThrowableNewLinesMsg("", ex); - } - - /** - * 获取错误信息(有换行) - * @param eHint 获取失败提示 - * @param ex 错误信息 - * @return - */ - public static String getThrowableNewLinesMsg(String eHint, Throwable ex) { - PrintWriter printWriter = null; - try { - if(ex != null) { - // 初始化Writer,PrintWriter打印流 - Writer writer = new StringWriter(); - printWriter = new PrintWriter(writer); - // 获取错误栈信息 - StackTraceElement[] stElement = ex.getStackTrace(); - // 标题,提示属于什么异常 - printWriter.append(ex.toString()); - printWriter.append(NEW_LINE_STR); - // 遍历错误栈信息,并且进行换行,缩进 - for(StackTraceElement st : stElement) { - printWriter.append("\tat "); - printWriter.append(st.toString()); - printWriter.append(NEW_LINE_STR); - } - // 关闭流 - printWriter.close(); - return writer.toString(); - } - } catch (Exception e) { - } finally { - if(printWriter != null) { - try { - printWriter.close(); - } catch (Exception e) { - } - } - } - return eHint; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/FileRecordUtils.java b/DevLibUtils/src/main/java/dev/utils/app/FileRecordUtils.java deleted file mode 100644 index 9679193688..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/FileRecordUtils.java +++ /dev/null @@ -1,499 +0,0 @@ -package dev.utils.app; - -import android.annotation.SuppressLint; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.Build; -import android.text.TextUtils; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.reflect.Field; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: App 文件记录工具类 - * Created by Ttt - */ -public final class FileRecordUtils { - - private FileRecordUtils(){ - } - - // 日志TAG - private static final String TAG = FileRecordUtils.class.getSimpleName(); - - // =================== 配置信息 ======================= - - /** App版本(如1.0.01) 显示给用户看的 */ - static String APP_VERSION_NAME = ""; - - /** android:versionCode——整数值,代表应用程序代码的相对版本,也就是版本更新过多少次。(不显示给用户看) */ - static String APP_VERSION_CODE = ""; - - /** 设备信息 */ - static String DEVICE_INFO_STR = null; - - /** 用来存储设备信息 */ - static HashMap DEVICE_INFO_MAPS = new HashMap(); - - /** 换行字符串 */ - static final String NEW_LINE_STR = System.getProperty("line.separator"); - - /** 换行字符串 - 两行 */ - static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; - - // ================== APP、设备信息处理 =================== - - /** - * 获取app版本信息 - */ - static String[] getAppVersion() { - String[] aVersion = null; - try { - PackageManager pm = DevUtils.getContext().getPackageManager(); - PackageInfo pi = pm.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_ACTIVITIES); - if (pi != null) { - String versionName = pi.versionName == null ? "null" : pi.versionName; - String versionCode = pi.versionCode + ""; - // -- - aVersion = new String[]{versionName,versionCode}; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppVersion"); - } - return aVersion; - } - - /** - * 获取设备信息 - * @param dInfoMaps 传入设备信息传出HashMap - */ - static void getDeviceInfo(HashMap dInfoMaps) { - // 获取设备信息类的所有申明的字段,即包括public、private和proteced, 但是不包括父类的申明字段。 - Field[] fields = Build.class.getDeclaredFields(); - // 遍历字段 - for (Field field : fields) { - try { - // 取消java的权限控制检查 - field.setAccessible(true); - // 获取类型对应字段的数据,并保存 - dInfoMaps.put(field.getName(), field.get(null).toString()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); - } - } - } - - /** - * 处理设备信息 - * @param eHint 错误提示,如获取设备信息失败 - */ - static String handleDeviceInfo(String eHint) { - try { - // 如果不为null,则直接返回之前的信息 - if(!TextUtils.isEmpty(DEVICE_INFO_STR)) { - return DEVICE_INFO_STR; - } - // 初始化StringBuilder,拼接字符串 - StringBuilder sBuilder = new StringBuilder(); - // 获取设备信息 - Iterator> mapIter = DEVICE_INFO_MAPS.entrySet().iterator(); - // 遍历设备信息 - while (mapIter.hasNext()) { - // 获取对应的key-value - Map.Entry rnEntry = (Map.Entry) mapIter.next(); - String rnKey = (String) rnEntry.getKey(); // key - String rnValue = (String) rnEntry.getValue(); // value - // 保存设备信息 - sBuilder.append(rnKey); - sBuilder.append(" = "); - sBuilder.append(rnValue); - sBuilder.append(NEW_LINE_STR); - } - // 保存设备信息 - DEVICE_INFO_STR = sBuilder.toString(); - // 返回设备信息 - return DEVICE_INFO_STR; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleDeviceInfo"); - } - return eHint; - } - - // ================== 时间格式化 =================== - - /** 日期格式类型 */ - static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; - - /** - * 获取当前日期的字符串 - * @return 字符串 - */ - @SuppressLint("SimpleDateFormat") - static String getDateNow() { - try { - Calendar cld = Calendar.getInstance(); - DateFormat df = new SimpleDateFormat(yyyyMMddHHmmss); - return df.format(cld.getTime()); - } catch (Exception e) { - } - return null; - } - - - // ================== 文件操作 =================== - - /** - * 判断某个文件夹是否创建,未创建则创建(不能加入文件名) - * @param fPath 文件夹路径 - */ - static File createFile(String fPath) { - try { - File file = new File(fPath); - // 当这个文件夹不存在的时候则创建文件夹 - if(!file.exists()) { - // 允许创建多级目录 - file.mkdirs(); - } - return file; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "createFile"); - } - return null; - } - - /** - * 保存文件 - * @param txt 保存内容 - * @param fUrl 保存路径(包含文件名.后缀) - * @return 是否保存成功 - */ - static boolean saveFile(String txt, String fUrl) { - try { - // 保存内容到一个文件 - FileOutputStream fos = new FileOutputStream(fUrl); - fos.write(txt.getBytes()); - fos.close(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "saveFile"); - return false; - } - return true; - } - - - // ================== 异常信息处理 =================== - - /** - * 获取错误信息(无换行) - * @param eHint 获取失败提示 - * @param ex 错误信息 - * @return - */ - static String getThrowableMsg(String eHint, Throwable ex) { - PrintWriter printWriter = null; - try { - if(ex != null) { - // 初始化Writer,PrintWriter打印流 - Writer writer = new StringWriter(); - printWriter = new PrintWriter(writer); - // 写入错误栈信息 - ex.printStackTrace(printWriter); - // 关闭流 - printWriter.close(); - return writer.toString(); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getThrowableMsg"); - } finally { - if(printWriter != null) { - printWriter.close(); - } - } - return eHint; - } - - /** - * 获取错误信息(有换行) - * @param eHint 获取失败提示 - * @param ex 错误信息 - * @return - */ - static String getThrowableNewLinesMsg(String eHint, Throwable ex) { - PrintWriter printWriter = null; - try { - if(ex != null) { - // 初始化Writer,PrintWriter打印流 - Writer writer = new StringWriter(); - printWriter = new PrintWriter(writer); - // 获取错误栈信息 - StackTraceElement[] stElement = ex.getStackTrace(); - // 标题,提示属于什么异常 - printWriter.append(ex.toString()); - printWriter.append(NEW_LINE_STR); - // 遍历错误栈信息,并且进行换行,缩进 - for(StackTraceElement st : stElement) { - printWriter.append("\tat "); - printWriter.append(st.toString()); - printWriter.append(NEW_LINE_STR); - } - // 关闭流 - printWriter.close(); - return writer.toString(); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getThrowableNewLinesMsg"); - } finally { - if(printWriter != null) { - try { - printWriter.close(); - } catch (Exception e) { - } - } - } - return eHint; - } - - // ================== 对外公开方法 =================== - - /** - * App初始化调用方法 - */ - public static void appInit() { - // 如果版本信息为null,才进行处理 - if (TextUtils.isEmpty(APP_VERSION_CODE) || TextUtils.isEmpty(APP_VERSION_NAME)) { - // 获取app版本信息 - String[] aVersion = getAppVersion(); - // 保存app版本信息 - APP_VERSION_NAME = aVersion[0]; - APP_VERSION_CODE = aVersion[1]; - } - // 判断是否存在设备信息 - if (DEVICE_INFO_MAPS.size() == 0) { - // 获取设备信息 - getDeviceInfo(DEVICE_INFO_MAPS); - // 转换字符串 - handleDeviceInfo(""); - } - } - - // ========= 保存错误日志信息 ========== - - /** - * 保存app错误日志 - * @param ex 错误信息 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param isNewLines 是否换行 - * @param printDevice 是否打印设备信息 - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveErrorLog(Throwable ex, String fPath, String fName, boolean isNewLines, boolean printDevice, String... eHint) { - return saveErrorLog(ex, null, null, fPath, fName, isNewLines, printDevice, eHint); - } - - /** - * 保存app错误日志 - * @param ex 错误信息 - * @param head 顶部标题 - * @param bottom 底部内容 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param isNewLines 是否换行 - * @param printDevice 是否打印设备信息 - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveErrorLog(Throwable ex, String head, String bottom, String fPath, String fName, boolean isNewLines, boolean printDevice, String... eHint) { - // 处理可变参数(错误提示) - eHint = handleVariable(2, eHint); - // 日志拼接 - StringBuilder sBuilder = new StringBuilder(); - // 防止文件夹不存在 - createFile(fPath); - // 设备信息 - String dInfo = handleDeviceInfo(eHint[0]); - // 如果存在顶部内容,则进行添加 - if(!TextUtils.isEmpty(head)) { - sBuilder.append(head); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - } - // ============ - // 保存App信息 - sBuilder.append("date: " + getDateNow()); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("versionName: " + APP_VERSION_NAME); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("versionCode: " + APP_VERSION_CODE); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - // 如果需要打印设备信息 - if (printDevice) { - // 保存设备信息 - sBuilder.append(dInfo); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - } - // ============ - // 错误信息 - String eMsg = null; - // 是否换行 - if(isNewLines) { - eMsg = getThrowableNewLinesMsg(eHint[1], ex); - } else { - eMsg = getThrowableMsg(eHint[1], ex); - } - // 保存异常信息 - sBuilder.append(eMsg); - // 如果存在顶部内容,则进行添加 - if(!TextUtils.isEmpty(bottom)) { - sBuilder.append(NEW_LINE_STR); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append(bottom); - } - // 保存日志到文件 - return saveFile(sBuilder.toString(), fPath + File.separator + fName); - } - - /** - * 保存app日志 - * @param log 日志信息 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param printDevice 是否打印设备信息 - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveLog(String log, String fPath, String fName, boolean printDevice, String... eHint){ - return saveLog(log, null, null, fPath, fName, printDevice, eHint); - } - - /** - * 保存app日志 - * @param log 日志信息 - * @param head 顶部标题 - * @param bottom 底部内容 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param printDevice 是否打印设备信息 - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveLog(String log, String head, String bottom, String fPath, String fName, boolean printDevice, String... eHint){ - // 处理可变参数(错误提示) - eHint = handleVariable(2, eHint); - // 日志拼接 - StringBuilder sBuilder = new StringBuilder(); - // 防止文件夹不存在 - createFile(fPath); - // 设备信息 - String dInfo = handleDeviceInfo(eHint[0]); - // 如果存在顶部内容,则进行添加 - if(!TextUtils.isEmpty(head)) { - sBuilder.append(head); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - } - // ============ - // 保存App信息 - sBuilder.append("date: " + getDateNow()); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("versionName: " + APP_VERSION_NAME); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("versionCode: " + APP_VERSION_CODE); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - // 如果需要打印设备信息 - if (printDevice) { - // 保存设备信息 - sBuilder.append(dInfo); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - } - // ============ - // 保存日志信息 - sBuilder.append(log); - // 如果存在顶部内容,则进行添加 - if(!TextUtils.isEmpty(bottom)) { - sBuilder.append(NEW_LINE_STR); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append(bottom); - } - // 保存日志到文件 - return saveFile(sBuilder.toString(), fPath + File.separator + fName); - } - - // == - - /** - * 处理可变参数 - * @param length 保留长度 - * @param vArrays 可变参数数组 - * @return - */ - public static String[] handleVariable(int length, String[] vArrays) { - // 处理后的数据, - String[] hArrays = new String[length]; - // 是否统一处理 - boolean isUnifiedHandler = true; - try { - if(vArrays != null) { - // 获取可变参数数组长度 - int vLength = vArrays.length; - // 如果长度超出预留长度 - if(vLength >= length) { - for(int i = 0;i < length;i++) { - if(vArrays[i] == null) { - hArrays[i] = ""; - } else { - hArrays[i] = vArrays[i]; - } - } - // 但可变参数长度,超过预留长度时,已经处理完毕,不需要再次处理,节省遍历资源 - isUnifiedHandler = false; - } else { - for(int i = 0;i < vLength;i++) { - if(vArrays[i] == null) { - hArrays[i] = ""; - } else { - hArrays[i] = vArrays[i]; - } - } - } - } - if(isUnifiedHandler) { - // 统一处理,如果数据未null,则设置为“”,防止拼接出现 "null" - for(int i = 0;i < length;i++) { - if(hArrays[i] == null) { - hArrays[i] = ""; - } - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleVariable"); - } - return hArrays; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/HandlerUtils.java b/DevLibUtils/src/main/java/dev/utils/app/HandlerUtils.java deleted file mode 100644 index fed98ae7c5..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/HandlerUtils.java +++ /dev/null @@ -1,80 +0,0 @@ -package dev.utils.app; - -import android.content.Context; -import android.os.Handler; - -/** - * detail: Handler 工具类, 默认开启一个 Handler,方便在各个地方随时执行主线程任务 - * Created by Ttt - */ -public final class HandlerUtils { - - private HandlerUtils() { - } - - /** 主线程 Handler */ - private static Handler mainHandler; - - /** - * 初始化操作 - * @param context - */ - public static void init(Context context) { - if (mainHandler == null) mainHandler = new Handler(context.getMainLooper()); - } - - /** - * 获取主线程 Handler - * @return 主线程 Handler - */ - public static Handler getMainHandler() { - return mainHandler; - } - - /** - * 在主线程 Handler 中执行任务 - * @param runnable 可执行的任务 - */ - public static void postRunnable(Runnable runnable) { - getMainHandler().post(runnable); - } - - /** - * 在主线程 Handler 中执行延迟任务 - * @param runnable 可执行的任务 - * @param delay 延迟时间 - */ - public static void postRunnable(Runnable runnable, long delay) { - getMainHandler().postDelayed(runnable, delay); - } - - /** - * 在主线程 Handler 中执行延迟任务 - * @param runnable 可执行的任务 - * @param delay 延迟时间 - * @param times 轮询次数 - * @param interval 轮询时间 - */ - public static void postRunnable(final Runnable runnable, long delay, final int times, final int interval) { - Runnable loop = new Runnable() { - private int mTimes; - @Override - public void run() { - if (mTimes < times) { - runnable.run(); - getMainHandler().postDelayed(this, interval); - } - mTimes++; - } - }; - getMainHandler().postDelayed(loop, delay); - } - - /** - * 在主线程 Handler 中清除任务 - * @param runnable 需要清除的任务 - */ - public static void removeRunnable(Runnable runnable) { - getMainHandler().removeCallbacks(runnable); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/HtmlUtils.java b/DevLibUtils/src/main/java/dev/utils/app/HtmlUtils.java deleted file mode 100644 index 24c67fb056..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/HtmlUtils.java +++ /dev/null @@ -1,120 +0,0 @@ -package dev.utils.app; - -import android.text.Html; -import android.widget.TextView; - -/** - * detail: Html 工具类 - * Created by Ttt - */ -public final class HtmlUtils { - - private HtmlUtils() { - } - - /** - * 设置内容, 最终做处理 - * @param textView - * @param content - */ - public static void setHtmlText(TextView textView, String content){ - if (textView != null && content != null){ - textView.setText(Html.fromHtml(content)); - } - } - - /** - * 为给定的字符串添加HTML颜色标记 - * @param content 给定的字符串 - * @return 最后放在 Html.fromHtml();内 - */ - public static String addHtmlColor(String content, String color) { - return "" + content + ""; - } - - /** - * 为给定的字符串添加HTML颜色标记 - * @param content 给定的字符串 - * @param fString 格式化的内容 - * @return 最后放在 Html.fromHtml();内 - */ - public static String addHtmlColor(String content, String fString, String color) { - return String.format(content, (""+ fString +"")); - } - - /** - * 为给定的字符串添加HTML加粗标记 - * @param content 给定的字符串 - * @return 最后放在 Html.fromHtml();内 - * ====================== - * 这种方式也可以加粗,但是是所有字体加粗(非部分字体加粗) - * android.text.TextPaint tp = textView.getPaint(); - * tp.setFakeBoldText(true); - */ - public static String addHtmlBold(String content) { - return "" + content + ""; - } - - /** - * 为给定的字符串添加HTML颜色标记,以及加粗 - * @param content 给定的字符串 - * @return 最后放在 Html.fromHtml();内 - */ - public static String addHtmlColorAndBlod(String content, String color) { - return "" + content + ""; - } - - /** - * 为给定的字符串添加HTML下划线 - * @param content 给定的字符串 - * @return 最后放在 Html.fromHtml();内 - */ - public static String addHtmlUnderline(String content) { - return "" + content + ""; - } - - /** - * 为给定的字符串添加HTML 字体倾斜 - * @param content 给定的字符串 - * @return 最后放在 Html.fromHtml();内 - * ================================= - * 如果需要倾斜自定义角度,需要自定义TextView,在onDraw里面加上 - * - 倾斜度,上下左右居中 - * canvas.rotate(倾斜角度, getMeasuredWidth() / 3, getMeasuredHeight() / 3); - */ - public static String addHtmlIncline(String content) { - return "" + content + ""; - } - - /** - * 将给定的字符串中所有给定的关键字标色 - * @param source 给定的字符串 - * @param keyword 给定的关键字 - * @param color 需要标记的颜色 - * @return - */ - public static String keywordMadeRed(String source, String keyword, String color) { - return keywordReplaceAll(source, keyword, ("" + keyword + "")); - } - - /** - * 将给定的字符串中所有给定的关键字进行替换内容 - * @param source 给定的字符串 - * @param keyword 给定的关键字 - * @param replacement 替换的内容 - * @return 返回的是带Html标签的字符串,在使用时要通过Html.fromHtml() 转换为Spanned对象再传递给TextView对象 - */ - public static String keywordReplaceAll(String source, String keyword, String replacement) { - try { - if(source != null && source.trim().length() != 0) { - if (keyword != null && keyword.trim().length() != 0) { - if(replacement != null && replacement.trim().length() != 0) { - return source.replaceAll(keyword , replacement); - } - } - } - } catch (Exception e) { - } - return source; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/IntentUtils.java b/DevLibUtils/src/main/java/dev/utils/app/IntentUtils.java deleted file mode 100644 index 0002476e65..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/IntentUtils.java +++ /dev/null @@ -1,533 +0,0 @@ -package dev.utils.app; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.provider.MediaStore; -import android.provider.Settings; -import android.support.v4.content.FileProvider; -import android.text.TextUtils; - -import java.io.File; - -import dev.DevUtils; -import dev.utils.common.FileUtils; - -/** - * detail: Intent(意图) 相关工具类 - * Created by Ttt - */ -public final class IntentUtils { - - private IntentUtils() { - } - - /** - * 获取安装 App(支持 8.0)的意图 - * - * @param filePath The path of file. - * @param authority 7.0 及以上安装需要传入清单文件中的}的 authorities 属性 - *
参看 https://developer.android.com/reference/android/support/v4/content/FileProvider.html - * @return 安装 App(支持 8.0)的意图 - */ - public static Intent getInstallAppIntent(final String filePath, final String authority) { - return getInstallAppIntent(FileUtils.getFileByPath(filePath), authority); - } - - /** - * 获取安装 App(支持 8.0)的意图 - * - * @param file The file. - * @param authority 7.0 及以上安装需要传入清单文件中的}的 authorities 属性 - *
参看 https://developer.android.com/reference/android/support/v4/content/FileProvider.html - * @return 安装 App(支持 8.0)的意图 - */ - public static Intent getInstallAppIntent(final File file, final String authority) { - return getInstallAppIntent(file, authority, false); - } - - /** - * 获取安装 App(支持 8.0)的意图 - * - * @param file The file. - * @param authority 7.0 及以上安装需要传入清单文件中的}的 authorities 属性 - *
参看 https://developer.android.com/reference/android/support/v4/content/FileProvider.html - * @param isNewTask 是否开启新的任务栈 - * @return 安装 App(支持 8.0)的意图 - */ - public static Intent getInstallAppIntent(final File file, final String authority, final boolean isNewTask) { - if (file == null) return null; - Intent intent = new Intent(Intent.ACTION_VIEW); - Uri data; - String type = "application/vnd.android.package-archive"; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { - data = Uri.fromFile(file); - } else { - intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - data = FileProvider.getUriForFile(DevUtils.getContext(), authority, file); - } - intent.setDataAndType(data, type); - return getIntent(intent, isNewTask); - } - - /** - * 获取卸载 App 的意图 - * @param packageName The name of the package. - * @return 卸载 App 的意图 - */ - public static Intent getUninstallAppIntent(final String packageName) { - return getUninstallAppIntent(packageName, false); - } - - /** - * 获取卸载 App 的意图 - * @param packageName The name of the package. - * @param isNewTask 是否开启新的任务栈 - * @return 卸载 App 的意图 - */ - public static Intent getUninstallAppIntent(final String packageName, final boolean isNewTask) { - Intent intent = new Intent(Intent.ACTION_DELETE); - intent.setData(Uri.parse("package:" + packageName)); - return getIntent(intent, isNewTask); - } - - /** - * 获取打开 App 的意图 - * @param packageName The name of the package. - * @return 打开 App 的意图 - */ - public static Intent getLaunchAppIntent(final String packageName) { - return getLaunchAppIntent(packageName, false); - } - - /** - * 获取打开 App 的意图 - * @param packageName The name of the package. - * @param isNewTask 是否开启新的任务栈 - * @return 打开 App 的意图 - */ - public static Intent getLaunchAppIntent(final String packageName, final boolean isNewTask) { - Intent intent = DevUtils.getContext().getPackageManager().getLaunchIntentForPackage(packageName); - if (intent == null) return null; - return getIntent(intent, isNewTask); - } - - /** - * 获取 App 具体设置的意图 - * @param packageName The name of the package. - * @return App 具体设置的意图 - */ - public static Intent getLaunchAppDetailsSettingsIntent(final String packageName) { - return getLaunchAppDetailsSettingsIntent(packageName, false); - } - - /** - * 获取 App 具体设置的意图 - * @param packageName The name of the package. - * @param isNewTask 是否开启新的任务栈 - * @return App 具体设置的意图 - */ - public static Intent getLaunchAppDetailsSettingsIntent(final String packageName, final boolean isNewTask) { - Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS"); - intent.setData(Uri.parse("package:" + packageName)); - return getIntent(intent, isNewTask); - } - - /** - * 获取 到应用商店app详情界面的意图 - * @param packageName 目标App的包名 - * @param marketPkg 应用商店包名 ,如果为""则由系统弹出应用商店列表供用户选择,否则调转到目标市场的应用详情界面,某些应用商店可能会失败 - */ - public static Intent getlaunchAppDetailIntent(final String packageName, final String marketPkg) { - return getlaunchAppDetailIntent(packageName, marketPkg, false); - } - - /** - * 获取 到应用商店app详情界面的意图 - * @param packageName 目标App的包名 - * @param marketPkg 应用商店包名 ,如果为""则由系统弹出应用商店列表供用户选择,否则调转到目标市场的应用详情界面,某些应用商店可能会失败 - * @param isNewTask 是否开启新的任务栈 - */ - public static Intent getlaunchAppDetailIntent(final String packageName, final String marketPkg, final boolean isNewTask) { - try { - if (TextUtils.isEmpty(packageName)) return null; - - Uri uri = Uri.parse("market://details?id=" + packageName); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - if (!TextUtils.isEmpty(marketPkg)) { - intent.setPackage(marketPkg); - } - return getIntent(intent, isNewTask); - } catch (Exception e) { - } - return null; - } - - /** - * 获取分享文本的意图 - * @param content 分享文本 - * @return 分享文本的意图 - */ - public static Intent getShareTextIntent(final String content) { - return getShareTextIntent(content, false); - } - - /** - * 获取分享文本的意图 - * @param content 分享文本 - * @param isNewTask 是否开启新的任务栈 - * @return 分享文本的意图 - */ - - public static Intent getShareTextIntent(final String content, final boolean isNewTask) { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_TEXT, content); - return getIntent(intent, isNewTask); - } - - /** - * 获取分享图片的意图 - * @param content 文本 - * @param imagePath 图片文件路径 - * @return 分享图片的意图 - */ - public static Intent getShareImageIntent(final String content, final String imagePath) { - return getShareImageIntent(content, imagePath, false); - } - - /** - * 获取分享图片的意图 - * @param content 文本 - * @param imagePath 图片文件路径 - * @param isNewTask 是否开启新的任务栈 - * @return 分享图片的意图 - */ - public static Intent getShareImageIntent(final String content, final String imagePath, final boolean isNewTask) { - if (imagePath == null || imagePath.length() == 0) return null; - return getShareImageIntent(content, new File(imagePath), isNewTask); - } - - /** - * 获取分享图片的意图 - * @param content 文本 - * @param image 图片文件 - * @return 分享图片的意图 - */ - public static Intent getShareImageIntent(final String content, final File image) { - return getShareImageIntent(content, image, false); - } - - /** - * 获取分享图片的意图 - * @param content 文本 - * @param image 图片文件 - * @param isNewTask 是否开启新的任务栈 - * @return 分享图片的意图 - */ - public static Intent getShareImageIntent(final String content, final File image, final boolean isNewTask) { - if (image != null && image.isFile()) return null; - return getShareImageIntent(content, Uri.fromFile(image), isNewTask); - } - - /** - * 获取分享图片的意图 - * @param content 分享文本 - * @param uri 图片 uri - * @return 分享图片的意图 - */ - public static Intent getShareImageIntent(final String content, final Uri uri) { - return getShareImageIntent(content, uri, false); - } - - /** - * 获取分享图片的意图 - * @param content 分享文本 - * @param uri 图片 uri - * @param isNewTask 是否开启新的任务栈 - * @return 分享图片的意图 - */ - public static Intent getShareImageIntent(final String content, final Uri uri, final boolean isNewTask) { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.putExtra(Intent.EXTRA_TEXT, content); - intent.putExtra(Intent.EXTRA_STREAM, uri); - intent.setType("image/*"); - return getIntent(intent, isNewTask); - } - - /** - * 获取其他应用组件的意图 - * @param packageName The name of the package. - * @param className 全类名 - * @return 其他应用组件的意图 - */ - public static Intent getComponentIntent(final String packageName, final String className) { - return getComponentIntent(packageName, className, null, false); - } - - /** - * 获取其他应用组件的意图 - * @param packageName The name of the package. - * @param className 全类名 - * @param isNewTask 是否开启新的任务栈 - * @return 其他应用组件的意图 - */ - public static Intent getComponentIntent(final String packageName, final String className, final boolean isNewTask) { - return getComponentIntent(packageName, className, null, isNewTask); - } - - /** - * 获取其他应用组件的意图 - * @param packageName The name of the package. - * @param className 全类名 - * @param bundle bundle - * @return 其他应用组件的意图 - */ - public static Intent getComponentIntent(final String packageName, final String className, final Bundle bundle) { - return getComponentIntent(packageName, className, bundle, false); - } - - /** - * 获取其他应用组件的意图 - * @param packageName The name of the package. - * @param className 全类名 - * @param bundle bundle - * @param isNewTask 是否开启新的任务栈 - * @return 其他应用组件的意图 - */ - public static Intent getComponentIntent(final String packageName, final String className, final Bundle bundle, final boolean isNewTask) { - Intent intent = new Intent(Intent.ACTION_VIEW); - if (bundle != null) intent.putExtras(bundle); - ComponentName cn = new ComponentName(packageName, className); - intent.setComponent(cn); - return getIntent(intent, isNewTask); - } - - /** - * 获取关机的意图 - * - * @return 关机的意图 - */ - public static Intent getShutdownIntent() { - return getShutdownIntent(false); - } - - /** - * 获取关机的意图 - * - * @param isNewTask 是否开启新的任务栈 - * @return 关机的意图 - */ - public static Intent getShutdownIntent(final boolean isNewTask) { - Intent intent = new Intent(Intent.ACTION_SHUTDOWN); - return getIntent(intent, isNewTask); - } - - /** - * 获取跳至拨号界面意图 - * @param phoneNumber 电话号码 - * @return 跳至拨号界面意图 - */ - public static Intent getDialIntent(final String phoneNumber) { - return getDialIntent(phoneNumber, false); - } - - /** - * 获取跳至拨号界面意图 - * @param phoneNumber 电话号码 - * @param isNewTask 是否开启新的任务栈 - * @return 跳至拨号界面意图 - */ - public static Intent getDialIntent(final String phoneNumber, final boolean isNewTask) { - Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber)); - return getIntent(intent, isNewTask); - } - - /** - * 获取拨打电话意图 - * - * @param phoneNumber 电话号码 - * @return 拨打电话意图 - */ - public static Intent getCallIntent(final String phoneNumber) { - return getCallIntent(phoneNumber, false); - } - - /** - * 获取拨打电话意图 - * - * @param phoneNumber 电话号码 - * @param isNewTask 是否开启新的任务栈 - * @return 拨打电话意图 - */ - public static Intent getCallIntent(final String phoneNumber, final boolean isNewTask) { - Intent intent = new Intent("android.intent.action.CALL", Uri.parse("tel:" + phoneNumber)); - return getIntent(intent, isNewTask); - } - - /** - * 获取发送短信界面的意图 - * @param phoneNumber 接收号码 - * @param content 短信内容 - * @return 发送短信界面的意图 - */ - public static Intent getSendSmsIntent(final String phoneNumber, final String content) { - return getSendSmsIntent(phoneNumber, content, false); - } - - /** - * 获取跳至发送短信界面的意图 - * @param phoneNumber 接收号码 - * @param content 短信内容 - * @param isNewTask 是否开启新的任务栈 - * @return 发送短信界面的意图 - */ - public static Intent getSendSmsIntent(final String phoneNumber, final String content, final boolean isNewTask) { - Uri uri = Uri.parse("smsto:" + phoneNumber); - Intent intent = new Intent(Intent.ACTION_SENDTO, uri); - intent.putExtra("sms_body", content); - return getIntent(intent, isNewTask); - } - - /** - * 获取拍照的意图 - * @param outUri 输出的 uri - * @return 拍照的意图 - */ - public static Intent getCaptureIntent(final Uri outUri) { - return getCaptureIntent(outUri, false); - } - - /** - * 获取拍照的意图 - * @param outUri 输出的 uri - * @param isNewTask 是否开启新的任务栈 - * @return 拍照的意图 - */ - public static Intent getCaptureIntent(final Uri outUri, final boolean isNewTask) { - Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - return getIntent(intent, isNewTask); - } - - /** - * 跳转到系统设置页面 - * @param activity - */ - public static void startSysSetting(Activity activity) { -// if(android.os.Build.VERSION.SDK_INT > 10 ) { -// activity.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS)); -// }else { -// activity.startActivity(new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS)); -// } -// // 跳转到 无线和网络 设置页面(可以设置移动网络,sim卡1,2的移动网络) -// Intent intent = new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS); -// Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS); - - Intent intent = new Intent(Settings.ACTION_SETTINGS); - activity.startActivity(intent); - } - - /** - * 跳转到系统设置页面 - * @param activity - * @param requestCode 回传请求code - */ - public static void startSysSetting(Activity activity, int requestCode) { - Intent intent = new Intent(Settings.ACTION_SETTINGS); - activity.startActivityForResult(intent, requestCode); - } - - /** - * 打开网络设置界面 - 3.0以下打开设置界面 - */ - public static void openWirelessSettings(Activity activity) { - if (android.os.Build.VERSION.SDK_INT > 10) { - activity.startActivity(new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - } else { - activity.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - } - } - - // == - - private static Intent getIntent(final Intent intent, final boolean isNewTask) { - return isNewTask ? intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) : intent; - } - -// /** -// * 获取选择照片的 Intent -// * -// * @return -// */ -// public static Intent getPickIntentWithGallery() { -// Intent intent = new Intent(Intent.ACTION_PICK); -// return intent.setType("image*//*"); -// } -// -// /** -// * 获取从文件中选择照片的 Intent -// * -// * @return -// */ -// public static Intent getPickIntentWithDocuments() { -// Intent intent = new Intent(Intent.ACTION_GET_CONTENT); -// return intent.setType("image*//*"); -// } -// -// -// public static Intent buildImageGetIntent(final Uri saveTo, final int outputX, final int outputY, final boolean returnData) { -// return buildImageGetIntent(saveTo, 1, 1, outputX, outputY, returnData); -// } -// -// public static Intent buildImageGetIntent(Uri saveTo, int aspectX, int aspectY, -// int outputX, int outputY, boolean returnData) { -// Intent intent = new Intent(); -// if (Build.VERSION.SDK_INT < 19) { -// intent.setAction(Intent.ACTION_GET_CONTENT); -// } else { -// intent.setAction(Intent.ACTION_OPEN_DOCUMENT); -// intent.addCategory(Intent.CATEGORY_OPENABLE); -// } -// intent.setType("image*//*"); -// intent.putExtra("output", saveTo); -// intent.putExtra("aspectX", aspectX); -// intent.putExtra("aspectY", aspectY); -// intent.putExtra("outputX", outputX); -// intent.putExtra("outputY", outputY); -// intent.putExtra("scale", true); -// intent.putExtra("return-data", returnData); -// intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString()); -// return intent; -// } -// -// public static Intent buildImageCropIntent(final Uri uriFrom, final Uri uriTo, final int outputX, final int outputY, final boolean returnData) { -// return buildImageCropIntent(uriFrom, uriTo, 1, 1, outputX, outputY, returnData); -// } -// -// public static Intent buildImageCropIntent(Uri uriFrom, Uri uriTo, int aspectX, int aspectY, -// int outputX, int outputY, boolean returnData) { -// Intent intent = new Intent("com.android.camera.action.CROP"); -// intent.setDataAndType(uriFrom, "image*//*"); -// intent.putExtra("crop", "true"); -// intent.putExtra("output", uriTo); -// intent.putExtra("aspectX", aspectX); -// intent.putExtra("aspectY", aspectY); -// intent.putExtra("outputX", outputX); -// intent.putExtra("outputY", outputY); -// intent.putExtra("scale", true); -// intent.putExtra("return-data", returnData); -// intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString()); -// return intent; -// } -// -// public static Intent buildImageCaptureIntent(final Uri uri) { -// Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); -// intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); -// return intent; -// } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/KeyBoardUtils.java b/DevLibUtils/src/main/java/dev/utils/app/KeyBoardUtils.java deleted file mode 100644 index 64f032209c..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/KeyBoardUtils.java +++ /dev/null @@ -1,522 +0,0 @@ -package dev.utils.app; - -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.graphics.Rect; -import android.os.Handler; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; - -import java.lang.reflect.Field; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 软键盘相关辅助类 - * Created by Ttt - */ -public final class KeyBoardUtils { - - private KeyBoardUtils() { - } - - // 日志TAG - private static final String TAG = KeyBoardUtils.class.getSimpleName(); - - /** 默认延迟时间 */ - private static int DELAY_MILLIS = 300; - /** 键盘显示 */ - public static final int KEYBOARD_DISPLAY = 930; - /** 键盘隐藏 */ - public static final int KEYBOARD_HIDE = 931; - - - /** - * 避免输入法面板遮挡 manifest.xml 中 activity 中设置 - * android:windowSoftInputMode="adjustPan" - * android:windowSoftInputMode="adjustUnspecified|stateHidden" - */ - - // == ----------------------------------------- == - - /** - * 设置延迟时间 - * @param delayMillis - */ - public static void setDelayMillis(int delayMillis) { - DELAY_MILLIS = delayMillis; - } - - // ================ - // == 打开软键盘 == - // ================ - - /** - * 打开软键盘 - * @param mEditText 输入框 - */ - public static void openKeyboard(EditText mEditText) { - if (mEditText != null) { - try { - InputMethodManager imm = (InputMethodManager) mEditText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(mEditText, InputMethodManager.SHOW_FORCED); - imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "openKeyboard"); - } - } - } - - /** - * 打开软键盘 - * @param mEditText - * @param vHandler - */ - public static void openKeyboard(final EditText mEditText, Handler vHandler){ - openKeyboard(mEditText, vHandler, DELAY_MILLIS); - } - - /** - * 打开软键盘 - * @param mEditText - * @param vHandler - * @param delayMillis - */ - public static void openKeyboard(final EditText mEditText, Handler vHandler, int delayMillis){ - if (vHandler != null && mEditText != null){ - // 延迟打开 - vHandler.postDelayed(new Runnable() { - @Override - public void run() { - try { - mEditText.requestFocus(); - mEditText.setSelection(mEditText.getText().toString().length()); - } catch (Exception e){ - } - openKeyboard(mEditText); - } - }, delayMillis); - } - } - - // - - - /** - * 打开软键盘 - */ - public static void openKeyboard() { - try { - InputMethodManager imm = (InputMethodManager) DevUtils.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "openKeyboard"); - } - } - - /** - * 打开软键盘 - * @param vHandler - */ - public static void openKeyboard(Handler vHandler){ - openKeyboard(vHandler, DELAY_MILLIS); - } - - /** - * 打开软键盘 - * @param vHandler - * @param delayMillis - */ - public static void openKeyboard(Handler vHandler, int delayMillis){ - if (vHandler != null && DevUtils.getContext() != null){ - // 延迟打开 - vHandler.postDelayed(new Runnable() { - @Override - public void run() { - openKeyboard(); - } - }, delayMillis); - } - } - - // ================ - // == 关闭软键盘 == - // ================ - - /** - * 关闭软键盘 - * @param mEditText 输入框 - */ - public static void closeKeyboard(EditText mEditText) { - if (mEditText != null) { - try { - InputMethodManager imm = (InputMethodManager) mEditText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "closeKeyboard"); - } - } - } - - /** - * 关闭软键盘 - */ - public static void closeKeyboard() { - if (DevUtils.getContext() != null) { - try { - InputMethodManager imm = (InputMethodManager) DevUtils.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "closeKeyboard"); - } - } - } - - /** - * 关闭软键盘 - * @param mActivity 当前页面 - */ - public static void closeKeyboard(Activity mActivity) { - if (mActivity != null) { - try { - InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mActivity.getWindow().peekDecorView().getWindowToken(), 0); - //imm.hideSoftInputFromWindow(mActivity.getCurrentFocus().getWindowToken(), 0); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "closeKeyboard"); - } - } - } - - /** - * 关闭dialog中打开的键盘 - * @param mDialog - */ - public static void closeKeyboard(Dialog mDialog) { - if (mDialog != null) { - try { - InputMethodManager imm = (InputMethodManager) mDialog.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mDialog.getWindow().peekDecorView().getWindowToken(), 0); - //imm.hideSoftInputFromWindow(mDialog.getCurrentFocus().getWindowToken(), 0); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "closeKeyboard"); - } - } - } - - // == - - /** - * 关闭软键盘 - 特殊处理 - * @param mEditText - * @param mDialog - */ - public static void closeKeyBoardSpecial(EditText mEditText, Dialog mDialog){ - try { - // 关闭输入法 - closeKeyboard(); - // 关闭输入法 - closeKeyboard(mEditText); - // 关闭输入法 - closeKeyboard(mDialog); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "closeKeyBoardSpecial"); - } - } - - public static void closeKeyBoardSpecial(final EditText mEditText, final Dialog mDialog, Handler vHandler){ - closeKeyBoardSpecial(mEditText, mDialog, vHandler, DELAY_MILLIS); - } - - /** - * 关闭软键盘 - 特殊处理(两个都关闭) - * @param mEditText - * @param mDialog - * @param vHandler - * @param delayMillis - */ - public static void closeKeyBoardSpecial(final EditText mEditText, final Dialog mDialog, Handler vHandler, int delayMillis){ - if (vHandler != null){ - // 延迟打开 - vHandler.postDelayed(new Runnable() { - @Override - public void run() { - closeKeyBoardSpecial(mEditText, mDialog); - } - }, delayMillis); - } - } - - // - - - public static void closeKeyboard(final EditText mEditText, Handler vHandler){ - closeKeyboard(mEditText, vHandler, DELAY_MILLIS); - } - - /** - * 关闭软键盘 - * @param mEditText - * @param vHandler - * @param delayMillis - */ - public static void closeKeyboard(final EditText mEditText, Handler vHandler, int delayMillis){ - if (vHandler != null && mEditText != null){ - // 延迟打开 - vHandler.postDelayed(new Runnable() { - @Override - public void run() { - closeKeyboard(mEditText); - } - }, delayMillis); - } - } - - public static void closeKeyboard(Handler vHandler){ - closeKeyboard(vHandler, DELAY_MILLIS); - } - - /** - * 关闭软键盘 - * @param vHandler - * @param delayMillis - */ - public static void closeKeyboard(Handler vHandler, int delayMillis){ - if (vHandler != null && DevUtils.getContext() != null){ - // 延迟打开 - vHandler.postDelayed(new Runnable() { - @Override - public void run() { - closeKeyboard(); - } - }, delayMillis); - } - } - - public static void closeKeyboard(final Activity mActivity, Handler vHandler){ - closeKeyboard(mActivity, vHandler, DELAY_MILLIS); - } - - /** - * 关闭软键盘 - * @param mActivity - * @param vHandler - * @param delayMillis - */ - public static void closeKeyboard(final Activity mActivity, Handler vHandler, int delayMillis){ - if (vHandler != null && mActivity != null){ - // 延迟打开 - vHandler.postDelayed(new Runnable() { - @Override - public void run() { - closeKeyboard(mActivity); - } - }, delayMillis); - } - } - - public static void closeKeyboard(final Dialog mDialog, Handler vHandler){ - closeKeyboard(mDialog, vHandler, DELAY_MILLIS); - } - - /** - * 关闭软键盘 - * @param mDialog - * @param vHandler - * @param delayMillis - */ - public static void closeKeyboard(final Dialog mDialog, Handler vHandler, int delayMillis){ - if (vHandler != null && mDialog != null){ - // 延迟打开 - vHandler.postDelayed(new Runnable() { - @Override - public void run() { - closeKeyboard(mDialog); - } - }, delayMillis); - } - } - - // == ----------------------------------------- == - - // 下面暂时无法使用,缺少判断键盘是否显示,否则和自动切换无区别 - // InputMethodManager.isActive() (无法获取) - // Activity.getWindow().getAttributes().softInputMode (有些版本可以,不适用) - // ==----== - - /** - * 自动切换键盘状态,如果键盘显示了则隐藏,隐藏着显示 - */ - public static void toggleKeyboard() { - // 程序启动后,自动弹出软键盘,可以通过设置一个时间函数来实现,不能再onCreate里写 - try { - InputMethodManager imm = (InputMethodManager) DevUtils.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "toggleKeyboard"); - } - } - - // ========== 点击非EditText 则隐藏输入法 =============== - - /** - * 某个View里面的子View的View判断 - * @param view - */ - public static void judgeView(View view, final Activity activity) { - if (!(view instanceof EditText)) { - view.setOnTouchListener(new View.OnTouchListener() { - public boolean onTouch(View v, MotionEvent event) { - closeKeyboard(activity); - return false; - } - }); - } - // -- - if (view instanceof ViewGroup) { - for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { - View innerView = ((ViewGroup) view).getChildAt(i); - judgeView(innerView, activity); - } - } - } - - // ========== 输入法隐藏显示 =============== - - /** - * 判断软键盘是否可见 - * @param activity - * @return true : 可见, false : 不可见 - */ - public static boolean isSoftInputVisible(final Activity activity) { - return isSoftInputVisible(activity, 200); - } - - /** - * 判断软键盘是否可见 - * @param activity - * @param minHeightOfSoftInput 软键盘最小高度 - * @return true : 可见, false : 不可见 - */ - public static boolean isSoftInputVisible(final Activity activity, final int minHeightOfSoftInput) { - return getContentViewInvisibleHeight(activity) >= minHeightOfSoftInput; - } - - /** - * 计算View的宽度高度 - * @param activity - * @return - */ - private static int getContentViewInvisibleHeight(final Activity activity) { - try { - final View contentView = activity.findViewById(android.R.id.content); - Rect rect = new Rect(); - contentView.getWindowVisibleDisplayFrame(rect); - return contentView.getRootView().getHeight() - rect.height(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getContentViewInvisibleHeight"); - return 0; - } - } - - /** - * 注册软键盘改变监听器 - * @param activity - * @param listener listener - */ - public static void registerSoftInputChangedListener(final Activity activity, final OnSoftInputChangedListener listener) { - try { - // 获取根View - final View contentView = activity.findViewById(android.R.id.content); - // 添加事件 - contentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (listener != null) { - // 获取高度 - int height = getContentViewInvisibleHeight(activity); - // 判断是否相同 - listener.onSoftInputChanged(height >= 200, height); - } - } - }); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "registerSoftInputChangedListener"); - } - } - - /** - * 注册软键盘改变监听器 - * @param activity - * @param listener listener - */ - public static void registerSoftInputChangedListener2(final Activity activity, final OnSoftInputChangedListener listener) { - final View decorView = activity.getWindow().getDecorView(); - decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (listener != null){ - try { - Rect rect = new Rect(); - decorView.getWindowVisibleDisplayFrame(rect); - // 计算出可见屏幕的高度 - int displayHight = rect.bottom - rect.top; - // 获得屏幕整体的高度 - int hight = decorView.getHeight(); - // 获得键盘高度 - int keyboardHeight = hight - displayHight; - // 计算一定比例 - boolean visible = (double) displayHight / hight < 0.8; - // 判断是否显示 - listener.onSoftInputChanged(visible, keyboardHeight); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "registerSoftInputChangedListener2"); - } - } - } - }); - } - - /** 输入法弹出、隐藏改变事件 */ - public interface OnSoftInputChangedListener { - - void onSoftInputChanged(boolean visible, int height); - } - - // == - - /** - * 修复软键盘内存泄漏 在 Activity.onDestroy() 中使用 - * @param context - */ - public static void fixSoftInputLeaks(final Context context) { - if (context == null) return; - try { - InputMethodManager imm = (InputMethodManager) DevUtils.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - String[] strArr = new String[]{"mCurRootView", "mServedView", "mNextServedView"}; - for (int i = 0; i < 3; i++) { - try { - Field declaredField = imm.getClass().getDeclaredField(strArr[i]); - if (declaredField == null) continue; - if (!declaredField.isAccessible()) { - declaredField.setAccessible(true); - } - Object obj = declaredField.get(imm); - if (obj == null || !(obj instanceof View)) continue; - View view = (View) obj; - if (view.getContext() == context) { - declaredField.set(imm, null); - } else { - return; - } - } catch (Throwable th) { - } - } - } catch (Exception e){ - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/KeyguardUtils.java b/DevLibUtils/src/main/java/dev/utils/app/KeyguardUtils.java deleted file mode 100644 index 4caab7a7e0..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/KeyguardUtils.java +++ /dev/null @@ -1,120 +0,0 @@ -package dev.utils.app; - -import android.app.KeyguardManager; -import android.content.Context; -import android.os.Build; -import android.support.annotation.RequiresApi; - -import dev.DevUtils; - -/** - * detail: 锁屏工具类 - 锁屏管理, 锁屏、禁用锁屏,判断是否锁屏 - * Created by Ttt - * - */ -@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) -public final class KeyguardUtils { - - // KeyguardUtils 实例 - private static KeyguardUtils INSTANCE; - - /** 获取 KeyguardUtils 实例 ,单例模式 */ - public static KeyguardUtils getInstance() { - if (INSTANCE == null){ - INSTANCE = new KeyguardUtils(); - } - return INSTANCE; - } - - // 锁屏管理类 - KeyguardManager keyguardManager; - // android-26 开始过时 - KeyguardManager.KeyguardLock keyguardLock; - - /** 构造函数 */ - private KeyguardUtils() { - // 获取系统服务 - keyguardManager = (KeyguardManager) DevUtils.getContext().getSystemService(Context.KEYGUARD_SERVICE); - // 初始化锁 - keyguardLock = keyguardManager.newKeyguardLock("KeyguardUtils"); - } - - /** - * 是否锁屏 - android 4.1以上支持 - * @return - */ - public boolean isKeyguardLocked() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - return false; - } else { - return keyguardManager.isKeyguardLocked(); - } - } - - /** - * 是否有锁屏密码 - android 4.1以上支持 - * @return - */ - public boolean isKeyguardSecure() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - return false; - } else { - return keyguardManager.isKeyguardSecure(); - } - } - - /** - * 是否锁屏 - android 4.1 之前 - * @return - */ - public boolean inKeyguardRestrictedInputMode() { - return keyguardManager.inKeyguardRestrictedInputMode(); - } - - public KeyguardManager getKeyguardManager() { - return keyguardManager; - } - - public void setKeyguardManager(KeyguardManager keyguardManager) { - this.keyguardManager = keyguardManager; - } - - // == - - /** - * 屏蔽系统的屏保 - * 利用 disableKeyguard 解锁, 解锁并不是真正的解锁, 只是把锁屏的界面隐藏掉而已 - */ - public void disableKeyguard() { - keyguardLock.disableKeyguard(); - } - - /** - * 使能显示锁屏界面,如果你之前调用了disableKeyguard()方法取消锁屏界面,那么会马上显示锁屏界面。 - */ - public void reenableKeyguard() { - keyguardLock.reenableKeyguard(); - } - - /** - * 释放资源 - */ - public void release() { - if (keyguardLock != null) { - // 禁用显示键盘锁定 - keyguardLock.reenableKeyguard(); - } - } - - public void newKeyguardLock (String tag){ - keyguardLock = keyguardManager.newKeyguardLock(tag); - } - - public KeyguardManager.KeyguardLock getKeyguardLock() { - return keyguardLock; - } - - public void setKeyguardLock(KeyguardManager.KeyguardLock keyguardLock) { - this.keyguardLock = keyguardLock; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/ListenerUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ListenerUtils.java deleted file mode 100644 index 18c514f20a..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ListenerUtils.java +++ /dev/null @@ -1,152 +0,0 @@ -package dev.utils.app; - -import android.app.Activity; -import android.view.View; -import android.view.Window; - -import java.lang.reflect.Field; - -import dev.utils.LogPrintUtils; - -/** - * detail: 事件工具类 => AppReflectUtils(可以删除) - * Created by Ttt - */ -public final class ListenerUtils { - - private ListenerUtils() { - } - - // 日志TAG - private static final String TAG = ListenerUtils.class.getSimpleName(); - - /** - * 获取 View 设置的 OnTouchListener - * @param view - * @return - */ - public static View.OnTouchListener getTouchListener(View view){ - return (View.OnTouchListener) getListenerInfoListener(view, "mOnTouchListener"); - } - - /** - * 获取 View ListenerInfo 对象(内部类) - * @param view - * @return - */ - public static Object getListenerInfo(View view){ - try { - // 获取 ListenerInfo 对象 - Field infoField = View.class.getDeclaredField("mListenerInfo"); - infoField.setAccessible(true); - return infoField.get(view); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getListenerInfo"); - } - return null; - } - - /** - * 获取 View ListenerInfo 对象内部事件对象 - * @param view - * @param listener - * @return - */ - public static Object getListenerInfoListener(View view, String listener){ - try { - // 获取 ListenerInfo 对象 - Object listenerInfo = getListenerInfo(view); - // 获取 ListenerInfo 对象中的 mOnTouchListener 属性 - Class infoClazz = Class.forName("android.view.View$ListenerInfo"); - Field listenerField = infoClazz.getDeclaredField(listener); - listenerField.setAccessible(true); - // 进行获取返回 - return listenerField.get(listenerInfo); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getListenerInfoListener"); - } - return null; - } - - // = - - /** - * 设置点击事件 - * @param view - * @param onClickListener - * @param viewIds - */ - public static void setOnClicks(final View view, final View.OnClickListener onClickListener, final int... viewIds){ - if (view != null && onClickListener != null && viewIds != null){ - for (int i = 0, len = viewIds.length; i < len; i++){ - View findView = findViewById(view, viewIds[i]); - findView.setOnClickListener(onClickListener); - } - } - } - - /** - * 设置点击事件 - * @param activity - * @param onClickListener - * @param viewIds - */ - public static void setOnClicks(final Activity activity, final View.OnClickListener onClickListener, final int... viewIds){ - if (activity != null && onClickListener != null && viewIds != null){ - for (int i = 0, len = viewIds.length; i < len; i++){ - View findView = findViewById(activity, viewIds[i]); - findView.setOnClickListener(onClickListener); - } - } - } - - /** - * 设置点击事件 - * @param onClickListener - * @param views - */ - public static void setOnClicks(final View.OnClickListener onClickListener, final View... views){ - if (onClickListener != null && views != null){ - for (int i = 0, len = views.length; i < len; i++){ - if (views[i] != null) { - views[i].setOnClickListener(onClickListener); - } - } - } - } - - // == 初始化View操作等 == - - /** - * 初始化View - * @param view - * @param id - * @param - * @return - */ - private static T findViewById(View view, int id) { - return view.findViewById(id); - } - - /** - * 初始化View - * @param window - * @param id - * @param - * @return - */ - private static T findViewById(Window window, int id){ - return window.findViewById(id); - } - - /** - * 初始化View - * @param activity - * @param id - * @param - * @return - */ - private static T findViewById(Activity activity, int id) { - return activity.findViewById(id); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/LocationUtils.java b/DevLibUtils/src/main/java/dev/utils/app/LocationUtils.java deleted file mode 100644 index b7e869e7fa..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/LocationUtils.java +++ /dev/null @@ -1,363 +0,0 @@ -package dev.utils.app; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.location.Address; -import android.location.Criteria; -import android.location.Geocoder; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.location.LocationProvider; -import android.os.Bundle; -import android.provider.Settings; - -import java.io.IOException; -import java.util.List; -import java.util.Locale; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -import static android.content.Context.LOCATION_SERVICE; - -/** - * detail: 定位相关工具类 - * Created by Ttt - */ -public final class LocationUtils { - - private LocationUtils() { - } - - // 日志TAG - private static final String TAG = LocationUtils.class.getSimpleName(); - - /** 时间常量 = 2分钟 */ - private static final int MINUTES_TWO = 1000 * 60 * 2; - /** 定位改变通知事件 */ - private static OnLocationChangeListener mListener; - /** 自定义定位事件 */ - private static CustomLocationListener myLocationListener; - /** 定位管理对象 */ - private static LocationManager mLocationManager; - - /** - * 获取位置, 需要先判断是否开启了定位 - * - * @param context - * @param listener - * @param time 间隔时间 - * @param distance 间隔距离 - * @return {@code Location} - */ - @SuppressLint("MissingPermission") - public static Location getLocation(Context context, LocationListener listener, long time, float distance) { - Location location = null; - try { - mLocationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE); - if (isLocationEnabled()) { - mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, time, distance, listener); - if (mLocationManager != null) { - location = mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); - if (location != null) { - mLocationManager.removeUpdates(listener); - return location; - } - } - } - //when GPS is enabled. - if (isGpsEnabled()) { - if (location == null) { - mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, time, distance, listener); - if (mLocationManager != null) { - location = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); - if (location != null) { - mLocationManager.removeUpdates(listener); - return location; - } - } - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e,"getLocation"); - } - return location; - } - - /** - * 判断Gps是否可用 - * @return true : 是, false : 否 - */ - public static boolean isGpsEnabled() { - LocationManager lm = (LocationManager) DevUtils.getContext().getSystemService(LOCATION_SERVICE); - return lm != null && lm.isProviderEnabled(LocationManager.GPS_PROVIDER); - } - - /** - * 判断定位是否可用 - * @return true : 是, false : 否 - */ - public static boolean isLocationEnabled() { - LocationManager lm = (LocationManager) DevUtils.getContext().getSystemService(LOCATION_SERVICE); - return lm != null && (lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER) || lm.isProviderEnabled(LocationManager.GPS_PROVIDER)); - } - - /** - * 打开Gps设置界面 - */ - public static void openGpsSettings() { - Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); - DevUtils.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - } - - /** - * 注册 - 使用完记得调用{@link #unregister()} - * 需添加权限 {@code } - * 需添加权限 {@code } - * 需添加权限 {@code } - * 如果{@code minDistance}为0,则通过{@code minTime}来定时更新; - * {@code minDistance}不为0,则以{@code minDistance}为准; - * 两者都为0,则随时刷新。 - * @param minTime 位置信息更新周期(单位:毫秒) - * @param minDistance 位置变化最小距离:当位置距离变化超过此值时,将更新位置信息(单位:米) - * @param listener 位置刷新的回调接口 - * @return true : 初始化成功, false : 初始化失败 - */ - @SuppressLint("MissingPermission") - public static boolean register(long minTime, long minDistance, OnLocationChangeListener listener) { - if (listener == null) return false; - mLocationManager = (LocationManager) DevUtils.getContext().getSystemService(LOCATION_SERVICE); - if (mLocationManager == null || (!mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) - && !mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))) { - return false; - } - mListener = listener; - String provider = mLocationManager.getBestProvider(getCriteria(), true); - Location location = mLocationManager.getLastKnownLocation(provider); - if (location != null) listener.getLastKnownLocation(location); - if (myLocationListener == null) myLocationListener = new CustomLocationListener(); - mLocationManager.requestLocationUpdates(provider, minTime, minDistance, myLocationListener); - return true; - } - - /** - * 注销 - */ - @SuppressLint("MissingPermission") - public static void unregister() { - if (mLocationManager != null) { - if (myLocationListener != null) { - mLocationManager.removeUpdates(myLocationListener); - myLocationListener = null; - } - mLocationManager = null; - } - if (mListener != null) { - mListener = null; - } - } - - /** - * 设置定位参数 - * @return {@link Criteria} - */ - private static Criteria getCriteria() { - Criteria criteria = new Criteria(); - // 设置定位精确度 Criteria.ACCURACY_COARSE比较粗略,Criteria.ACCURACY_FINE则比较精细 - criteria.setAccuracy(Criteria.ACCURACY_FINE); - // 设置是否要求速度 - criteria.setSpeedRequired(false); - // 设置是否允许运营商收费 - criteria.setCostAllowed(false); - // 设置是否需要方位信息 - criteria.setBearingRequired(false); - // 设置是否需要海拔信息 - criteria.setAltitudeRequired(false); - // 设置对电源的需求 - criteria.setPowerRequirement(Criteria.POWER_LOW); - return criteria; - } - - /** - * 根据经纬度获取地理位置 - * @param latitude 纬度 - * @param longitude 经度 - * @return {@link Address} - */ - public static Address getAddress(double latitude, double longitude) { - Geocoder geocoder = new Geocoder(DevUtils.getContext(), Locale.getDefault()); - try { - List
addresses = geocoder.getFromLocation(latitude, longitude, 1); - if (addresses.size() > 0) return addresses.get(0); - } catch (IOException e) { - LogPrintUtils.eTag(TAG, e,"getAddress"); - } - return null; - } - - /** - * 根据经纬度获取所在国家 - * @param latitude 纬度 - * @param longitude 经度 - * @return 所在国家 - */ - public static String getCountryName(double latitude, double longitude) { - Address address = getAddress(latitude, longitude); - return address == null ? "unknown" : address.getCountryName(); - } - - /** - * 根据经纬度获取所在地 - * @param latitude 纬度 - * @param longitude 经度 - * @return 所在地 - */ - public static String getLocality(double latitude, double longitude) { - Address address = getAddress(latitude, longitude); - return address == null ? "unknown" : address.getLocality(); - } - - /** - * 根据经纬度获取所在街道 - * @param latitude 纬度 - * @param longitude 经度 - * @return 所在街道 - */ - public static String getStreet(double latitude, double longitude) { - Address address = getAddress(latitude, longitude); - return address == null ? "unknown" : address.getAddressLine(0); - } - - /** - * 是否更好的位置 - * @param newLocation The new Location that you want to evaluate - * @param currentBestLocation The current Location fix, to which you want to compare the new one - * @return true : 是, false : 否 - */ - public static boolean isBetterLocation(Location newLocation, Location currentBestLocation) { - if (currentBestLocation == null) { - // A new location is always better than no location - return true; - } - // 检查位置信息的时间间隔 - long timeDelta = newLocation.getTime() - currentBestLocation.getTime(); - boolean isSignificantlyNewer = timeDelta > MINUTES_TWO; - boolean isSignificantlyOlder = timeDelta < -MINUTES_TWO; - boolean isNewer = timeDelta > 0; - - // 如果时间超过2分钟, 则使用新的位置 - if (isSignificantlyNewer) { - return true; - } else if (isSignificantlyOlder) { // 时间超过两分钟 - return false; - } - - // 检查新的位置时间 - int accuracyDelta = (int) (newLocation.getAccuracy() - currentBestLocation.getAccuracy()); - boolean isLessAccurate = accuracyDelta > 0; - boolean isMoreAccurate = accuracyDelta < 0; - boolean isSignificantlyLessAccurate = accuracyDelta > 200; - - // 检查旧位置和新位置是否来自同一提供者。 - boolean isFromSameProvider = isSameProvider(newLocation.getProvider(), currentBestLocation.getProvider()); - - // 判断最新的位置 - if (isMoreAccurate) { - return true; - } else if (isNewer && !isLessAccurate) { - return true; - } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) { - return true; - } - return false; - } - - /** - * 是否相同的提供者 - * @param provider0 提供者1 - * @param provider1 提供者2 - * @return true : 是, false : 否 - */ - public static boolean isSameProvider(String provider0, String provider1) { - if (provider0 == null) { - return provider1 == null; - } - return provider0.equals(provider1); - } - - /** - * 自定义定位监听事件 - */ - private static class CustomLocationListener implements LocationListener { - /** - * 当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发 - * @param location 坐标 - */ - @Override - public void onLocationChanged(Location location) { - if (mListener != null) { - mListener.onLocationChanged(location); - } - } - - /** - * provider的在可用、暂时不可用和无服务三个状态直接切换时触发此函数 - * @param provider 提供者 - * @param status 状态 - * @param extras provider可选包 - */ - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - if (mListener != null) { - mListener.onStatusChanged(provider, status, extras); - } - switch (status) { - case LocationProvider.AVAILABLE: - LogPrintUtils.dTag(TAG, "当前GPS状态为可见状态"); - break; - case LocationProvider.OUT_OF_SERVICE: - LogPrintUtils.dTag(TAG, "当前GPS状态为服务区外状态"); - break; - case LocationProvider.TEMPORARILY_UNAVAILABLE: - LogPrintUtils.dTag(TAG, "当前GPS状态为暂停服务状态"); - break; - } - } - - /** provider被enable时触发此函数,比如GPS被打开 */ - @Override - public void onProviderEnabled(String provider) { - } - - /** provider被disable时触发此函数,比如GPS被关闭 */ - @Override - public void onProviderDisabled(String provider) { - } - } - - /** 定位改变事件 */ - public interface OnLocationChangeListener { - - /** - * 获取最后一次保留的坐标 - * @param location 坐标 - */ - void getLastKnownLocation(Location location); - - /** - * 当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发 - * @param location 坐标 - */ - void onLocationChanged(Location location); - - /** - * provider的在可用、暂时不可用和无服务三个状态直接切换时触发此函数 - * @param provider 提供者 - * @param status 状态 - * @param extras provider可选包 - */ - void onStatusChanged(String provider, int status, Bundle extras);//位置状态发生改变 - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/ManifestUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ManifestUtils.java deleted file mode 100644 index 58b5e495aa..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ManifestUtils.java +++ /dev/null @@ -1,147 +0,0 @@ -package dev.utils.app; - -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: Android Manifest工具类 - * Created by Ttt - */ -public final class ManifestUtils { - - private ManifestUtils(){ - } - - // 日志TAG - private static final String TAG = ManifestUtils.class.getSimpleName(); - - /** - * 获取 Manifest Meta Data - * @param metaKey - * @return - */ - public static String getMetaData(String metaKey) { - try { - ApplicationInfo appInfo = DevUtils.getContext().getPackageManager().getApplicationInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_META_DATA); - String data = appInfo.metaData.getString(metaKey); - return data; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getMetaData"); - } - return null; - } - - // == - - /** - * 获取app版本信息 - * @return 0 = versionName , 1 = versionCode - */ - public static String[] getAppVersion() { - try { - PackageManager pm = DevUtils.getContext().getPackageManager(); - PackageInfo pi = pm.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_ACTIVITIES); - if (pi != null) { - String versionName = pi.versionName == null ? "null" : pi.versionName; - String versionCode = pi.versionCode + ""; - - return new String[]{versionName,versionCode}; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppVersion"); - } - return null; - } - - /** - * 获取app版本号 - * @return 当前版本Code - */ - public static int getAppVersionCode() { - try { - PackageManager pm = DevUtils.getContext().getPackageManager(); - PackageInfo pi = pm.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_ACTIVITIES); - if (pi != null) { - return pi.versionCode; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppVersionCode"); - } - return -1; - } - - /** - * 获取app版本信息 - * @return 当前版本信息 - */ - public static String getAppVersionName() { - try { - PackageManager pm = DevUtils.getContext().getPackageManager(); - PackageInfo pi = pm.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_ACTIVITIES); - if (pi != null) { - return pi.versionName; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppVersionName"); - } - return null; - } - - // = - - /** - * 获取app版本号 - 内部判断 - * @param packageName - * @return - */ - public static int getAppVersionCode(final String packageName) { - if (isSpace(packageName)) return -1; - try { - PackageManager pm = DevUtils.getContext().getPackageManager(); - PackageInfo pi = pm.getPackageInfo(packageName, 0); - return pi == null ? -1 : pi.versionCode; - } catch (PackageManager.NameNotFoundException e) { - LogPrintUtils.eTag(TAG, e, "getAppVersionCode"); - return -1; - } - } - - - /** - * 获取app版本名 - 对外显示 - * @param packageName - * @return - */ - public static String getAppVersionName(final String packageName) { - if (isSpace(packageName)) return null; - try { - PackageManager pm = DevUtils.getContext().getPackageManager(); - PackageInfo pi = pm.getPackageInfo(packageName, 0); - return pi == null ? null : pi.versionName; - } catch (PackageManager.NameNotFoundException e) { - LogPrintUtils.eTag(TAG, e, "getAppVersionName"); - return null; - } - } - - // = - - /** - * 判断字符串是否为 null 或全为空白字符 - * @param str 待校验字符串 - * @return - */ - private static boolean isSpace(final String str) { - if (str == null) return true; - for (int i = 0, len = str.length(); i < len; ++i) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/MemoryUtils.java b/DevLibUtils/src/main/java/dev/utils/app/MemoryUtils.java deleted file mode 100644 index cd40be9c65..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/MemoryUtils.java +++ /dev/null @@ -1,179 +0,0 @@ -package dev.utils.app; - -import android.annotation.TargetApi; -import android.app.ActivityManager; -import android.content.Context; -import android.os.Build; -import android.text.format.Formatter; - -import java.io.BufferedReader; -import java.io.FileReader; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 获取内存信息 - * Created by Ttt - */ -public final class MemoryUtils { - - private MemoryUtils(){ - } - - // 日志TAG - private static final String TAG = MemoryUtils.class.getSimpleName(); - - /** - * Print memory info. such as: - * MemTotal: 1864292 kB - * MemFree: 779064 kB - * Buffers: 4540 kB - * Cached: 185656 kB - * SwapCached: 13160 kB - * Active: 435588 kB - * Inactive: 269312 kB - * Active(anon): 386188 kB - * Inactive(anon): 132576 kB - * Active(file): 49400 kB - * Inactive(file): 136736 kB - * Unevictable: 2420 kB - * Mlocked: 0 kB - * HighTotal: 1437692 kB - * HighFree: 520212 kB - * LowTotal: 426600 kB - * LowFree: 258852 kB - * SwapTotal: 511996 kB - * SwapFree: 171876 kB - * Dirty: 412 kB - * Writeback: 0 kB - * AnonPages: 511924 kB - * Mapped: 152368 kB - * Shmem: 1636 kB - * Slab: 109224 kB - * SReclaimable: 75932 kB - * SUnreclaim: 33292 kB - * KernelStack: 13056 kB - * PageTables: 28032 kB - * NFS_Unstable: 0 kB - * Bounce: 0 kB - * WritebackTmp: 0 kB - * CommitLimit: 1444140 kB - * Committed_AS: 25977748 kB - * VmallocTotal: 458752 kB - * VmallocUsed: 123448 kB - * VmallocChunk: 205828 kB - */ - public static String printMemInfo() { - try { - FileReader fileReader = new FileReader(MEM_INFO_PATH); - BufferedReader bufferedReader = new BufferedReader(fileReader, 4 * 1024); - StringBuffer stringBuffer = new StringBuffer(); - String str; - while ((str = bufferedReader.readLine()) != null) { - // 追加保存内容 - stringBuffer.append(str); - } - bufferedReader.close(); - return stringBuffer.toString(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "printMemInfo"); - } - return ""; - } - - /** - * 获取内存信息 - * @return - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static ActivityManager.MemoryInfo getMemoryInfo() { - ActivityManager am = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); - ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); - am.getMemoryInfo(mi); - return mi; - } - - /** - * Print Memory info. - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static ActivityManager.MemoryInfo printMemoryInfo(Context context) { - ActivityManager.MemoryInfo mi = getMemoryInfo(); - StringBuilder sBuilder = new StringBuilder(); - sBuilder.append("_______ Memory : "); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - sBuilder.append("\ntotalMem :").append(mi.totalMem); - } - sBuilder.append("\navailMem :").append(mi.availMem); - sBuilder.append("\nlowMemory :").append(mi.lowMemory); - sBuilder.append("\nthreshold :").append(mi.threshold); - return mi; - } - - /** - * Get available memory info. - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static String getAvailMemory() {// 获取android 当前可用内存大小 - ActivityManager am = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); - ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); - am.getMemoryInfo(mi); // mi.availMem; 当前系统的可用内存 - // 将获取的内存大小规格化 - return Formatter.formatFileSize(DevUtils.getContext(), mi.availMem); - } - - - // 内存信息文件地址 - private static final String MEM_INFO_PATH = "/proc/meminfo"; - // 获取内存总大小 - public static final String MEMTOTAL = "MemTotal"; - // 获取可用内存 - public static final String MEMAVAILABLE = "MemAvailable"; - - - /** - * 获取总内存大小 - * @return - */ - public static String getTotalMemory() { - return getMemInfoIype(MEMTOTAL); - } - - /** - * 获取可用内存大小 - * @return - */ - public static String getMemoryAvailable() { - return getMemInfoIype(MEMAVAILABLE); - } - - /** - * 获取 type info - * @param type - * @return - */ - public static String getMemInfoIype(String type) { - try { - FileReader fileReader = new FileReader(MEM_INFO_PATH); - BufferedReader bufferedReader = new BufferedReader(fileReader, 4 * 1024); - String str = null; - while ((str = bufferedReader.readLine()) != null) { - if (str.contains(type)) { - break; - } - } - bufferedReader.close(); - /* \\s表示 空格,回车,换行等空白符, - +号表示一个或多个的意思 */ - String[] array = str.split("\\s+"); - // 获得系统总内存,单位是KB,乘以1024转换为Byte - long length = Long.valueOf(array[1]).longValue() * 1024; - return android.text.format.Formatter.formatFileSize(DevUtils.getContext(), length); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getMemInfoIype"); - } - return "unknown"; - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/NetWorkUtils.java b/DevLibUtils/src/main/java/dev/utils/app/NetWorkUtils.java deleted file mode 100644 index ffc4636fa2..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/NetWorkUtils.java +++ /dev/null @@ -1,398 +0,0 @@ -package dev.utils.app; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.NetworkInfo.State; -import android.net.wifi.WifiManager; -import android.os.Build; -import android.support.annotation.RequiresPermission; -import android.telephony.TelephonyManager; - -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.Enumeration; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 网络管理工具类 - * Created by Ttt - */ -public final class NetWorkUtils { - - private NetWorkUtils() { - } - - // 日志TAG - private static final String TAG = NetWorkUtils.class.getSimpleName(); - - /** - * 获取移动网络打开状态(默认属于未打开) - * @return - */ - public static boolean getMobileDataEnabled() { - try { - // 移动网络开关状态 - boolean mState; - // 属于5.0以下的使用 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - // 获取网络连接状态 - ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); - // 反射获取方法 - Method method = cManager.getClass().getMethod("getMobileDataEnabled"); - // 调用方法,获取状态 - mState = (Boolean) method.invoke(cManager); - } else { - TelephonyManager tm = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - // 反射获取方法 - Method method = tm.getClass().getDeclaredMethod("getDataEnabled"); - // 调用方法,获取状态 - mState = (Boolean) method.invoke(tm); - } - // 返回移动网络开关状态 - return mState; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getMobileDataEnabled"); - } - return false; - } - - /** - * 设置移动网络开关(无判断是否已开启移动网络) - 实际无效果, 非系统应用无权限 - * @param isOpen 是否打开移动网络 - * @return 是否执行正常 - * hint: - * 需系统应用、添加权限 - * - */ - public static boolean setMobileDataEnabled(boolean isOpen) { - try { - // 属于5.0以下的使用 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - // 获取网络连接状态 - ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); - // 通过反射设置移动网络 - Method mMethod = ConnectivityManager.class.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE); - // 设置移动网络 - mMethod.invoke(cManager, isOpen); - } else { // 需要 android.Manifest.permission.MODIFY_PHONE_STATE 权限, 普通APP无法获取 - TelephonyManager tm = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - // 通过反射设置移动网络 - Method mMethod = tm.getClass().getDeclaredMethod("setDataEnabled", boolean.class); - // 设置移动网络 - mMethod.invoke(tm, isOpen); - } - } catch (Exception e) { // 开启移动网络失败 - LogPrintUtils.eTag(TAG, e, "setMobileDataEnabled"); - return false; - } - return true; - } - - /** - * 判断是否连接了网络 - */ - @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public static boolean isConnect() { - // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) - try { - ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); - if (cManager != null) { - // 获取网络连接管理的对象 - NetworkInfo nInfo = cManager.getActiveNetworkInfo(); - if (nInfo != null && nInfo.isConnected()) { - // 判断当前网络是否已经连接 - if (nInfo.getState() == State.CONNECTED) { - return true; - } - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "isConnect"); - } - return false; - } - - /** - * 获取连接的网络类型 - * @return -1 = 等于未知 , 1 = Wifi, 2 = 移动网络 - */ - @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public static int getConnectType() { - // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) - try { - // 获取网络连接状态 - ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); - // 判断连接的是否wifi - State wifi = cManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState(); - // 判断是否连接上 - if (wifi == State.CONNECTED || wifi == State.CONNECTING) { - return 1; - } else { - // 判断连接的是否移动网络 - State mobile = cManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState(); - // 判断移动网络是否连接上 - if (mobile == State.CONNECTED || mobile == State.CONNECTING) { - return 2; - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getConnectType"); - } - return -1; - } - - /** - * 判断是否连接Wifi(连接上、连接中) - * @return - */ - @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public static boolean isConnWifi() { - return (getConnectType() == 1); - } - - /** - * 判断是否连接移动网络(连接上、连接中) - * @return - */ - @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public static boolean isConnMobileData() { - return (getConnectType() == 2); - } - - // === - - /** 网络连接类型 */ - public enum NetworkType { - NETWORK_WIFI, - NETWORK_4G, - NETWORK_3G, - NETWORK_2G, - NETWORK_UNKNOWN, - NETWORK_NO - } - - /** - * 判断网络是否可用 - * @return true: 可用 false: 不可用 - */ - @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public static boolean isAvailable() { - NetworkInfo info = getActiveNetworkInfo(); - return info != null && info.isAvailable(); - } - - /** - * 获取活动网络信息 - * @return NetworkInfo - */ - @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - private static NetworkInfo getActiveNetworkInfo() { - if (DevUtils.getContext() != null){ - try { - return ((ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getActiveNetworkInfo"); - } - } - return null; - } - - // == - - /** - * 判断是否4G网络 - * @return - */ - @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public static boolean is4G(){ - return getNetworkType() == NetworkType.NETWORK_4G; - } - - /** - * 判断wifi是否打开 - * @return - */ - public static boolean getWifiEnabled() { - try { - @SuppressLint("WifiManagerLeak") - WifiManager wifiManager = (WifiManager) DevUtils.getContext().getSystemService(Context.WIFI_SERVICE); - return wifiManager.isWifiEnabled(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getWifiEnabled"); - } - return false; - } - - /** - * 判断wifi数据是否可用 - * @return - */ - @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public static boolean isWifiAvailable() { - return getWifiEnabled() && isAvailable(); - } - - /** - * 获取网络运营商名称 - 中国移动、如中国联通、中国电信 - * @return 运营商名称 - */ - public static String getNetworkOperatorName() { - try { - TelephonyManager tm = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - return tm != null ? tm.getNetworkOperatorName() : null; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getNetworkOperatorName"); - } - return null; - } - - // == - - /** - * 获取当前网络类型 - * @return DevUtils.getContext() - * @return 网络类型 - */ - @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public static NetworkType getNetworkType() { - // 默认网络类型 - NetworkType netType = NetworkType.NETWORK_NO; - // 获取网络信息 - NetworkInfo networkInfo = getActiveNetworkInfo(); - // 判断是否可用 - if (networkInfo != null && networkInfo.isAvailable()) { // 同 getNetworkClass 方法 - // 属于可用则修改为未知 - netType = NetworkType.NETWORK_UNKNOWN; - // 获取类型 - switch (networkInfo.getType()){ - case ConnectivityManager.TYPE_WIFI: // 属于Wifi - netType = NetworkType.NETWORK_WIFI; - break; - case ConnectivityManager.TYPE_MOBILE: // 属于手机网络 - switch (networkInfo.getSubtype()) { - // == 2G网络 == - case TelephonyManager.NETWORK_TYPE_GSM: - case TelephonyManager.NETWORK_TYPE_GPRS: - case TelephonyManager.NETWORK_TYPE_CDMA: - case TelephonyManager.NETWORK_TYPE_EDGE: - case TelephonyManager.NETWORK_TYPE_1xRTT: - case TelephonyManager.NETWORK_TYPE_IDEN: - netType = NetworkType.NETWORK_2G; - break; - // == 3G网络 == - case TelephonyManager.NETWORK_TYPE_TD_SCDMA: - case TelephonyManager.NETWORK_TYPE_EVDO_A: - case TelephonyManager.NETWORK_TYPE_UMTS: - case TelephonyManager.NETWORK_TYPE_EVDO_0: - case TelephonyManager.NETWORK_TYPE_HSDPA: - case TelephonyManager.NETWORK_TYPE_HSUPA: - case TelephonyManager.NETWORK_TYPE_HSPA: - case TelephonyManager.NETWORK_TYPE_EVDO_B: - case TelephonyManager.NETWORK_TYPE_EHRPD: - case TelephonyManager.NETWORK_TYPE_HSPAP: - netType = NetworkType.NETWORK_3G; - break; - // == 4G网络 == - case TelephonyManager.NETWORK_TYPE_LTE: - case TelephonyManager.NETWORK_TYPE_IWLAN: - //case TelephonyManager.NETWORK_TYPE_LTE_CA: // 19 - case 19: - netType = NetworkType.NETWORK_4G; - break; - default: // 其他判断 - try { - // 判断子类名字 - String subtypeName = networkInfo.getSubtypeName(); - // == 3G 网络 == - if (subtypeName.equalsIgnoreCase("TD-SCDMA") - || subtypeName.equalsIgnoreCase("WCDMA") - || subtypeName.equalsIgnoreCase("CDMA2000")) { - netType = NetworkType.NETWORK_3G; - } else { - netType = NetworkType.NETWORK_UNKNOWN; - } - } catch (Exception e) { - // 保存未知 - netType = NetworkType.NETWORK_UNKNOWN; - } - break; - } - break; - } - } - return netType; - } - - /** - * 获取域名ip地址 - * @param domain 域名 www.baidu.com 不需要加上http - * @return ip地址 - */ - public static String getDomainAddress(final String domain) { - try { - ExecutorService exec = Executors.newCachedThreadPool(); - Future fs = exec.submit(new Callable() { - @Override - public String call() throws Exception { - InetAddress inetAddress; - try { - inetAddress = InetAddress.getByName(domain); - return inetAddress.getHostAddress(); - } catch (UnknownHostException e) { - LogPrintUtils.eTag(TAG, e, "getDomainAddress"); - } - return null; - } - }); - return fs.get(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDomainAddress"); - } - return null; - } - - /** - * 获取IP地址 - * @param useIPv4 是否用IPv4 - * @return IP地址 - */ - public static String getIPAddress(boolean useIPv4) { - try { - Enumeration nis = NetworkInterface.getNetworkInterfaces(); - while (nis.hasMoreElements()) { - NetworkInterface ni = nis.nextElement(); - // 防止小米手机返回10.0.2.15 - if (!ni.isUp()) continue; - for (Enumeration addresses = ni.getInetAddresses(); addresses.hasMoreElements(); ) { - InetAddress inetAddress = addresses.nextElement(); - if (!inetAddress.isLoopbackAddress()) { - String hostAddress = inetAddress.getHostAddress(); - boolean isIPv4 = hostAddress.indexOf(':') < 0; - if (useIPv4) { - if (isIPv4) return hostAddress; - } else { - if (!isIPv4) { - int index = hostAddress.indexOf('%'); - return index < 0 ? hostAddress.toUpperCase() : hostAddress.substring(0, index).toUpperCase(); - } - } - } - } - } - } catch (SocketException e) { - LogPrintUtils.eTag(TAG, e, "getIPAddress"); - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/NotificationUtils.java b/DevLibUtils/src/main/java/dev/utils/app/NotificationUtils.java deleted file mode 100644 index 43b22ed0fa..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/NotificationUtils.java +++ /dev/null @@ -1,300 +0,0 @@ -package dev.utils.app; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.os.Build; - -import dev.DevUtils; - -/** - * detail: 通知栏管理类 - * Created by Ttt - * https://blog.csdn.net/hss01248/article/details/55096553 - * https://www.jianshu.com/p/cf5f6c30019d - */ -public final class NotificationUtils { - - private NotificationUtils(){ - } - - // 通知栏管理类 - private static NotificationManager mNotificationManager = null; - - /** - * 获取通知栏管理类 - * @return - */ - public static NotificationManager getNotificationManager() { - if (mNotificationManager == null){ - mNotificationManager = (NotificationManager) DevUtils.getContext().getSystemService(Context.NOTIFICATION_SERVICE); - } - return mNotificationManager; - } - - /** - * 移除通知 - 移除所有通知(只是针对当前Context下的Notification) - */ - public static void cancelAll(){ - if (getNotificationManager() != null){ - mNotificationManager.cancelAll(); - } - } - - /** - * 移除通知 - 移除标记为id的通知 (只是针对当前Context下的所有Notification) - * @param args - */ - public static void cancel(final int... args){ - if (getNotificationManager() != null && args != null){ - for (int id : args){ - mNotificationManager.cancel(id); - } - } - } - - /** - * 移除通知 - 移除标记为id的通知 (只是针对当前Context下的所有Notification) - * @param tag - * @param id - */ - public static void cancel(final String tag, final int id){ - if (getNotificationManager() != null && tag != null){ - mNotificationManager.cancel(tag, id); - } - } - - /** - * 进行通知 - * @param id - * @param notification - * @return - */ - public static boolean notify(final int id, final Notification notification){ - if (getNotificationManager() != null && notification != null){ - mNotificationManager.notify(id, notification); - return true; - } - return false; - } - - /** - * 进行通知 - * @param tag - * @param id - * @param notification - * @return - */ - public static boolean notify(final String tag, final int id, final Notification notification){ - if (getNotificationManager() != null && tag != null && notification != null){ - mNotificationManager.notify(tag, id, notification); - return true; - } - return false; - } - - // == 封装外部调用 == - -// // 使用自定义View -// RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.xxx); -// // 设置View -// Notification.builder.setContent(remoteViews); - - /** - * 获取跳转id - * @param intent - * @param id - * @return - */ - public static PendingIntent crePendingIntent(Intent intent, int id){ - /* 跳转Intent */ - PendingIntent pIntent = PendingIntent.getActivity(DevUtils.getContext(), id, intent, PendingIntent.FLAG_UPDATE_CURRENT); - return pIntent; - } - - /** - * 创建通知栏对象 - * @param icon - * @param title - * @param msg - * @return - */ - public static Notification creNotification(int icon, String title, String msg) { - return creNotification(null, icon, title, title, msg, true, VibratePattern.obtain(0, 100, 300), LightPattern.obtain(Color.WHITE, 1000, 1000)); - } - - /** - * 创建通知栏对象 - * @param icon - * @param title - * @param msg - * @param vibratePattern - * @param lightPattern - * @return - */ - public static Notification creNotification(int icon, String title, String msg, VibratePattern vibratePattern, LightPattern lightPattern) { - return creNotification(null, icon, title, title, msg, true, vibratePattern, lightPattern); - } - - /** - * 创建通知栏对象 - * @param pendingIntent - * @param icon - * @param ticker - * @param title - * @param msg - * @param isAutoCancel - * @param vibratePattern - * @param lightPattern - * @return - */ - public static Notification creNotification(PendingIntent pendingIntent, int icon, String ticker, String title, String msg, boolean isAutoCancel, VibratePattern vibratePattern, LightPattern lightPattern) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - Notification.Builder builder = new Notification.Builder(DevUtils.getContext()); - // 点击通知执行intent - builder.setContentIntent(pendingIntent); - // 设置图标 - builder.setSmallIcon(icon); - // 设置图标 - builder.setLargeIcon(BitmapFactory.decodeResource(DevUtils.getContext().getResources(), icon)); - // 指定通知的ticker内容,通知被创建的时候,在状态栏一闪而过,属于瞬时提示信息。 - builder.setTicker(ticker); - // 设置标题 - builder.setContentTitle(title); - // 设置内容 - builder.setContentText(msg); - // 设置消息提醒,震动 | 声音 - builder.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND); - // 将AutoCancel设为true后,当你点击通知栏的notification后,它会自动被取消消失 - builder.setAutoCancel(isAutoCancel); - // 设置时间 - builder.setWhen(System.currentTimeMillis()); - // 设置震动参数 - if (vibratePattern != null && !vibratePattern.isEmpty()){ - builder.setVibrate(vibratePattern.vibrates); - } - // 设置 led 灯参数 - if (lightPattern != null){ - builder.setLights(lightPattern.argb, lightPattern.durationMS, lightPattern.startOffMS); - } - // = 初始化 Notification 对象 = - Notification baseNF; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - baseNF = builder.getNotification(); - } else { - baseNF = builder.build(); - } - return baseNF; - } else { -// // https://www.cnblogs.com/Arture/p/5523695.html -// // -- 初始化通知信息实体类 -- -// Notification notification = new Notification(); -// // 设置图标 -// notification.icon = icon; -// // 设置图标 -// notification.largeIcon = BitmapFactory.decodeResource(DevUtils.getContext().getResources(), icon); -// // 指定通知的ticker内容,通知被创建的时候,在状态栏一闪而过,属于瞬时提示信息。 -// notification.tickerText = title; -// // 设置时间 -// notification.when = System.currentTimeMillis(); -// // 设置消息提醒,震动 | 声音 -// notification.defaults = Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND; -// // 点击了此通知则取消该通知栏 -// if (isAutoCancel) { -// notification.flags |= Notification.FLAG_AUTO_CANCEL; -// } -// // 设置震动参数 -// if (vibratePattern != null && !vibratePattern.isEmpty()){ -// notification.vibrate = vibratePattern.vibrates; -// } -// // 设置 led 灯参数 -// if (lightPattern != null){ -// try { -// notification.ledARGB = lightPattern.argb; // 控制 LED 灯的颜色,一般有红绿蓝三种颜色可选 -// notification.ledOffMS = lightPattern.startOffMS; // 指定 LED 灯暗去的时长,也是以毫秒为单位 -// notification.ledOnMS = lightPattern.durationMS; // 指定 LED 灯亮起的时长,以毫秒为单位 -// notification.flags = Notification.FLAG_SHOW_LIGHTS; -// } catch (Exception e){ -// } -// } -// // 设置标题内容等 - 已经移除, 现在都是支持 4.0以上, 不需要兼容处理 -// notification.setLatestEventInfo(DevUtils.getContext(), title, msg, pendingIntent); -// return notification; - } - return null; - } - - /** - * detail: 设置通知栏 Led 灯参数实体类 - * Created by Ttt - */ - public static class LightPattern { - /** - * 手机处于锁屏状态时, LED灯就会不停地闪烁, 提醒用户去查看手机,下面是绿色的灯光一 闪一闪的效果 - */ - private int argb = 0; // 控制 LED 灯的颜色,一般有红绿蓝三种颜色可选 - private int startOffMS = 0; // 指定 LED 灯暗去的时长,也是以毫秒为单位 - private int durationMS = 0; // 指定 LED 灯亮起的时长,以毫秒为单位 - - private LightPattern(int argb, int startOffMS, int durationMS) { - this.argb = argb; - this.startOffMS = startOffMS; - this.durationMS = durationMS; - } - - /** - * 获取 Led 配置参数 - * @param argb 颜色 - * @param startOffMS 开始时间 - * @param durationMS 持续时间 - * @return - */ - public static LightPattern obtain(int argb, int startOffMS, int durationMS) { - return new LightPattern(argb, startOffMS, durationMS); - } - } - - /** - * detail: 设置通知栏 震动参数实体类 - * Created by Ttt - */ - public static class VibratePattern { - /** - * vibrate 属性是一个长整型的数组,用于设置手机静止和振动的时长,以毫秒为单位。 - * 参数中下标为0的值表示手机静止的时长,下标为1的值表示手机振动的时长, 下标为2的值又表示手机静止的时长,以此类推。 - */ - // long[] vibrates = { 0, 1000, 1000, 1000 }; - private long[] vibrates = null; - - private VibratePattern(long[] vibrates) { - this.vibrates = vibrates; - } - - /** - * 判断是否为null - * @return - */ - public boolean isEmpty(){ - if (vibrates != null){ - if (vibrates.length != 0){ - return false; - } - } - return true; - } - - /** - * 获取 震动时间 配置参数 - * @param args - * @return - */ - public static VibratePattern obtain(long... args) { - return new VibratePattern(args); - } - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/PathUtils.java b/DevLibUtils/src/main/java/dev/utils/app/PathUtils.java deleted file mode 100644 index 4e7c2182c1..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/PathUtils.java +++ /dev/null @@ -1,360 +0,0 @@ -package dev.utils.app; - -import android.content.ContentResolver; -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; -import android.support.annotation.RequiresApi; - -import dev.DevUtils; - -/** - * detail: 路径相关工具类 - * @author: Blankj - */ -public final class PathUtils { - - private PathUtils() { - } - - /** - * 获取 Android 系统根目录 - path: /system - * @return 系统根目录 - */ - public static String getRootPath() { - return Environment.getRootDirectory().getAbsolutePath(); - } - - /** - * 获取 data 目录 - path: /data - * @return data 目录 - */ - public static String getDataPath() { - return Environment.getDataDirectory().getAbsolutePath(); - } - - /** - * 获取缓存目录 - path: data/cache - * @return 缓存目录 - */ - public static String getIntDownloadCachePath() { - return Environment.getDownloadCacheDirectory().getAbsolutePath(); - } - - /** - * 获取此应用的缓存目录 - path: /data/data/package/cache - * @return 此应用的缓存目录 - */ - public static String getAppIntCachePath() { - return DevUtils.getContext().getCacheDir().getAbsolutePath(); - } - - /** - * 获取此应用的文件目录 - path: /data/data/package/files - * @return 此应用的文件目录 - */ - public static String getAppIntFilesPath() { - return DevUtils.getContext().getFilesDir().getAbsolutePath(); - } - - /** - * 获取此应用的数据库文件目录 - path: /data/data/package/databases/name - * @param name 数据库文件名 - * @return 数据库文件目录 - */ - public static String getAppIntDbPath(String name) { - return DevUtils.getContext().getDatabasePath(name).getAbsolutePath(); - } - - /** - * 获取 Android 外置储存的根目录 - path: /storage/emulated/0 - * @return 外置储存根目录 - */ - public static String getExtStoragePath() { - return Environment.getExternalStorageDirectory().getAbsolutePath(); - } - - /** - * 获取闹钟铃声目录 - path: /storage/emulated/0/Alarms - * @return 闹钟铃声目录 - */ - public static String getExtAlarmsPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS).getAbsolutePath(); - } - - /** - * 获取相机拍摄的照片和视频的目录 - path: /storage/emulated/0/DCIM - * @return 照片和视频目录 - */ - public static String getExtDcimPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath(); - } - - /** - * 获取文档目录 - path: /storage/emulated/0/Documents - * @return 文档目录 - */ - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - public static String getExtDocumentsPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath(); - } - - /** - * 获取下载目录 - path: /storage/emulated/0/Download - * @return 下载目录 - */ - public static String getExtDownloadsPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); - } - - /** - * 获取视频目录 - path: /storage/emulated/0/Movies - * @return 视频目录 - */ - public static String getExtMoviesPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath(); - } - - /** - * 获取音乐目录 - path: /storage/emulated/0/Music - * @return 音乐目录 - */ - public static String getExtMusicPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath(); - } - - /** - * 获取提示音目录 - path: /storage/emulated/0/Notifications - * @return 提示音目录 - */ - public static String getExtNotificationsPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS).getAbsolutePath(); - } - - /** - * 获取图片目录 - path: /storage/emulated/0/Pictures - * @return 图片目录 - */ - public static String getExtPicturesPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath(); - } - - /** - * 获取 Podcasts 目录 - path: /storage/emulated/0/Podcasts - * @return Podcasts 目录 - */ - public static String getExtPodcastsPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS).getAbsolutePath(); - } - - /** - * 获取铃声目录 - path: /storage/emulated/0/Ringtones - * @return 下载目录 - */ - public static String getExtRingtonesPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的缓存目录 - path: /storage/emulated/0/Android/data/package/cache - * @return 此应用在外置储存中的缓存目录 - */ - public static String getAppExtCachePath() { - return DevUtils.getContext().getExternalCacheDir().getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的文件目录 - path: /storage/emulated/0/Android/data/package/files - * @return 此应用在外置储存中的文件目录 - */ - public static String getAppExtFilePath() { - return DevUtils.getContext().getExternalFilesDir(null).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的闹钟铃声目录 - path: /storage/emulated/0/Android/data/package/files/Alarms - * @return 此应用在外置储存中的闹钟铃声目录 - */ - public static String getAppExtAlarmsPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_ALARMS).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的相机目录 - path: /storage/emulated/0/Android/data/package/files/DCIM - * @return 此应用在外置储存中的相机目录 - */ - public static String getAppExtDcimPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_DCIM).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的文档目录 - path: /storage/emulated/0/Android/data/package/files/Documents - * @return 此应用在外置储存中的文档目录 - */ - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - public static String getAppExtDocumentsPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的闹钟目录 - path: /storage/emulated/0/Android/data/package/files/Download - * @return 此应用在外置储存中的闹钟目录 - */ - public static String getAppExtDownloadPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的视频目录 - path: /storage/emulated/0/Android/data/package/files/Movies - * @return 此应用在外置储存中的视频目录 - */ - public static String getAppExtMoviesPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_MOVIES).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的音乐目录 - path: /storage/emulated/0/Android/data/package/files/Music - * @return 此应用在外置储存中的音乐目录 - */ - public static String getAppExtMusicPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_MUSIC).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的提示音目录 - path: /storage/emulated/0/Android/data/package/files/Notifications - * @return 此应用在外置储存中的提示音目录 - */ - public static String getAppExtNotificationsPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_NOTIFICATIONS).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的图片目录 - path: /storage/emulated/0/Android/data/package/files/Pictures - * @return 此应用在外置储存中的图片目录 - */ - public static String getAppExtPicturesPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的 Podcasts 目录 - path: /storage/emulated/0/Android/data/package/files/Podcasts - * @return 此应用在外置储存中的 Podcasts 目录 - */ - public static String getAppExtPodcastsPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_PODCASTS).getAbsolutePath(); - } - - /** - * 获取此应用在外置储存中的铃声目录 - path: /storage/emulated/0/Android/data/package/files/Ringtones - * @return 此应用在外置储存中的铃声目录 - */ - public static String getAppExtRingtonesPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_RINGTONES).getAbsolutePath(); - } - - /** - * 获取此应用的 Obb 目录 - path: /storage/emulated/0/Android/obb/package - * 一般用来存放游戏数据包 - * @return 此应用的 Obb 目录 - */ - public static String getObbPath() { - return DevUtils.getContext().getObbDir().getAbsolutePath(); - } - - public static String getFilePathByUri(Context context, Uri uri) { - String path = null; - // 以 file:// 开头的 - if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { - path = uri.getPath(); - return path; - } - // 以 content:// 开头的,比如 content://media/extenral/images/media/17766 - if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.Media.DATA}, null, null, null); - if (cursor != null) { - if (cursor.moveToFirst()) { - int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); - if (columnIndex > -1) { - path = cursor.getString(columnIndex); - } - } - cursor.close(); - } - return path; - } - // 4.4及之后的 是以 content:// 开头的,比如 content://com.android.providers.media.documents/document/image%3A235700 - if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - if (DocumentsContract.isDocumentUri(context, uri)) { - if (isExternalStorageDocument(uri)) { - // ExternalStorageProvider - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - if ("primary".equalsIgnoreCase(type)) { - path = Environment.getExternalStorageDirectory() + "/" + split[1]; - return path; - } - } 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)); - path = getDataColumn(context, contentUri, null, null); - return path; - } 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]}; - path = getDataColumn(context, contentUri, selection, selectionArgs); - return path; - } - } - } - return null; - } - - private 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 column_index = cursor.getColumnIndexOrThrow(column); - return cursor.getString(column_index); - } - } finally { - if (cursor != null) - cursor.close(); - } - return null; - } - - private static boolean isExternalStorageDocument(Uri uri) { - return "com.android.externalstorage.documents".equals(uri.getAuthority()); - } - - private static boolean isDownloadsDocument(Uri uri) { - return "com.android.providers.downloads.documents".equals(uri.getAuthority()); - } - - private static boolean isMediaDocument(Uri uri) { - return "com.android.providers.media.documents".equals(uri.getAuthority()); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/PermissionUtils.java b/DevLibUtils/src/main/java/dev/utils/app/PermissionUtils.java deleted file mode 100644 index 5950762201..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/PermissionUtils.java +++ /dev/null @@ -1,383 +0,0 @@ -package dev.utils.app; - -import android.Manifest; -import android.app.Activity; -import android.app.Fragment; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.RequiresApi; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import dev.DevUtils; - -/** - * detail: 权限请求工具类 - * Created by Ttt - * hint: - * - - * 参考: - * https://github.com/anthonycr/Grant - * compile 'com.anthonycr.grant:permissions:1.0' - * - - * 权限介绍 - * https://www.cnblogs.com/mengdd/p/4892856.html - * - - * 第三方库: - * PermissionsDispatcher: https://github.com/hotchemi/PermissionsDispatcher - * RxPermissions: https://github.com/tbruyelle/RxPermissions - * Grant: https://github.com/anthonycr/Grant - * - - * 使用方法 - * // 第一种请求方式 - * PermissionUtils.permission("").callBack(null).request(); - * // 第二种请求方式 - 需要在 onRequestPermissionsResult 中通知调用 - * PermissionUtils.permission("").callBack(null).request(Activity); - * ====== - * 注意事项: 需要注意在onResume 中调用 - * 不管是第一种方式, 跳自定义的Activity, 还是第二种 系统内部跳转授权页面, 都会多次触发onResume - * https://www.aliyun.com/jiaocheng/8030.html - * 尽量避免在 onResume中调用 - * com.anthonycr.grant:permissions:1.0 也是会触发onResume 只是 通过 Set mPendingRequests 来控制请求过的权限 - * 拒绝后在onResume 方法内再次请求, 直接触发授权成功, 如果需要清空通过调用 notifyPermissionsChange 通知改变, 否则一直调用获取权限,拒绝过后,都会认为是请求通过 - */ -public final class PermissionUtils { - - /** Permission 请求Code */ - public static final int P_REQUEST_CODE = 100; - /** 全部权限 */ - private final static Set mAllPermissions = new HashSet<>(1); - // 判断是否请求过 - private boolean isRequest = false; - /** 申请的权限 */ - private List mPermissions = new ArrayList<>(); - /** 准备请求的权限 */ - private List mPermissionsRequest = new ArrayList<>(); - /** 申请通过的权限 */ - private List mPermissionsGranted = new ArrayList<>(); - /** 申请未通过的权限 */ - private List mPermissionsDenied = new ArrayList<>(); - /** 申请未通过的权限 - 永久拒绝 */ - private List mPermissionsDeniedForever = new ArrayList<>(); - /** 查询不到的权限 */ - private List mPermissionsNotFound = new ArrayList<>(); - /** 操作回调 */ - private PermissionCallBack mCallBack; - /** 回调方法 */ - private Looper mLooper = Looper.getMainLooper(); - - static { - // 初始化权限数据 - initializePermissionsMap(); - } - - /** 初始化遍历保存全部权限 */ - private static synchronized void initializePermissionsMap() { - Field[] fields = Manifest.permission.class.getFields(); - for (Field field : fields) { - String name = null; - try { - name = (String) field.get(""); - } catch (IllegalAccessException e) { - } - mAllPermissions.add(name); - } - } - - // = - - /** - * 构造函数 - * @param permissions - */ - private PermissionUtils(final String... permissions){ - mPermissions.clear(); - // 防止数据为null - if (permissions != null && permissions.length != 0){ - // 遍历全部需要申请的权限 - for (String permission : permissions){ - mPermissions.add(permission); - } - } - } - - // == - - /** - * 判断是否授予了权限 - * @param permissions - * @return - */ - public static boolean isGranted(final String... permissions) { - // 防止数据为null - if (permissions != null && permissions.length != 0) { - // 遍历全部需要申请的权限 - for (String permission : permissions) { - if (!isGranted(DevUtils.getContext(), permission)) { - return false; - } - } - } - return true; - } - - /** - * 判断是否授予了权限 - * @param context - * @param permission - * @return - */ - private static boolean isGranted(final Context context, final String permission) { - // SDK 版本小于 23 则表示直接通过 || 检查是否通过权限 - return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(context, permission); - } - - /** - * 是否拒绝了权限 - 拒绝过一次, 再次申请时, 弹出选择不再提醒并拒绝才会触发 true - * @param activity - * @param permission - * @return - */ - public static boolean shouldShowRequestPermissionRationale(Activity activity, final String permission){ - return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission); - } - - // == 使用方法 == - - /** - * 申请权限初始化 - * @param permissions - * @return - */ - public static PermissionUtils permission(final String... permissions){ - return new PermissionUtils(permissions); - } - - /** - * 设置回调方法 - * @param callBack - */ - public PermissionUtils callBack(PermissionCallBack callBack){ - if (isRequest){ - return this; - } - this.mCallBack = callBack; - return this; - } - - /** - * 权限判断处理 - * @return -1 已经请求过, 0 = 不处理, 1 = 需要请求 - */ - private int checkPermissions(){ - if (isRequest){ - return -1; // 已经申请过 - } - isRequest = true; - // 如果 SDK 版本小于 23 则直接通过 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - // 表示全部权限都通过 - mPermissionsGranted.addAll(mPermissions); - // 处理请求回调 - requestCallback(); - } else { - for (String permission : mPermissions){ - // 首先判断是否存在 - if (mAllPermissions.contains(permission)){ - // 判断是否通过请求 - if (isGranted(DevUtils.getContext(), permission)){ - mPermissionsGranted.add(permission); // 权限允许通过 - } else { - mPermissionsRequest.add(permission); // 准备请求权限 - } - } else { - // 保存到没找到的权限集合 - mPermissionsNotFound.add(permission); - } - } - // 判断是否存在等待请求的权限 - if (mPermissionsRequest.isEmpty()){ - // 处理请求回调 - requestCallback(); - } else { // 表示需要申请 - return 1; - } - } - // 表示不需要申请 - return 0; - } - - /** - * 请求权限 - * -- - * 内部自动调用 PermissionUtils.isGranted, 并且进行判断处理 - * 无需调用以下代码判断 - * boolean isGranted = PermissionUtils.isGranted(Manifest.permission.xx); - */ - public void request(){ - if (checkPermissions() == 1){ - // 如果 SDK 版本大于 23 才请求 - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { - sInstance = this; - // 自定义Activity - PermissionUtils.PermissionActivity.start(DevUtils.getContext()); - } - } - } - - /** - * 请求权限 - * @param activity {@link Fragment#getActivity()} - */ - public void request(Activity activity){ - request(activity, P_REQUEST_CODE); - } - - /** - * 请求权限 - 需要在Activity 的 onRequestPermissionsResult 回调中 调用 PermissionUtils.onRequestPermissionsResult(this); - * @param activity {@link Fragment#getActivity()} - * @param requestCode - */ - public void request(Activity activity, int requestCode){ - if (checkPermissions() == 1 && activity != null){ - // 如果 SDK 版本大于 23 才请求 - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { - sInstance = this; - // 请求权限 - String[] permissions = mPermissionsRequest.toArray(new String[mPermissionsRequest.size()]); - // 请求权限 - ActivityCompat.requestPermissions(activity, permissions, requestCode); - } - } - } - - // == 请求权限回调 == - - public interface PermissionCallBack { - /** - * 授权通过权限 - * @param permissionUtils - */ - void onGranted(PermissionUtils permissionUtils); - - /** - * 授权未通过权限 - * @param permissionUtils - */ - void onDenied(PermissionUtils permissionUtils); - } - - // == 内部Activity == - - // 内部持有对象 - private static PermissionUtils sInstance; - - // 实现Activity的透明效果 - // https://blog.csdn.net/u014434080/article/details/52260407 - @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) { - super.onCreate(savedInstanceState); - // 请求权限 - int size = sInstance.mPermissionsRequest.size(); - requestPermissions(sInstance.mPermissionsRequest.toArray(new String[size]), 1); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - sInstance.onRequestPermissionsResultCommon(this); // 处理回调 - finish(); // 关闭当前页面 - } - } - - // == 内部处理方法 == - - /** 内部请求回调, 统一处理方法 */ - private void requestCallback() { - if (mCallBack != null){ - // 判断是否允许全部权限 - boolean isGrantedAll = (mPermissions.size() == mPermissionsGranted.size()); - // 允许则触发回调 - if (isGrantedAll){ - new Handler(mLooper).post(new Runnable() { - @Override - public void run() { - mCallBack.onGranted(PermissionUtils.this); - } - }); - } else { - new Handler(mLooper).post(new Runnable() { - @Override - public void run() { - mCallBack.onDenied(PermissionUtils.this); - } - }); - } - } - } - - /** - * 请求回调权限回调处理 - 通用 - * @param activity - */ - private void onRequestPermissionsResultCommon(final Activity activity) { - // 获取权限状态 - getPermissionsStatus(activity); - // 判断请求结果 - requestCallback(); - } - - /** - * 获取权限状态 - * @param activity - */ - private void getPermissionsStatus(final Activity activity) { - for (String permission : mPermissionsRequest) { - // 判断是否通过请求 - if (isGranted(activity, permission)){ - mPermissionsGranted.add(permission); - } else { - // 未授权 - mPermissionsDenied.add(permission); - // 拒绝权限 - if (!shouldShowRequestPermissionRationale(activity, permission)) { - mPermissionsDeniedForever.add(permission); - } - } - } - } - - // == 通过传入Activity 方式 == - - /** - * 请求权限回调 - 需要在 onRequestPermissionsResult 回调里面调用 - * @param activity - */ - public static void onRequestPermissionsResult(Activity activity){ - if (activity != null && sInstance != null){ - // 触发回调 - sInstance.onRequestPermissionsResultCommon(activity); - } - } -} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/PhoneUtils.java b/DevLibUtils/src/main/java/dev/utils/app/PhoneUtils.java deleted file mode 100644 index ca38436017..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/PhoneUtils.java +++ /dev/null @@ -1,808 +0,0 @@ -package dev.utils.app; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.PendingIntent; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.SystemClock; -import android.provider.ContactsContract; -import android.provider.Settings; -import android.telephony.SmsManager; -import android.telephony.TelephonyManager; -import android.text.TextUtils; -import android.util.Xml; - -import org.xmlpull.v1.XmlSerializer; - -import java.io.File; -import java.io.FileOutputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.UUID; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 手机相关工具类 - * Created by Ttt - */ -public final class PhoneUtils { - - private PhoneUtils() { - } - - // 日志TAG - private static final String TAG = PhoneUtils.class.getSimpleName(); - - /** - * 判断是否装载sim卡 - * @return - */ - public static boolean isSimReady() { - try { - // 获取电话管理类 - TelephonyManager tpManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - // 是否准备完毕 - if(tpManager != null && tpManager.getSimState() == TelephonyManager.SIM_STATE_READY) { - return true; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "isSimReady"); - } - return false; - } - - /** - * 获取Sim卡所属地区,非国内地区暂不支持播放 - * @return 返回SIM的地区码 - */ - public static String getUserCountry() { - try { - TelephonyManager tpManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - String simCountry = tpManager.getSimCountryIso(); - if (simCountry != null && simCountry.length() == 2) { - // SIM country code is available - return simCountry.toLowerCase(Locale.CHINA); - } else if (tpManager.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) { - // device is not 3G (would be unreliable) - String networkCountry = tpManager.getNetworkCountryIso(); - if (networkCountry != null && networkCountry.length() == 2) { - // network country code is available - return networkCountry.toLowerCase(Locale.CHINESE); - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getUserCountry"); - } - return null; - } - - /** - * 判断地区,是否属于国内 - * @return 状态码 1 属于国内(中国),2 属于 国外,3 属于无SIM卡 - */ - public static int judgeArea() { - // 默认属于无sim卡 - int state = 3; - try { - String countryCode = getUserCountry(); - // 不等于null,表示属于存在SIM卡 - if (countryCode != null) { - // zh_CN Locale.SIMPLIFIED_CHINESE - // 截取前面两位属于zh表示属于中国 - String country = countryCode.substring(0, 2); - // 如果属于ch开头表示属于中国 - if (country.toLowerCase().equals("cn")) { - state = 1; - } else { - state = 2; - } - } else { // 不存在sim卡 - String localCountry = Locale.getDefault().getCountry(); - // 如果属于ch开头表示属于中国 - if (localCountry.toLowerCase().equals("cn")) { - return 1; - } else { - return 2; - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "judgeArea"); - } - return state; - } - - /** - * 判断设备是否是手机 - * @return true : 是, false : 否 - */ - public static boolean isPhone() { - try { - TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - return telephonyManager != null && telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "isPhone"); - } - return false; - } - - /** - * 获取 IMEI 码 - * - * @return IMEI 码 - * ========== - * IMEI是International Mobile Equipment Identity (国际移动设备标识)的简称 - * IMEI由15位数字组成的”电子串号”,它与每台手机一一对应,而且该码是全世界唯一的 - * 其组成为: - * 1. 前6位数(TAC)是”型号核准号码”,一般代表机型 - * 2. 接着的2位数(FAC)是”最后装配号”,一般代表产地 - * 3. 之后的6位数(SNR)是”串号”,一般代表生产顺序号 - * 4. 最后1位数(SP)通常是”0″,为检验码,目前暂备用 - */ - @SuppressLint({"HardwareIds", "MissingPermission"}) - public static String getIMEI() { - try { - TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - return telephonyManager != null ? telephonyManager.getDeviceId() : null; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getIMEI"); - } - return null; - } - - /** - * 获取 IMSI 码 - * - * @return IMSI 码 - * ========= - * IMSI是国际移动用户识别码的简称(International Mobile Subscriber Identity) - * IMSI共有15位,其结构如下: - * MCC+MNC+MIN - * MCC:Mobile Country Code,移动国家码,共3位,中国为460; - * MNC:Mobile NetworkCode,移动网络码,共2位 - * 在中国,移动的代码为电00和02,联通的代码为01,电信的代码为03 - * 合起来就是(也是Android手机中APN配置文件中的代码): - * 中国移动:46000 46002 - * 中国联通:46001 - * 中国电信:46003 - * 举例,一个典型的IMSI号码为460030912121001 - */ - @SuppressLint({"HardwareIds", "MissingPermission"}) - public static String getIMSI() { - try { - TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - return telephonyManager != null ? telephonyManager.getSubscriberId() : null; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getIMSI"); - } - return null; - } - - /** - * 获取IMSI处理过后的简称 - * @param IMSI - * @return - */ - public static String getIMSIIDName(String IMSI){ - if (IMSI != null) { - if (IMSI.startsWith("46000") || IMSI.startsWith("46002")) { - return "中国移动"; - } else if (IMSI.startsWith("46001")) { - return "中国联通"; - } else if (IMSI.startsWith("46003")) { - return "中国电信"; - } - } - return null; - } - - /** - * 获取移动终端类型 - * @return 手机制式 - * {@link TelephonyManager#PHONE_TYPE_NONE } : 0 手机制式未知 - * {@link TelephonyManager#PHONE_TYPE_GSM } : 1 手机制式为 GSM,移动和联通 - * {@link TelephonyManager#PHONE_TYPE_CDMA } : 2 手机制式为 CDMA,电信 - * {@link TelephonyManager#PHONE_TYPE_SIP } : 3 - */ - public static int getPhoneType() { - try { - TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - return telephonyManager != null ? telephonyManager.getPhoneType() : -1; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getPhoneType"); - } - return -1; - } - - /** - * 判断 sim 卡是否准备好 - * @return true : 是, false : 否 - */ - public static boolean isSimCardReady() { - try { - TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - return telephonyManager != null && telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "isSimCardReady"); - } - return false; - } - - /** - * 获取 Sim 卡运营商名称 => 中国移动、如中国联通、中国电信 - * @return sim 卡运营商名称 - */ - public static String getSimOperatorName() { - try { - TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - return telephonyManager != null ? telephonyManager.getSimOperatorName() : null; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getSimOperatorName"); - } - return null; - } - - /** - * 获取 Sim 卡运营商名称 => 中国移动、如中国联通、中国电信 - * @return 移动网络运营商名称 - */ - public static String getSimOperatorByMnc() { - try { - TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - String operator = telephonyManager != null ? telephonyManager.getSimOperator() : null; - if (operator == null) return null; - switch (operator) { - case "46000": - case "46002": - case "46007": - return "中国移动"; - case "46001": - return "中国联通"; - case "46003": - return "中国电信"; - default: - return operator; - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getSimOperatorByMnc"); - } - return null; - } - - /** - * 获取设备id - * - * @return - */ - @SuppressLint({"HardwareIds", "MissingPermission"}) - public static String getDeviceId(){ - try { - TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - if (telephonyManager == null) return ""; - return telephonyManager.getDeviceId(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getDeviceId"); - } - return null; - } - - /** - * 返回设备序列化 - * @return - */ - public static String getSerialNumber(){ - return android.os.Build.SERIAL; - } - - /** - * 获取Android id - * @return - */ - public static String getAndroidId() { - try { - // 在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,当设备被wipe后该值会被重置。 - String androidId = Settings.Secure.getString(DevUtils.getContext().getContentResolver(), Settings.Secure.ANDROID_ID); - return androidId; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAndroidId"); - } - return null; - } - - /** - * 获取设备唯一id - * @return - */ - public static String getUUID(){ - String androidId = getAndroidId() + ""; - String deviceId = getDeviceId() + ""; - String serialNumber = getSerialNumber() + ""; - // 生成唯一关联uuid androidId.hashCode(), deviceId.hashCode() | serialNumber.hashCode() - UUID deviceUuid = new UUID(androidId.hashCode(), ((long) deviceId.hashCode() << 32) | serialNumber.hashCode()); - // 获取uid - return deviceUuid.toString(); - } - - /** - * 获取手机状态信息 - * - * @return DeviceId(IMEI) = 99000311726612
- * DeviceSoftwareVersion = 00
- * Line1Number =
- * NetworkCountryIso = cn
- * NetworkOperator = 46003
- * NetworkOperatorName = 中国电信
- * NetworkType = 6
- * PhoneType = 2
- * SimCountryIso = cn
- * SimOperator = 46003
- * SimOperatorName = 中国电信
- * SimSerialNumber = 89860315045710604022
- * SimState = 5
- * SubscriberId(IMSI) = 460030419724900
- * VoiceMailNumber = *86
- */ - @SuppressLint({"HardwareIds", "MissingPermission"}) - public static String getPhoneStatus() { - try { - TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - if (telephonyManager == null) return ""; - StringBuffer stringBuffer = new StringBuffer(); - stringBuffer.append("DeviceId(IMEI) = " + telephonyManager.getDeviceId() + "\n"); - stringBuffer.append("DeviceSoftwareVersion = " + telephonyManager.getDeviceSoftwareVersion() + "\n"); - stringBuffer.append("Line1Number = " + telephonyManager.getLine1Number() + "\n"); - stringBuffer.append("NetworkCountryIso = " + telephonyManager.getNetworkCountryIso() + "\n"); - stringBuffer.append("NetworkOperator = " + telephonyManager.getNetworkOperator() + "\n"); - stringBuffer.append("NetworkOperatorName = " + telephonyManager.getNetworkOperatorName() + "\n"); - stringBuffer.append("NetworkType = " + telephonyManager.getNetworkType() + "\n"); - stringBuffer.append("PhoneType = " + telephonyManager.getPhoneType() + "\n"); - stringBuffer.append("SimCountryIso = " + telephonyManager.getSimCountryIso() + "\n"); - stringBuffer.append("SimOperator = " + telephonyManager.getSimOperator() + "\n"); - stringBuffer.append("SimOperatorName = " + telephonyManager.getSimOperatorName() + "\n"); - stringBuffer.append("SimSerialNumber = " + telephonyManager.getSimSerialNumber() + "\n"); - stringBuffer.append("SimState = " + telephonyManager.getSimState() + "\n"); - stringBuffer.append("SubscriberId(IMSI) = " + telephonyManager.getSubscriberId() + "(" + getIMSIIDName(telephonyManager.getSubscriberId()) + ")"+ "\n"); - stringBuffer.append("VoiceMailNumber = " + telephonyManager.getVoiceMailNumber() + "\n"); - return stringBuffer.toString(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getPhoneStatus"); - } - return ""; - } - - /** - * 跳至拨号界面 - * @param phoneNumber 电话号码 - */ - public static void dial(final String phoneNumber) { - DevUtils.getContext().startActivity(IntentUtils.getDialIntent(phoneNumber, true)); - } - - /** - * 拨打电话 - * - * @param phoneNumber 电话号码 - */ - public static void call(final String phoneNumber) { - DevUtils.getContext().startActivity(IntentUtils.getCallIntent(phoneNumber, true)); - } - - /** - * 跳至发送短信界面 - * @param phoneNumber 接收号码 - * @param content 短信内容 - */ - public static void sendSms(final String phoneNumber, final String content) { - DevUtils.getContext().startActivity(IntentUtils.getSendSmsIntent(phoneNumber, content, true)); - } - - /** - * 发送短信 - * - * @param phoneNumber 接收号码 - * @param content 短信内容 - */ - public static void sendSmsSilent(final String phoneNumber, final String content) { - if (TextUtils.isEmpty(content)) return; - PendingIntent sentIntent = PendingIntent.getBroadcast(DevUtils.getContext(), 0, new Intent(), 0); - SmsManager smsManager = SmsManager.getDefault(); - if (content.length() >= 70) { - List ms = smsManager.divideMessage(content); - for (String str : ms) { - smsManager.sendTextMessage(phoneNumber, null, str, sentIntent, null); - } - } else { - smsManager.sendTextMessage(phoneNumber, null, content, sentIntent, null); - } - } - - /** - * 获取手机联系人 - * - * - * return - */ - public static ArrayList> getAllContactInfo() { - ArrayList> list = new ArrayList<>(); - // 1.获取内容解析者 - ContentResolver resolver = DevUtils.getContext().getContentResolver(); - // 2.获取内容提供者的地址:com.android.contacts - // raw_contacts 表的地址 :raw_contacts - // view_data 表的地址 : data - // 3.生成查询地址 - Uri raw_uri = Uri.parse("content://com.android.contacts/raw_contacts"); - Uri date_uri = Uri.parse("content://com.android.contacts/data"); - // 4.查询操作,先查询 raw_contacts,查询 contact_id - // projection : 查询的字段 - Cursor cursor = resolver.query(raw_uri, new String[]{"contact_id"}, null, null, null); - try { - // 5.解析 cursor - if (cursor != null) { - while (cursor.moveToNext()) { - // 6.获取查询的数据 - String contact_id = cursor.getString(0); - // cursor.getString(cursor.getColumnIndex("contact_id"));//getColumnIndex - // : 查询字段在 cursor 中索引值,一般都是用在查询字段比较多的时候 - // 判断 contact_id 是否为空 - if (!TextUtils.isEmpty(contact_id)) {//null "" - // 7.根据 contact_id 查询 view_data 表中的数据 - // selection : 查询条件 - // selectionArgs :查询条件的参数 - // sortOrder : 排序 - // 空指针: 1.null.方法 2.参数为 null - Cursor c = resolver.query(date_uri, new String[]{"data1", "mimetype"}, "raw_contact_id=?", new String[]{contact_id}, null); - HashMap map = new HashMap(); - // 8.解析 c - if (c != null) { - while (c.moveToNext()) { - // 9.获取数据 - String data1 = c.getString(0); - String mimetype = c.getString(1); - // 10.根据类型去判断获取的 data1 数据并保存 - if (mimetype.equals("vnd.android.cursor.item/phone_v2")) { - map.put("phone", data1); // 电话 - } else if (mimetype.equals("vnd.android.cursor.item/name")) { - map.put("name", data1); // 姓名 - } - } - } - // 11.添加到集合中数据 - list.add(map); - // 12.关闭 cursor - if (c != null) { - c.close(); - } - } - } - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getAllContactInfo"); - } finally { - // 12.关闭 cursor - if (cursor != null) { - cursor.close(); - } - } - return list; - } - - /** - * 获取手机联系人 - * @return - */ - public static ArrayList> getAllContactInfo2() { - ArrayList> list = new ArrayList<>(); - try { - Cursor cursor = DevUtils.getContext().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null); - while (cursor.moveToNext()) { - HashMap map = new HashMap<>(); - // 电话号码 - String phoneNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)).trim().replaceAll(" ", ""); - // 手机联系人名字 - String phoneName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)).trim(); - // 保存手机号 - map.put("phone", phoneNumber); - // 保存名字 - map.put("name", phoneName); - // == - list.add(map); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAllContactInfo2"); - } - // 返回数据 - return list; - } - - /** - * 打开手机联系人界面点击联系人后便获取该号码 - */ - public static void getContactNum() { - Intent intent = new Intent(); - intent.setAction("android.intent.action.PICK"); - intent.setType("vnd.android.cursor.dir/phone_v2"); - ((Activity) DevUtils.getContext()).startActivityForResult(intent, 0); - -// @Override -// protected void onActivityResult ( int requestCode, int resultCode, Intent data){ -// super.onActivityResult(requestCode, resultCode, data); -// if (data != null) { -// Uri uri = data.getData(); -// String num = null; -// // 创建内容解析者 -// ContentResolver contentResolver = getContentResolver(); -// Cursor cursor = contentResolver.query(uri, -// null, null, null, null); -// while (cursor.moveToNext()) { -// num = cursor.getString(cursor.getColumnIndex("data1")); -// } -// cursor.close(); -// num = num.replaceAll("-", "");//替换的操作,555-6 -> 5556 -// } -// } - } - - /** - * 获取手机短信并保存到 xml 中 - * - * - */ - public static void getAllSMS() { - // 1.获取短信 - // 1.1获取内容解析者 - ContentResolver resolver = DevUtils.getContext().getContentResolver(); - // 1.2获取内容提供者地址 sms,sms表的地址:null 不写 - // 1.3获取查询路径 - Uri uri = Uri.parse("content://sms"); - // 1.4.查询操作 - // projection : 查询的字段 - // selection : 查询的条件 - // selectionArgs : 查询条件的参数 - // sortOrder : 排序 - Cursor cursor = resolver.query(uri, - new String[]{"address", "date", "type", "body"}, - null, - null, - null - ); - // 设置最大进度 - int count = cursor.getCount();//获取短信的个数 - // 2.备份短信 - // 2.1获取xml序列器 - XmlSerializer xmlSerializer = Xml.newSerializer(); - try { - // 2.2设置xml文件保存的路径 - // os : 保存的位置 - // encoding : 编码格式 - xmlSerializer.setOutput(new FileOutputStream(new File("/mnt/sdcard/backupsms.xml")), "utf-8"); - // 2.3设置头信息 - // standalone : 是否独立保存 - xmlSerializer.startDocument("utf-8", true); - // 2.4设置根标签 - xmlSerializer.startTag(null, "smss"); - // 1.5.解析cursor - while (cursor.moveToNext()) { - SystemClock.sleep(1000); - // 2.5设置短信的标签 - xmlSerializer.startTag(null, "sms"); - // 2.6设置文本内容的标签 - xmlSerializer.startTag(null, "address"); - String address = cursor.getString(0); - // 2.7设置文本内容 - xmlSerializer.text(address); - xmlSerializer.endTag(null, "address"); - xmlSerializer.startTag(null, "date"); - String date = cursor.getString(1); - xmlSerializer.text(date); - xmlSerializer.endTag(null, "date"); - xmlSerializer.startTag(null, "type"); - String type = cursor.getString(2); - xmlSerializer.text(type); - xmlSerializer.endTag(null, "type"); - xmlSerializer.startTag(null, "body"); - String body = cursor.getString(3); - xmlSerializer.text(body); - xmlSerializer.endTag(null, "body"); - xmlSerializer.endTag(null, "sms"); - } - xmlSerializer.endTag(null, "smss"); - xmlSerializer.endDocument(); - // 2.8将数据刷新到文件中 - xmlSerializer.flush(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAllSMS"); - } - } - - // ================ - // === 双卡模块 === - // ================ - - // 双卡双待系统IMEI和IMSI方案 - // http://benson37.iteye.com/blog/1923946 - - /** - * 双卡双待神机IMSI、IMSI、PhoneType信息 - * - */ - public static class TeleInfo { - public String imsi_1; - public String imsi_2; - public String imei_1; - public String imei_2; - public int phoneType_1; - public int phoneType_2; - - @Override - public String toString() { - return "TeleInfo{" + - "imsi_1='" + imsi_1 + '\'' + - ", imsi_2='" + imsi_2 + '\'' + - ", imei_1='" + imei_1 + '\'' + - ", imei_2='" + imei_2 + '\'' + - ", phoneType_1=" + phoneType_1 + - ", phoneType_2=" + phoneType_2 + - '}'; - } - } - - /** - * MTK Phone. - * 获取 MTK 神机的双卡 IMSI、IMSI 信息 - */ - public static TeleInfo getMtkTeleInfo(Context context) { - TeleInfo teleInfo = new TeleInfo(); - try { - Class phone = Class.forName("com.android.internal.telephony.Phone"); - - Field fields1 = phone.getField("GEMINI_SIM_1"); - fields1.setAccessible(true); - int simId_1 = (Integer) fields1.get(null); - - Field fields2 = phone.getField("GEMINI_SIM_2"); - fields2.setAccessible(true); - int simId_2 = (Integer) fields2.get(null); - - TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - Method getSubscriberIdGemini = TelephonyManager.class.getDeclaredMethod("getSubscriberIdGemini", int.class); - String imsi_1 = (String) getSubscriberIdGemini.invoke(tm, simId_1); - String imsi_2 = (String) getSubscriberIdGemini.invoke(tm, simId_2); - teleInfo.imsi_1 = imsi_1; - teleInfo.imsi_2 = imsi_2; - - Method getDeviceIdGemini = TelephonyManager.class.getDeclaredMethod("getDeviceIdGemini", int.class); - String imei_1 = (String) getDeviceIdGemini.invoke(tm, simId_1); - String imei_2 = (String) getDeviceIdGemini.invoke(tm, simId_2); - - teleInfo.imei_1 = imei_1; - teleInfo.imei_2 = imei_2; - - Method getPhoneTypeGemini = TelephonyManager.class.getDeclaredMethod("getPhoneTypeGemini", int.class); - int phoneType_1 = (Integer) getPhoneTypeGemini.invoke(tm, simId_1); - int phoneType_2 = (Integer) getPhoneTypeGemini.invoke(tm, simId_2); - teleInfo.phoneType_1 = phoneType_1; - teleInfo.phoneType_2 = phoneType_2; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getMtkTeleInfo"); - } - return teleInfo; - } - - /** - * MTK Phone. - * 获取 MTK 神机的双卡 IMSI、IMSI 信息 - */ - public static TeleInfo getMtkTeleInfo2(Context context) { - TeleInfo teleInfo = new TeleInfo(); - try { - TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - Class phone = Class.forName("com.android.internal.telephony.Phone"); - Field fields1 = phone.getField("GEMINI_SIM_1"); - fields1.setAccessible(true); - int simId_1 = (Integer) fields1.get(null); - Field fields2 = phone.getField("GEMINI_SIM_2"); - fields2.setAccessible(true); - int simId_2 = (Integer) fields2.get(null); - - Method getDefault = TelephonyManager.class.getMethod("getDefault", int.class); - TelephonyManager tm1 = (TelephonyManager) getDefault.invoke(tm, simId_1); - TelephonyManager tm2 = (TelephonyManager) getDefault.invoke(tm, simId_2); - - String imsi_1 = tm1.getSubscriberId(); - String imsi_2 = tm2.getSubscriberId(); - teleInfo.imsi_1 = imsi_1; - teleInfo.imsi_2 = imsi_2; - - String imei_1 = tm1.getDeviceId(); - String imei_2 = tm2.getDeviceId(); - teleInfo.imei_1 = imei_1; - teleInfo.imei_2 = imei_2; - - int phoneType_1 = tm1.getPhoneType(); - int phoneType_2 = tm2.getPhoneType(); - teleInfo.phoneType_1 = phoneType_1; - teleInfo.phoneType_2 = phoneType_2; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getMtkTeleInfo2"); - } - return teleInfo; - } - - /** - * Qualcomm Phone. - * 获取 高通 神机的双卡 IMSI、IMSI 信息 - */ - public static TeleInfo getQualcommTeleInfo(Context context) { - TeleInfo teleInfo = new TeleInfo(); - try { - TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - Class simTMclass = Class.forName("android.telephony.MSimTelephonyManager"); - Object sim = context.getSystemService("phone_msim"); - int simId_1 = 0; - int simId_2 = 1; - - Method getSubscriberId = simTMclass.getMethod("getSubscriberId", int.class); - String imsi_1 = (String) getSubscriberId.invoke(sim, simId_1); - String imsi_2 = (String) getSubscriberId.invoke(sim, simId_2); - teleInfo.imsi_1 = imsi_1; - teleInfo.imsi_2 = imsi_2; - - Method getDeviceId = simTMclass.getMethod("getDeviceId", int.class); - String imei_1 = (String) getDeviceId.invoke(sim, simId_1); - String imei_2 = (String) getDeviceId.invoke(sim, simId_2); - teleInfo.imei_1 = imei_1; - teleInfo.imei_2 = imei_2; - - Method getDataState = simTMclass.getMethod("getDataState"); - int phoneType_1 = tm.getDataState(); - int phoneType_2 = (Integer) getDataState.invoke(sim); - teleInfo.phoneType_1 = phoneType_1; - teleInfo.phoneType_2 = phoneType_2; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getQualcommTeleInfo"); - } - return teleInfo; - } - - /** - * Spreadtrum Phone. - * 获取 展讯 神机的双卡 IMSI、IMSI 信息 - */ - public static TeleInfo getSpreadtrumTeleInfo() { - TeleInfo teleInfo = new TeleInfo(); - try { - TelephonyManager tm1 = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - String imsi_1 = tm1.getSubscriberId(); - String imei_1 = tm1.getDeviceId(); - int phoneType_1 = tm1.getPhoneType(); - teleInfo.imsi_1 = imsi_1; - teleInfo.imei_1 = imei_1; - teleInfo.phoneType_1 = phoneType_1; - - Class phoneFactory = Class.forName("com.android.internal.telephony.PhoneFactory"); - Method getServiceName = phoneFactory.getMethod("getServiceName", String.class, int.class); - getServiceName.setAccessible(true); - String spreadTmService = (String) getServiceName.invoke(phoneFactory, Context.TELEPHONY_SERVICE, 1); - - TelephonyManager tm2 = (TelephonyManager) DevUtils.getContext().getSystemService(spreadTmService); - String imsi_2 = tm2.getSubscriberId(); - String imei_2 = tm2.getDeviceId(); - int phoneType_2 = tm2.getPhoneType(); - teleInfo.imsi_2 = imsi_2; - teleInfo.imei_2 = imei_2; - teleInfo.phoneType_2 = phoneType_2; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getSpreadtrumTeleInfo"); - } - return teleInfo; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/PollingUtils.java b/DevLibUtils/src/main/java/dev/utils/app/PollingUtils.java deleted file mode 100644 index 66fc5cf046..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/PollingUtils.java +++ /dev/null @@ -1,60 +0,0 @@ -package dev.utils.app; - -import android.annotation.TargetApi; -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.SystemClock; - -/** - * detail: 轮询工具类 - * @author MaTianyu - */ -public final class PollingUtils { - - private PollingUtils(){ - } - - /** - * 开启轮询 - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static void startPolling(Context context, int mills, PendingIntent pendingIntent) { - AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - manager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), mills, pendingIntent); - } - - /** - * 停止轮询 - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static void stopPolling(Context context, PendingIntent pendingIntent) { - AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - manager.cancel(pendingIntent); - } - - /** - * 开启轮询服务 - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static void startPollingService(Context context, int mills, Class cls, String action) { - Intent intent = new Intent(context, cls); - intent.setAction(action); - PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - startPolling(context, mills, pendingIntent); - } - - /** - * 停止启轮询服务 - */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static void stopPollingService(Context context, Class cls, String action) { - Intent intent = new Intent(context, cls); - intent.setAction(action); - PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - stopPolling(context, pendingIntent); - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/PowerManagerUtils.java b/DevLibUtils/src/main/java/dev/utils/app/PowerManagerUtils.java deleted file mode 100644 index 6fc0249400..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/PowerManagerUtils.java +++ /dev/null @@ -1,128 +0,0 @@ -package dev.utils.app; - -import android.app.Activity; -import android.content.Context; -import android.os.Build; -import android.os.PowerManager; -import android.view.Window; -import android.view.WindowManager; - -import dev.DevUtils; - -/** - * detail: 电源管理工具类 - * Created by Ttt - * - */ -public final class PowerManagerUtils { - - // PowerManagerUtils 实例 - private static PowerManagerUtils INSTANCE; - - /** 获取 PowerManagerUtils 实例 ,单例模式 */ - public static PowerManagerUtils getInstance() { - if (INSTANCE == null){ - INSTANCE = new PowerManagerUtils(); - } - return INSTANCE; - } - - /** 电源管理类 */ - PowerManager powerManager; - // 电源管理锁 - PowerManager.WakeLock wakeLock; - - /** 构造函数 */ - private PowerManagerUtils() { - // 获取系统服务 - powerManager = (PowerManager) DevUtils.getContext().getSystemService(Context.POWER_SERVICE); - // 电源管理锁 - wakeLock = powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.FULL_WAKE_LOCK, "PowerManagerUtils"); - } - - /** - * 屏幕是否打开(亮屏) - * @return - */ - public boolean isScreenOn() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR_MR1) { - return false; - } else { - return powerManager.isScreenOn(); - } - } - - /** - * 唤醒屏幕/点亮亮屏 - */ - public void turnScreenOn() { - if (wakeLock != null && !wakeLock.isHeld()) { - wakeLock.acquire(); - } - } - - /** - * 释放屏幕锁, 允许休眠时间自动黑屏 - */ - public void turnScreenOff() { - if (wakeLock != null && wakeLock.isHeld()) { - try { - wakeLock.release(); - } catch (Exception e) { - } - } - } - - public PowerManager.WakeLock getWakeLock() { - return wakeLock; - } - - public void setWakeLock(PowerManager.WakeLock wakeLock) { - this.wakeLock = wakeLock; - } - - public PowerManager getPowerManager() { - return powerManager; - } - - public void setPowerManager(PowerManager powerManager) { - this.powerManager = powerManager; - } - - /** - * 设置屏幕常亮 - * @param activity - */ - public static void setBright(Activity activity){ - if (activity != null){ - setBright(activity.getWindow()); - } - } - - /** - * 设置屏幕常亮 - * @param window {@link Activity#getWindow()} - */ - public static void setBright(Window window){ - if (window != null) { - window.setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - } - - /** - * 设置WakeLock 常亮 - * @return - * run: {@link Activity#onResume()} - */ - public static PowerManager.WakeLock setWakeLockToBright(){ - // onResume() - PowerManager.WakeLock mWakeLock = PowerManagerUtils.getInstance().getPowerManager().newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "setWakeLockToBright"); - mWakeLock.acquire(); // 常量, 持有不黑屏 - -// // onPause() -// if (mWakeLock != null){ -// mWakeLock.release(); // 释放资源, 到休眠时间自动黑屏 -// } - return mWakeLock; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/ProcessUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ProcessUtils.java deleted file mode 100644 index d41f53bf66..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ProcessUtils.java +++ /dev/null @@ -1,209 +0,0 @@ -package dev.utils.app; - -import android.annotation.SuppressLint; -import android.app.ActivityManager; -import android.app.usage.UsageStats; -import android.app.usage.UsageStatsManager; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.provider.Settings; -import android.support.annotation.NonNull; -import android.text.TextUtils; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 进程相关工具类 - * Created by Ttt - */ -public final class ProcessUtils { - - private ProcessUtils() { - } - - // 日志TAG - private static final String TAG = ProcessUtils.class.getSimpleName(); - - /** - * 获取进程号对应的进程名 - * @param pid 进程号 => android.os.Process.myPid() - * @return 进程名 - */ - public static String getProcessName(int pid) { - BufferedReader reader = null; - try { - reader = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline")); - String processName = reader.readLine(); - if (!TextUtils.isEmpty(processName)) { - processName = processName.trim(); - } - return processName; - } catch (Throwable throwable) { - LogPrintUtils.eTag(TAG, throwable, "getProcessName"); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - } - } - } - return null; - } - - /** - * 获得当前进程的名字 - * hit: 获取当前进程 DevUtils.getContext().getApplicationInfo().packageName - * @return 进程号 - */ - public static String getCurProcessName() { - int pid = android.os.Process.myPid(); - ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); - for (ActivityManager.RunningAppProcessInfo appProcess : activityManager.getRunningAppProcesses()) { - if (appProcess.pid == pid) { - return appProcess.processName; - } - } - return null; - } - - /** - * 获取前台线程包名 - * => 属于系统权限 - * @return 前台应用包名 - */ - public static String getForegroundProcessName() { - if (DevUtils.getContext() == null){ - return null; - } - ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); - if (activityManager == null) return null; - List listInfos = activityManager.getRunningAppProcesses(); - if (listInfos != null && listInfos.size() > 0) { - for (ActivityManager.RunningAppProcessInfo apInfo : listInfos) { - if (apInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { - return apInfo.processName; - } - } - } - // SDK 大于 21 时 - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP) { - PackageManager packageManager = DevUtils.getContext().getPackageManager(); - Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); - List listResolves = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - // 无权限 - if (listResolves.size() <= 0) { - return null; - } - try { - UsageStatsManager usageStatsManager = (UsageStatsManager) DevUtils.getContext().getSystemService(Context.USAGE_STATS_SERVICE); - List listUsageStats = null; - if (usageStatsManager != null) { - long endTime = System.currentTimeMillis(); - long beginTime = endTime - 86400000 * 7; - listUsageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, beginTime, endTime); - } - if (listUsageStats == null || listUsageStats.isEmpty()) return null; - UsageStats recentStats = null; - for (UsageStats usageStats : listUsageStats) { - if (recentStats == null || usageStats.getLastTimeUsed() > recentStats.getLastTimeUsed()) { - recentStats = usageStats; - } - } - return recentStats == null ? null : recentStats.getPackageName(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getForegroundProcessName"); - } - } - return null; - } - - /** - * 获取后台服务进程 - * - * @return 后台服务进程 - */ - public static Set getAllBackgroundProcesses() { - if (DevUtils.getContext() == null){ - return Collections.emptySet(); - } - ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); - if (activityManager == null) return Collections.emptySet(); - List listInfos = activityManager.getRunningAppProcesses(); - Set set = new HashSet<>(); - if (listInfos != null) { - for (ActivityManager.RunningAppProcessInfo apInfo : listInfos) { - Collections.addAll(set, apInfo.pkgList); - } - } - return set; - } - - /** - * 杀死所有的后台服务进程 - * - * @return 被暂时杀死的服务集合 - */ - @SuppressLint("MissingPermission") - public static Set killAllBackgroundProcesses() { - if (DevUtils.getContext() == null){ - return null; - } - ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); - if (activityManager == null) return Collections.emptySet(); - List listInfos = activityManager.getRunningAppProcesses(); - Set set = new HashSet<>(); - for (ActivityManager.RunningAppProcessInfo apInfo : listInfos) { - for (String pkg : apInfo.pkgList) { - activityManager.killBackgroundProcesses(pkg); - set.add(pkg); - } - } - listInfos = activityManager.getRunningAppProcesses(); - for (ActivityManager.RunningAppProcessInfo aInfo : listInfos) { - for (String pkg : aInfo.pkgList) { - set.remove(pkg); - } - } - return set; - } - - /** - * 杀死后台服务进程 - * - * @param packageName The name of the package. - * @return true : 杀死成功, false : 杀死失败 - */ - @SuppressLint("MissingPermission") - public static boolean killBackgroundProcesses(@NonNull final String packageName) { - ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); - if (activityManager == null) return false; - List listInfos = activityManager.getRunningAppProcesses(); - if (listInfos == null || listInfos.size() == 0) return true; - for (ActivityManager.RunningAppProcessInfo apInfo : listInfos) { - if (Arrays.asList(apInfo.pkgList).contains(packageName)) { - activityManager.killBackgroundProcesses(packageName); - } - } - listInfos = activityManager.getRunningAppProcesses(); - if (listInfos == null || listInfos.size() == 0) return true; - for (ActivityManager.RunningAppProcessInfo aInfo : listInfos) { - if (Arrays.asList(aInfo.pkgList).contains(packageName)) { - return false; - } - } - return true; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/ResourceUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ResourceUtils.java deleted file mode 100644 index 7a2ba5ca79..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ResourceUtils.java +++ /dev/null @@ -1,498 +0,0 @@ -package dev.utils.app; - -import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; -import dev.utils.R; - -/** - * detail: 资源文件工具类 - * Created by Ttt - */ -public final class ResourceUtils { - - private ResourceUtils() { - } - - // 日志TAG - private static final String TAG = ResourceUtils.class.getSimpleName(); - - public static final String LAYTOUT = "layout"; - public static final String DRAWABLE = "drawable"; - public static final String MIPMAP = "mipmap"; - public static final String MENU = "menu"; - public static final String RAW = "raw"; - public static final String ANIM = "anim"; - public static final String STRING = "string"; - public static final String STYLE = "style"; - public static final String STYLEABLE = "styleable"; - public static final String INTEGER = "integer"; - public static final String ID = "id"; - public static final String DIMEN = "dimen"; - public static final String COLOR = "color"; - public static final String BOOL = "bool"; - public static final String ATTR = "attr"; - - /** - * 获得字符串 - * @param strId 字符串id - * @return 字符串 - */ - public static String getString(int strId) { - try { - return DevUtils.getContext().getResources().getString(strId); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getString"); - } - return ""; - } - - /** - * 获得颜色 - * @param colorId 颜色id - * @return 颜色 - */ - public static int getColor(int colorId) { - try { - return DevUtils.getContext().getResources().getColor(colorId); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getColor"); - } - return -1; - } - - /** - * 获得Drawable - * @param drawableId Drawable的id - * @return Drawable - */ - public static Drawable getDrawable(int drawableId) { - try { - return DevUtils.getContext().getResources().getDrawable(drawableId); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDrawable"); - } - return null; - } - - /** - * 根据资源名获得资源id - * @param name 资源名 - * @param type 资源类型 - * @return 资源id,找不到返回0 - */ - public static int getResourceId(String name, String type) { - try { - //PackageManager pm = DevUtils.getContext().getPackageManager(); - Resources resources = DevUtils.getContext().getResources(); - return resources.getIdentifier(name, type, DevUtils.getContext().getPackageName()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getResourceId"); - } - return 0; - } - - /** - * 获取资源id - * @param imageName - * @return - */ - public static int getDrawableId2(String imageName){ - Class mipmap = R.drawable.class; - try { - Field field = mipmap.getField(imageName); - int resId = field.getInt(imageName); - return resId; - } catch (NoSuchFieldException e) { // 如果没有在"drawable"下找到imageName,将会返回0 - LogPrintUtils.eTag(TAG, e, "getDrawableId2"); - return 0; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDrawableId2"); - return 0; - } - } - - // == - - /** - * 获取 layout 布局文件 - * @param resName layout xml 的文件名 - * @return layout - */ - public static int getLayoutId(String resName) { - try { - return DevUtils.getContext().getResources().getIdentifier(resName, "layout", DevUtils.getContext().getPackageName()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getLayoutId"); - } - return 0; - } - - /** - * 获取 string 值 - * @param resName string name的名称 - * @return string - */ - public static int getStringId(String resName) { - try { - return DevUtils.getContext().getResources().getIdentifier(resName, "string", DevUtils.getContext().getPackageName()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getStringId"); - } - return 0; - } - - /** - * 获取 drawable - * @param resName drawable 的名称 - * @return drawable - */ - public static int getDrawableId(String resName) { - try { - return DevUtils.getContext().getResources().getIdentifier(resName, "drawable", DevUtils.getContext().getPackageName()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getDrawableId"); - } - return 0; - } - - /** - * 获取 mipmap - * @param resName - * @return - */ - public static int getMipmapId(String resName) { - try { - return DevUtils.getContext().getResources().getIdentifier(resName, "mipmap", DevUtils.getContext().getPackageName()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getMipmapId"); - } - return 0; - } - - - /** - * 获取 style - * @param resName style的名称 - * @return style - */ - public static int getStyleId(String resName) { - try { - return DevUtils.getContext().getResources().getIdentifier(resName, "style", DevUtils.getContext().getPackageName()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getStyleId"); - } - return 0; - } - - /** - * 获取 styleable - * @param resName styleable 的名称 - * @return styleable - */ - public static Object getStyleableId(String resName){ - try { - return DevUtils.getContext().getResources().getIdentifier(resName, "styleable", DevUtils.getContext().getPackageName()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getStyleableId"); - } - return 0; - } - - - /** - * 获取 anim - * @param resName anim xml 文件名称 - * @return anim - */ - public static int getAnimId(String resName) { - try { - return DevUtils.getContext().getResources().getIdentifier(resName, "anim", DevUtils.getContext().getPackageName()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getAnimId"); - } - return 0; - } - - /** - * 获取 id - * @param resName id 的名称 - * @return - */ - public static int getId(String resName) { - try { - return DevUtils.getContext().getResources().getIdentifier(resName, "id", DevUtils.getContext().getPackageName()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getId"); - } - return 0; - } - - /** - * color - * @param resName color 名称 - * @return - */ - public static int getColorId(String resName) { - try { - return DevUtils.getContext().getResources().getIdentifier(resName, "color", DevUtils.getContext().getPackageName()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getColorId"); - } - return 0; - } - - // == ----------------------------------------- == - - /** - * 获取 Assets 资源文件数据 - * @param fileName 资源文件名,可分成,如根目录,a.txt 或者子目录 /www/a.html - * @return byte[] , - > new String(byte[],encode) - */ - public static byte[] readBytesFromAssets(String fileName) { - if (DevUtils.getContext() != null && !TextUtils.isEmpty(fileName)) { - InputStream iStream = null; - try { - iStream = DevUtils.getContext().getResources().getAssets().open(fileName); - int length = iStream.available(); - byte[] buffer = new byte[length]; - iStream.read(buffer); - iStream.close(); - iStream = null; - return buffer; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "readBytesFromAssets"); - } finally { - if(iStream != null) { - try { - iStream.close(); - } catch (Exception e) { - } - } - } - } - return null; - } - - /** - * 读取字符串 来自 Assets文件 - * @param fileName - * @return - */ - public static String readStringFromAssets(String fileName) { - try { - return new String(readBytesFromAssets(fileName), "UTF-8"); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "readStringFromAssets"); - } - return null; - } - - /** - * 从res/raw 中获取内容。 - * @param resId 资源id - * @return byte[] , - > new String(byte[],encode) - */ - public static byte[] readBytesFromRaw(int resId) { - if (DevUtils.getContext() != null) { - InputStream iStream = null; - try { - iStream = DevUtils.getContext().getResources().openRawResource(resId); - int length = iStream.available(); - byte[] buffer = new byte[length]; - iStream.read(buffer); - iStream.close(); - iStream = null; - return buffer; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "readBytesFromRaw"); - } finally { - if(iStream != null) { - try { - iStream.close(); - } catch (Exception e) { - } - } - } - } - return null; - } - - /** - * 读取字符串 来自Raw 文件 - * @param resId - * @return - */ - public static String readStringFromRaw(int resId) { - try { - return new String(readBytesFromRaw(resId), "UTF-8"); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "readStringFromRaw"); - } - return null; - } - - // == - - /** - * 获取 Assets 资源文件数据(返回ArrayList 一行的全部内容属于一个索引) - * @param fileName 资源文件名,可分成,如根目录,a.txt 或者子目录 /www/a.html - * @return - */ - public static ArrayList geFileToListFromAssets(String fileName) { - if (DevUtils.getContext() != null && !TextUtils.isEmpty(fileName)) { - InputStream iStream = null; - InputStreamReader inReader = null; - BufferedReader bufReader = null; - try { - iStream = DevUtils.getContext().getResources().getAssets().open(fileName); - inReader = new InputStreamReader(iStream); - bufReader = new BufferedReader(inReader); - ArrayList fileContent = new ArrayList(); - String line; - while ((line = bufReader.readLine()) != null) { - fileContent.add(line); - } - return fileContent; - } catch (IOException e) { - LogPrintUtils.eTag(TAG, e, "geFileToListFromAssets"); - } finally { - try { - bufReader.close(); - inReader.close(); - iStream.close(); - } catch (Exception e) { - } - } - } - return null; - } - - /** - * 从res/raw 中获取内容。(返回ArrayList 一行的全部内容属于一个索引) - * @param resId 资源id - * @return - */ - public static List geFileToListFromRaw(int resId) { - if (DevUtils.getContext() != null) { - InputStream iStream = null; - InputStreamReader inReader = null; - BufferedReader bufReader = null; - try { - iStream = DevUtils.getContext().getResources().openRawResource(resId); - inReader = new InputStreamReader(iStream); - bufReader = new BufferedReader(inReader); - List fileContent = new ArrayList(); - String line = null; - while ((line = bufReader.readLine()) != null) { - fileContent.add(line); - } - return fileContent; - } catch (IOException e) { - LogPrintUtils.eTag(TAG, e, "geFileToListFromRaw"); - } finally { - try { - bufReader.close(); - inReader.close(); - iStream.close(); - } catch (Exception e) { - } - } - } - return null; - } - - - // ======== - /** - * 从Assets 资源中获取内容并保存到本地 - * @param fileName 资源文件名,可分成,如根目录,a.txt 或者子目录 /www/a.html - * @param file 保存地址 - * @return 是否保存成功 - */ - public static boolean saveAssetsFormFile(String fileName, File file) { - if (DevUtils.getContext() != null) { - try { - // 获取 Assets 文件 - InputStream iStream = DevUtils.getContext().getResources().getAssets().open(fileName); - // 存入SDCard - FileOutputStream fileOutputStream = new FileOutputStream(file); - // 设置数据缓冲 - byte[] buffer = new byte[1024]; - // 创建输入输出流 - ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - int len = 0; - while ((len = iStream.read(buffer)) != -1) { - outStream.write(buffer, 0, len); - } - // 保存数据 - byte[] bytes = outStream.toByteArray(); - // 写入保存的文件 - fileOutputStream.write(bytes); - // 关闭流 - outStream.close(); - iStream.close(); - // -- - fileOutputStream.flush(); - fileOutputStream.close(); - return true; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "saveAssetsFormFile"); - } - } - return false; - } - - /** - * 从res/raw 中获取内容并保存到本地 - * @param resId 资源id - * @param file 保存地址 - * @return 是否保存成功 - */ - public static boolean saveRawFormFile(int resId, File file) { - if (DevUtils.getContext() != null) { - try { - // 获取raw 文件 - InputStream iStream = DevUtils.getContext().getResources().openRawResource(resId); - // 存入SDCard - FileOutputStream fileOutputStream = new FileOutputStream(file); - // 设置数据缓冲 - byte[] buffer = new byte[1024]; - // 创建输入输出流 - ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - int len = 0; - while ((len = iStream.read(buffer)) != -1) { - outStream.write(buffer, 0, len); - } - // 保存数据 - byte[] bytes = outStream.toByteArray(); - // 写入保存的文件 - fileOutputStream.write(bytes); - // 关闭流 - outStream.close(); - iStream.close(); - // -- - fileOutputStream.flush(); - fileOutputStream.close(); - return true; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "saveRawFormFile"); - } - } - return false; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/SDCardUtils.java b/DevLibUtils/src/main/java/dev/utils/app/SDCardUtils.java deleted file mode 100644 index ff1f9bddfb..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/SDCardUtils.java +++ /dev/null @@ -1,323 +0,0 @@ -package dev.utils.app; - -import android.content.Context; -import android.os.Environment; -import android.os.StatFs; -import android.os.storage.StorageManager; -import android.text.format.Formatter; - -import java.io.File; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; -import dev.utils.common.FileUtils; - -/** - * detail: SD卡相关辅助类 - * Created by Ttt - */ -public final class SDCardUtils { - - private SDCardUtils() { - } - - // 日志TAG - private static final String TAG = SDCardUtils.class.getSimpleName(); - - // == ----------------------------------------- == - - /** - * 判断SDCard是否正常挂载 - * @return - */ - public static boolean isSDCardEnable() { - // android.os.Environment - return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); - } - - /** - * 获取SD卡路径(File对象) - * @return - */ - public static File getSDCardFile() { - return Environment.getExternalStorageDirectory(); - } - - /** - * 获取SD卡路径(无添加 -> / -> File.separator) - * @return - */ - public static String getSDCardPath() { - return Environment.getExternalStorageDirectory().getAbsolutePath(); - } - - // == - - /** - * 判断 SD 卡是否可用 - * @return true : 可用, false : 不可用 - */ - public static boolean isSDCardEnablePath() { - return !getSDCardPaths().isEmpty(); - } - - /** - * 获取 SD 卡路径 - * @param removable true : 外置 SD 卡, false : 内置 SD 卡 - * @return SD 卡路径 - */ - @SuppressWarnings("TryWithIdenticalCatches") - public static List getSDCardPaths(final boolean removable) { - List listPaths = new ArrayList<>(); - try { - StorageManager storageManager = (StorageManager) DevUtils.getContext().getSystemService(Context.STORAGE_SERVICE); - Class storageVolumeClazz = Class.forName("android.os.storage.StorageVolume"); - Method getVolumeList = StorageManager.class.getMethod("getVolumeList"); - Method getPath = storageVolumeClazz.getMethod("getPath"); - Method isRemovable = storageVolumeClazz.getMethod("isRemovable"); - Object result = getVolumeList.invoke(storageManager); - final int length = Array.getLength(result); - for (int i = 0; i < length; i++) { - Object storageVolumeElement = Array.get(result, i); - String path = (String) getPath.invoke(storageVolumeElement); - boolean res = (Boolean) isRemovable.invoke(storageVolumeElement); - if (removable == res) { - listPaths.add(path); - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getSDCardPaths"); - } - return listPaths; - } - - /** - * 获取 SD 卡路径 - * @return SD 卡路径 - */ - @SuppressWarnings("TryWithIdenticalCatches") - public static List getSDCardPaths() { - List listPaths = new ArrayList<>(); - try { - StorageManager storageManager = (StorageManager) DevUtils.getContext().getSystemService(Context.STORAGE_SERVICE); - Method getVolumePathsMethod = StorageManager.class.getMethod("getVolumePaths"); - getVolumePathsMethod.setAccessible(true); - Object invoke = getVolumePathsMethod.invoke(storageManager); - listPaths = Arrays.asList((String[]) invoke); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getSDCardPaths"); - } - return listPaths; - } - - // == - - /** - * 返回对应路径的空间总大小 - * @param path - * @return - */ - public static long getAllBlockSize(String path) { - try { - // 获取路径的存储空间信息 - StatFs statFs = new StatFs(path); - // 获取单个数据块的大小(Byte) - long blockSize = statFs.getBlockSize(); - // 获取数据块的数量 - long blockCount = statFs.getBlockCount(); - // 返回空间总大小 - return (blockCount * blockSize); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAllBlockSize"); - } - return -1l; - } - - /** - * 返回对应路径的空闲空间(byte 字节大小) - * @param path - * @return - */ - public static long getAvailableBlocks(String path) { - try { - // 获取路径的存储空间信息 - StatFs statFs = new StatFs(path); - // 获取单个数据块的大小(Byte) - long blockSize = statFs.getBlockSize(); - // 空闲的数据块的数量 - long freeBlocks = statFs.getAvailableBlocks(); - // 返回空闲空间 - return (freeBlocks * blockSize); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAvailableBlocks"); - } - return -1l; - } - - - /** - * 返回对应路径,已使用的空间大小 - * @param path - * @return - */ - public static long getAlreadyBlock(String path) { - try { - // 获取路径的存储空间信息 - StatFs statFs = new StatFs(path); - // 获取单个数据块的大小(Byte) - long blockSize = statFs.getBlockSize(); - // 获取数据块的数量 - long blockCount = statFs.getBlockCount(); - // 空闲的数据块的数量 - long freeBlocks = statFs.getAvailableBlocks(); - // 返回空间总大小 - return ((blockCount - freeBlocks)* blockSize); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAlreadyBlock"); - } - return -1l; - } - - /** - * 返回对应路径的空间大小信息 - * @return 返回数据,0 = 总空间大小,1 = 空闲控件大小 , 2 = 已使用空间大小 - */ - public static long[] getBlockSizeInfos(String path) { - try { - // 获取路径的存储空间信息 - StatFs statFs = new StatFs(path); - // 获取单个数据块的大小(Byte) - long blockSize = statFs.getBlockSize(); - // 获取数据块的数量 - long blockCount = statFs.getBlockCount(); - // 空闲的数据块的数量 - long freeBlocks = statFs.getAvailableBlocks(); - // 计算空间大小信息 - long[] blocks = new long[3]; - blocks[0] = blockSize * blockCount; - blocks[1] = blockSize * freeBlocks; - blocks[2] = ((blockCount - freeBlocks)* blockSize); - // 返回空间大小信息 - return blocks; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getBlockSizeInfos"); - } - return null; - } - - /** - * 获得 SD 卡总大小 - * @return - */ - public static String getSDTotalSize() { - try { - File path = Environment.getExternalStorageDirectory(); - StatFs stat = new StatFs(path.getPath()); - long blockSize = stat.getBlockSizeLong(); - long totalBlocks = stat.getBlockCountLong(); - return Formatter.formatFileSize(DevUtils.getContext(), blockSize * totalBlocks); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getSDTotalSize"); - } - return "unknown"; - } - - /** - * 获得 SD 卡剩余容量,即可用大小 - * @return - */ - public static String getSDAvailableSize() { - try { - File path = Environment.getExternalStorageDirectory(); - StatFs stat = new StatFs(path.getPath()); - long blockSize = stat.getBlockSizeLong(); - long availableBlocks = stat.getAvailableBlocksLong(); - return Formatter.formatFileSize(DevUtils.getContext(), blockSize * availableBlocks); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getSDAvailableSize"); - } - return "unknown"; - } - - /** - * 获得机身内存总大小 - * @return - */ - public static String getRomTotalSize() { - try { - File path = Environment.getDataDirectory(); - StatFs stat = new StatFs(path.getPath()); - long blockSize = stat.getBlockSizeLong(); - long totalBlocks = stat.getBlockCountLong(); - return Formatter.formatFileSize(DevUtils.getContext(), blockSize * totalBlocks); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getRomTotalSize"); - } - return "unknown"; - } - - /** - * 获得机身可用内存 - * @return - */ - public static String getRomAvailableSize() { - try { - File path = Environment.getDataDirectory(); - StatFs stat = new StatFs(path.getPath()); - long blockSize = stat.getBlockSizeLong(); - long availableBlocks = stat.getAvailableBlocksLong(); - return Formatter.formatFileSize(DevUtils.getContext(), blockSize * availableBlocks); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getRomAvailableSize"); - } - return "unknown"; - } - - // - - - /** - * 获取缓存地址 - * @return - */ - public static String getDiskCacheDir() { - String cachePath; - if (isSDCardEnable()) { // 判断SDCard是否挂载 - cachePath = DevUtils.getContext().getExternalCacheDir().getPath(); - } else { - cachePath = DevUtils.getContext().getCacheDir().getPath(); - } - // 防止不存在目录文件,自动创建 - FileUtils.createFolder(cachePath); - // 返回文件存储地址 - return cachePath; - } - - /** - * 获取缓存资源地址 - * @param fPath 文件地址 - * @return - */ - public static File getCacheFile(String fPath) { - return new File(getCachePath(fPath)); - } - - /** - * 获取缓存资源地址 - * @param fPath 文件地址 - * @return - */ - public static String getCachePath(String fPath){ - // 获取缓存地址 - String cachePath = new File(getDiskCacheDir(), fPath).getAbsolutePath(); - // 防止不存在目录文件,自动创建 - FileUtils.createFolder(cachePath); - // 返回头像地址 - return cachePath; - } -} - diff --git a/DevLibUtils/src/main/java/dev/utils/app/ScreenUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ScreenUtils.java deleted file mode 100644 index d711d6e1ae..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ScreenUtils.java +++ /dev/null @@ -1,729 +0,0 @@ -package dev.utils.app; - -import android.app.Activity; -import android.app.KeyguardManager; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.Build; -import android.provider.Settings; -import android.support.annotation.NonNull; -import android.util.DisplayMetrics; -import android.view.Surface; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; - -import java.lang.reflect.Method; -import java.text.DecimalFormat; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 屏幕相关工具类 - * Created by Ttt - */ -public final class ScreenUtils { - - private ScreenUtils() { - } - - // 日志TAG - private static final String TAG = ScreenUtils.class.getSimpleName(); - - /** - * 通过上下文获取 DisplayMetrics (获取关于显示的通用信息,如显示大小,分辨率和字体) - * @return - */ - private static DisplayMetrics getDisplayMetrics() { - try { - WindowManager wManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); - if (wManager != null) { - DisplayMetrics dMetrics = new DisplayMetrics(); - wManager.getDefaultDisplay().getMetrics(dMetrics); - return dMetrics; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDisplayMetrics"); - } - return null; - } - - // == ----------------------------------------- == - -// /** -// * 通过上下文获取屏幕宽度 -// * @param mContext -// * @return -// */ -// @SuppressWarnings("deprecation") -// public static int getScreenWidth() { -// try { -// // 获取屏幕信息 -// DisplayMetrics dMetrics = getDisplayMetrics(); -// if (dMetrics != null) { -// return dMetrics.widthPixels; -// } -// // 这种也可以获取,不过已经提问过时(下面这段可以注释掉) -// WindowManager wManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); -// if (wManager != null) { -// return wManager.getDefaultDisplay().getWidth(); -// } -// } catch (Exception e) { -// LogPrintUtils.eTag(TAG, e, "getScreenWidth"); -// } -// return -1; -// } -// -// /** -// * 通过上下文获取屏幕高度 -// * @param mContext -// * @return -// */ -// @SuppressWarnings("deprecation") -// public static int getScreenHeight() { -// try { -// // 获取屏幕信息 -// DisplayMetrics dMetrics = getDisplayMetrics(); -// if (dMetrics != null) { -// return dMetrics.heightPixels; -// } -// // 这种也可以获取,不过已经提示过时(下面这段可以注释掉) -// WindowManager wManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); -// if (wManager != null) { -// return wManager.getDefaultDisplay().getHeight(); -// } -// } catch (Exception e) { -// LogPrintUtils.eTag(TAG, e, "getScreenHeight"); -// } -// return -1; -// } -// -// /** -// * 通过上下文获取屏幕宽度高度 -// * @param mContext -// * @return int[] 0 = 宽度,1 = 高度 -// */ -// @SuppressWarnings("deprecation") -// public static int[] getScreenWidthHeight() { -// try { -// // 获取屏幕信息 -// DisplayMetrics dMetrics = getDisplayMetrics(); -// if (dMetrics != null) { -// return new int[] { dMetrics.widthPixels, dMetrics.heightPixels }; -// } -// // 这种也可以获取,不过已经提示过时(下面这段可以注释掉) -// WindowManager wManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); -// if (wManager != null) { -// int width = wManager.getDefaultDisplay().getWidth(); -// int height = wManager.getDefaultDisplay().getHeight(); -// return new int[] { width, height }; -// } -// } catch (Exception e) { -// LogPrintUtils.eTag(TAG, e, "getScreenWidthHeight"); -// } -// return null; -// } - - /** - * 获取屏幕的宽度(单位:px) - * @return 屏幕宽 - */ - public static int getScreenWidth() { - try { - WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); - if (windowManager == null) { - return DevUtils.getContext().getResources().getDisplayMetrics().widthPixels; - } - Point point = new Point(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - windowManager.getDefaultDisplay().getRealSize(point); - } else { - windowManager.getDefaultDisplay().getSize(point); - } - return point.x; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getScreenWidth"); - } - return -1; - } - - /** - * 获取屏幕的高度(单位:px) - * @return 屏幕高 - */ - public static int getScreenHeight() { - try { - WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); - if (windowManager == null) { - return DevUtils.getContext().getResources().getDisplayMetrics().heightPixels; - } - Point point = new Point(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - windowManager.getDefaultDisplay().getRealSize(point); - } else { - windowManager.getDefaultDisplay().getSize(point); - } - return point.y; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getScreenHeight"); - } - return -1; - } - - /** - * 通过上下文获取屏幕宽度高度 - * @return point.x 宽, point.y 高 - */ - public static Point getScreenWidthHeightToPoint() { - try { - WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); - if (windowManager == null) { - DisplayMetrics dMetrics = DevUtils.getContext().getResources().getDisplayMetrics(); - return new Point(dMetrics.widthPixels, dMetrics.heightPixels); - } - Point point = new Point(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - windowManager.getDefaultDisplay().getRealSize(point); - } else { - windowManager.getDefaultDisplay().getSize(point); - } - return point; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getScreenWidthHeightToPoint"); - } - return null; - } - - /** - * 通过上下文获取屏幕宽度高度 - * @return int[] 0 = 宽度,1 = 高度 - */ - public static int[] getScreenWidthHeight() { - try { - WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); - if (windowManager == null) { - DisplayMetrics dMetrics = DevUtils.getContext().getResources().getDisplayMetrics(); - return new int[] { dMetrics.widthPixels, dMetrics.heightPixels }; - } - Point point = new Point(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - windowManager.getDefaultDisplay().getRealSize(point); - } else { - windowManager.getDefaultDisplay().getSize(point); - } - return new int[] { point.x, point.y }; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getScreenWidthHeight"); - } - return new int[] { 0, 0 }; - } - - /** - * 获取屏幕分辨率 - * @return - */ - public static String getScreenSize() { - try { - // 获取分辨率 - int[] whArys = getScreenWidthHeight(); - // 返回分辨率信息 - return whArys[1] + "x" + whArys[0]; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getScreenSize"); - } - return "unknown"; - } - - /** - * 获取屏幕英寸 例5.5英寸 - * @return - */ - public static String getScreenSizeOfDevice() { - // https://blog.csdn.net/lincyang/article/details/42679589 - try { - Point point = new Point(); - WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); - windowManager.getDefaultDisplay().getRealSize(point); - DisplayMetrics dm = DevUtils.getContext().getResources().getDisplayMetrics(); - double x = Math.pow(point.x/ dm.xdpi, 2); - double y = Math.pow(point.y / dm.ydpi, 2); - double screenInches = Math.sqrt(x + y); - // 转换大小 - DecimalFormat df = new DecimalFormat("#.0"); - return df.format(screenInches); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getScreenSizeOfDevice"); - } - return "unknown"; - } - - // == - - /** - * 通过上下文获取屏幕密度 - * @return - */ - public static float getDensity() { - try { - // 获取屏幕信息 - DisplayMetrics dMetrics = getDisplayMetrics(); - if (dMetrics != null) { - // 屏幕密度(0.75 / 1.0 / 1.5 / 2.0) - return dMetrics.density; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDensity"); - } - return -1; - } - - /** - * 通过上下文获取屏幕密度Dpi - * @return - */ - public static int getDensityDpi() { - try { - // 获取屏幕信息 - DisplayMetrics dMetrics = getDisplayMetrics(); - if (dMetrics != null) { - // 屏幕密度DPI(120 / 160 / 240 / 320) - return dMetrics.densityDpi; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDensityDpi"); - } - return -1; - } - - /** - * 通过上下文获取屏幕缩放密度 - * @return - */ - public static float getScaledDensity() { - try { - // 获取屏幕信息 - DisplayMetrics dMetrics = getDisplayMetrics(); - if (dMetrics != null) { - return dMetrics.scaledDensity; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getScaledDensity"); - } - return -1f; - } - - /** - * 获取 X轴 dpi - * @return - */ - public static float getXDpi() { - try { - // 获取屏幕信息 - DisplayMetrics dMetrics = getDisplayMetrics(); - if (dMetrics != null) { - return dMetrics.xdpi; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getXDpi"); - } - return -1f; - } - - /** - * 获取 Y轴 dpi - * @return - */ - public static float getYDpi() { - try { - // 获取屏幕信息 - DisplayMetrics dMetrics = getDisplayMetrics(); - if (dMetrics != null) { - return dMetrics.ydpi; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getYDpi"); - } - return -1f; - } - - /** - * 获取 宽度比例 dpi 基准 - * @return - */ - public static float getWidthDpi() { - try { - // 获取屏幕信息 - DisplayMetrics dMetrics = getDisplayMetrics(); - if (dMetrics != null) { - return dMetrics.widthPixels / dMetrics.density; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getWidthDpi"); - } - return -1f; - } - - /** - * 获取 高度比例 dpi 基准 - * @return - */ - public static float getHeightDpi() { - try { - // 获取屏幕信息 - DisplayMetrics dMetrics = getDisplayMetrics(); - if (dMetrics != null) { - return dMetrics.heightPixels / dMetrics.density; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getHeightDpi"); - } - return -1f; - } - - /** - * 获取屏幕信息 - * @return - */ - public static String getScreenInfo(){ - StringBuilder sBuilder = new StringBuilder(); - // 获取屏幕信息 - DisplayMetrics dMetrics = getDisplayMetrics(); - if (dMetrics != null) { - try { - int heightPixels = dMetrics.heightPixels; - int widthPixels = dMetrics.widthPixels; - - float xdpi = dMetrics.xdpi; - float ydpi = dMetrics.ydpi; - int densityDpi = dMetrics.densityDpi; - - float density = dMetrics.density; - float scaledDensity = dMetrics.scaledDensity; - - float heightDpi = heightPixels / density; - float widthDpi = widthPixels / density; - // - - sBuilder.append("\nheightPixels: " + heightPixels + "px"); - sBuilder.append("\nwidthPixels: " + widthPixels + "px"); - - sBuilder.append("\nxdpi: " + xdpi + "dip"); - sBuilder.append("\nydpi: " + ydpi + "dpi"); - sBuilder.append("\ndensityDpi: " + densityDpi + "dpi"); - - sBuilder.append("\ndensity: " + density); - sBuilder.append("\nscaledDensity: " + scaledDensity); - - sBuilder.append("\nheightDpi: " + heightDpi + "dpi"); - sBuilder.append("\nwidthDpi: " + widthDpi + "dpi"); - - return sBuilder.toString(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getScreenInfo"); - } - } - return sBuilder.toString(); - } - - // == - - /** - * 设置屏幕为全屏 - * @param activity - */ - public static void setFullScreen(@NonNull final Activity activity) { - try { - activity.requestWindowFeature(Window.FEATURE_NO_TITLE);// 隐藏标题 - activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "setFullScreen"); - } - } - - /** - * 设置屏幕为横屏 - * 还有一种就是在 Activity 中加属性 android:screenOrientation="landscape" - * 不设置 Activity 的 android:configChanges 时, - * 切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次 - * 设置 Activity 的 android:configChanges="orientation"时, - * 切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次 - * 设置 Activity 的 android:configChanges="orientation|keyboardHidden|screenSize" - * (4.0 以上必须带最后一个参数)时 - * 切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法 - * @param activity - */ - public static void setLandscape(@NonNull final Activity activity) { - try { - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setLandscape"); - } - } - - /** - * 设置屏幕为竖屏 - * @param activity - */ - public static void setPortrait(@NonNull final Activity activity) { - try { - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setPortrait"); - } - } - - /** - * 判断是否横屏 - * @return true : 是, false : 否 - */ - public static boolean isLandscape() { - try { - return DevUtils.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "isLandscape"); - } - return false; - } - - /** - * 判断是否竖屏 - * @return true : 是, false : 否 - */ - public static boolean isPortrait() { - try { - return DevUtils.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "isPortrait"); - } - return false; - } - - /** - * 获取屏幕旋转角度 - * @param activity - * @return 屏幕旋转角度 - */ - public static int getScreenRotation(@NonNull final Activity activity) { - try { - switch (activity.getWindowManager().getDefaultDisplay().getRotation()) { - case Surface.ROTATION_0: - return 0; - case Surface.ROTATION_90: - return 90; - case Surface.ROTATION_180: - return 180; - case Surface.ROTATION_270: - return 270; - default: - return 0; - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getScreenRotation"); - } - return 0; - } - - /** - * 判断是否锁屏 - * @return true : 是, false : 否 - */ - public static boolean isScreenLock() { - try { - KeyguardManager keyguardManager = (KeyguardManager) DevUtils.getContext().getSystemService(Context.KEYGUARD_SERVICE); - return keyguardManager != null && keyguardManager.inKeyguardRestrictedInputMode(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "isScreenLock"); - } - return false; - } - - /** - * 判断是否是平板 - * @return true : 是, false : 否 - */ - public static boolean isTablet() { - try { - return (DevUtils.getContext().getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "isTablet"); - } - return false; - } - - // == ----------------------------------------- == - - /** - * 获得状态栏的高度(无关 android:theme 获取状态栏高度) - * @return - */ - public static int getStatusHeight() { - try { - Class clazz = Class.forName("com.android.internal.R$dimen"); - Object object = clazz.newInstance(); - int height = Integer.parseInt(clazz.getField("status_bar_height").get(object).toString()); - return DevUtils.getContext().getResources().getDimensionPixelSize(height); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getStatusHeight"); - } - return -1; - } - - /** - * 获取应用区域 TitleBar 高度 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) - * @param activity - * @return - */ - public static int getStatusBarHeight(Activity activity) { - try { - Rect rect = new Rect(); - activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); - return rect.top; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getStatusBarHeight"); - } - return -1; - } - - /** - * 设置进入休眠时长 - 需添加权限 - * @param duration 时长 - */ - public static void setSleepDuration(final int duration) { - try { - Settings.System.putInt(DevUtils.getContext().getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT, duration); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setSleepDuration"); - } - } - - /** - * 获取进入休眠时长 - * @return 进入休眠时长,报错返回-123 - */ - public static int getSleepDuration() { - try { - return Settings.System.getInt(DevUtils.getContext().getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getSleepDuration"); - return -1; - } - } - - // == 截图(有用) == - - // https://www.cnblogs.com/angel88/p/7933437.html - - // https://github.com/weizongwei5/AndroidScreenShot_SysApi - - /** - * 获取当前屏幕截图,包含状态栏 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) - * @param activity - * @return - */ - public static Bitmap snapShotWithStatusBar(Activity activity) { - try { - View view = activity.getWindow().getDecorView(); - view.setDrawingCacheEnabled(true); - view.buildDrawingCache(); - Bitmap bmp = view.getDrawingCache(); - int[] sParams = getScreenWidthHeight(); - - // 主要是 getWindowVisibleDisplayFrame 内容才不为null - Rect frame = new Rect(); - activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); - // 创建新的图片 - Bitmap bitmap = Bitmap.createBitmap(bmp, 0, 0, sParams[0], sParams[1]); - view.destroyDrawingCache(); - return bitmap; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "snapShotWithStatusBar"); - } - return null; - } - - /** - * 获取当前屏幕截图,不包含状态栏 (如果 android:theme 全屏了,则截图无状态栏) - * @param activity - * @return - */ - public static Bitmap snapShotWithoutStatusBar(Activity activity) { - View view = activity.getWindow().getDecorView(); - view.setDrawingCacheEnabled(true); - view.buildDrawingCache(); - Bitmap bmp = view.getDrawingCache(); - int[] sParams = getScreenWidthHeight(); - - // 获取状态栏高度 - int statusBarHeight = getStatusBarHeight(activity); - if(statusBarHeight == -1) { - statusBarHeight = 0; - } - - Rect frame = new Rect(); - activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); - // 创建新的图片 - Bitmap bitmap = Bitmap.createBitmap(bmp, 0, statusBarHeight, sParams[0], sParams[1] - statusBarHeight); - view.destroyDrawingCache(); - return bitmap; - } - - // == - - /** - * 获取底部导航栏高度 - * @return - */ - public static int getNavigationBarHeight() { - int navigationBarHeight = 0; - try { - Resources resources = DevUtils.getContext().getResources(); - // 获取对应方向字符串 - String orientation = resources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape"; - // 获取对应的id - int resourceId = resources.getIdentifier(orientation, "dimen", "android"); - if (resourceId > 0 && checkDeviceHasNavigationBar()) { - navigationBarHeight = resources.getDimensionPixelSize(resourceId); - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getNavigationBarHeight"); - } - return navigationBarHeight; - } - - /** - * 检测是否具有底部导航栏 - * hint: 一加手机上判断不准确 - * @return - */ - public static boolean checkDeviceHasNavigationBar() { - boolean hasNavigationBar = false; - try { - Resources resources = DevUtils.getContext().getResources(); - int id = resources.getIdentifier("config_showNavigationBar", "bool", "android"); - if (id > 0) { - hasNavigationBar = resources.getBoolean(id); - } - try { - Class systemPropertiesClass = Class.forName("android.os.SystemProperties"); - Method m = systemPropertiesClass.getMethod("get", String.class); - String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys"); - if ("1".equals(navBarOverride)) { - hasNavigationBar = false; - } else if ("0".equals(navBarOverride)) { - hasNavigationBar = true; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "checkDeviceHasNavigationBar - SystemProperties"); - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "checkDeviceHasNavigationBar"); - } - return hasNavigationBar; - } -} - diff --git a/DevLibUtils/src/main/java/dev/utils/app/ShapeUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ShapeUtils.java deleted file mode 100644 index dec8114091..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ShapeUtils.java +++ /dev/null @@ -1,430 +0,0 @@ -package dev.utils.app; - -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.drawable.GradientDrawable; -import android.os.Build; -import android.support.annotation.ColorInt; -import android.support.annotation.ColorRes; -import android.support.annotation.RequiresApi; -import android.support.v4.content.ContextCompat; -import android.text.TextUtils; -import android.view.View; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: Shape 工具类 - * Created by Ttt - * https://blog.csdn.net/tanghongchang123/article/details/80283686 - * https://www.cnblogs.com/popfisher/p/5606690.html - * https://www.cnblogs.com/dongdong230/p/4183079.html - * https://www.cnblogs.com/zhongle/archive/2012/08/28/2659902.html - * https://www.2cto.com/kf/201601/456024.html - */ -public final class ShapeUtils { - - // 日志TAG - private static final String TAG = ShapeUtils.class.getSimpleName(); - - private final GradientDrawable drawable; - - private ShapeUtils(Builder builder) { - drawable = builder.gradientDrawable; - } - - public GradientDrawable getDrawable() { - return drawable; - } - - public void setDrawable(View view){ - if (view != null){ - view.setBackground(drawable); - } - } - - // == - - /** - * detail: 构造者模式 - * Created by Ttt - */ - public final static class Builder { - - private GradientDrawable gradientDrawable = new GradientDrawable(); - - public Builder() { - } - - public Builder(GradientDrawable drawable){ - if (drawable != null){ - this.gradientDrawable = drawable; - } - } - - /** - * 获取 Shape 工具类 - * @return - */ - public ShapeUtils build(){ - return new ShapeUtils(this); - } - - // ==== - - // = 设置圆角 = - - /** - * 设置圆角 - * @param radius - * @return - */ - public Builder setRadius(float radius){ - if (gradientDrawable != null){ - gradientDrawable.setCornerRadius(radius); - } - return this; - } - - // == 设置左边 == - - /** - * 设置圆角 - * @param left - * @return - */ - public Builder setRadiusLeft(float left){ - setCornerRadii(left, 0, 0, left); - return this; - } - - /** - * 设置圆角 - * @param leftTop - * @param leftBottom - * @return - */ - public Builder setRadiusLeft(float leftTop, float leftBottom){ - setCornerRadii(leftTop, 0, 0, leftBottom); - return this; - } - - // == 设置右边 == - - /** - * 设置圆角 - * @param right - * @return - */ - public Builder setRadiusRight(float right){ - setCornerRadii(0, right, right, 0); - return this; - } - - /** - * 设置圆角 - * @param rightTop - * @param rightBottom - * @return - */ - public Builder setRadiusRight(float rightTop, float rightBottom){ - setCornerRadii(0, rightTop, rightBottom, 0); - return this; - } - - // == 圆角内部处理 == - - /** - * 内部处理方法 - * @param leftTop - * @param rightTop - * @param rightBottom - * @param leftBottom - */ - public Builder setCornerRadii(float leftTop, float rightTop, float rightBottom, float leftBottom) { -// - - if (gradientDrawable != null){ - // radii 数组分别指定四个圆角的半径,每个角可以指定[X_Radius,Y_Radius],四个圆角的顺序为左上,右上,右下,左下。如果X_Radius,Y_Radius为0表示还是直角。 - gradientDrawable.setCornerRadii(new float[] { leftTop, leftTop, rightTop, rightTop, rightBottom, rightBottom, leftBottom, leftBottom }); - } - return this; - } - - // == 设置背景色(填充) == - -// - - /** - * 设置背景色(填充铺满) - * @param color - * @return - */ - public Builder setColor(String color){ - if (gradientDrawable != null && !TextUtils.isEmpty(color)){ - try { - gradientDrawable.setColor(Color.parseColor(color)); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setColor"); - } - } - return this; - } - - /** - * 设置背景色(填充铺满) - * @param color - * @return - */ - public Builder setColor(@ColorRes int color){ - if (gradientDrawable != null){ - try { - gradientDrawable.setColor(ContextCompat.getColor(DevUtils.getContext(), color)); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setColor"); - } - } - return this; - } - - // == 设置边框颜色 == - -// - - /** - * 设置大小 - * @param width - * @param height - * @return - */ - public Builder setSize(int width, int height){ - if (gradientDrawable != null){ - try { - gradientDrawable.setSize(width, height); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setSize"); - } - } - return this; - } - - // == 设置边距 == - -// /** -// * 设置边距 -// * @param padding -// * @return -// */ -// public Builder setPadding(int padding){ -// if (gradientDrawable != null){ -// try { -// Rect rect = new Rect(); -// rect.left = padding; -// rect.top = padding; -// rect.right = padding; -// rect.bottom = padding; -// gradientDrawable.getPadding(rect); -// } catch (Exception e){ -// LogPrintUtils.eTag(TAG, e, "setPadding"); -// } -// } -// return this; -// } - - // == 设置渐变 == - -// -// - -// switch (angle) { -// case 0: -// st.mOrientation = Orientation.LEFT_RIGHT; -// break; -// case 45: -// st.mOrientation = Orientation.BL_TR; -// break; -// case 90: -// st.mOrientation = Orientation.BOTTOM_TOP; -// break; -// case 135: -// st.mOrientation = Orientation.BR_TL; -// break; -// case 180: -// st.mOrientation = Orientation.RIGHT_LEFT; -// break; -// case 225: -// st.mOrientation = Orientation.TR_BL; -// break; -// case 270: -// st.mOrientation = Orientation.TOP_BOTTOM; -// break; -// case 315: -// st.mOrientation = Orientation.TL_BR; -// break; -// } - - /** - * 设置渐变颜色 - * @param colors - */ - public Builder(@ColorInt int[] colors){ - this(new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors)); - } - - /** - * 设置渐变颜色 - * @param orientation - * @param colors - */ - public Builder(GradientDrawable.Orientation orientation, @ColorInt int[] colors){ - this(new GradientDrawable(orientation, colors)); - } - } - - // == 快捷方法 == - - /** - * 创建新的 Shape Builder 对象 - * @param radius - * @param color - * @return - */ - public static Builder newBuilder(float radius, @ColorRes int color){ - return new Builder().setRadius(radius).setColor(color); - } - - /** - * 创建新的 Shape Builder 对象 - * @param left 通用左上, 左下 - * @param color - * @return - */ - public static Builder newBuilderToLeft(float left, @ColorRes int color){ - return new Builder().setRadiusLeft(left).setColor(color); - } - - /** - * 创建新的 Shape Builder 对象 - * @param right 通用右上, 左下 - * @param color - * @return - */ - public static Builder newBuilderToRight(float right, @ColorRes int color){ - return new Builder().setRadiusRight(right).setColor(color); - } - - /** - * 创建渐变的 Shape Builder 对象 - * @param colors - * @return - */ - public static Builder newBuilderToGradient(@ColorInt int[] colors){ - return new Builder(colors); - } - - /** - * 创建渐变的 Shape Builder 对象 - * @param orientation - * @param colors - * @return - */ - public static Builder newBuilderToGradient(GradientDrawable.Orientation orientation, @ColorInt int[] colors){ - return new Builder(orientation, colors); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/ShellUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ShellUtils.java deleted file mode 100644 index a188d94de8..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ShellUtils.java +++ /dev/null @@ -1,164 +0,0 @@ -package dev.utils.app; - -import java.io.BufferedReader; -import java.io.DataOutputStream; -import java.io.InputStreamReader; -import java.util.List; - -import dev.utils.LogPrintUtils; - -/** - * detail: Shell 相关工具类 - * Created by Blankj - * http://blankj.com - * Update to Ttt - */ -public final class ShellUtils { - - private ShellUtils() { - } - - // 日志TAG - private static final String TAG = ShellUtils.class.getSimpleName(); - - // 换行符 - private static final String LINE_SEP = System.getProperty("line.separator"); - - /** - * 是否是在 root 下执行命令 - * @param command 命令 - * @param isRoot 是否需要 root 权限执行 - * @return CommandResult - */ - public static CommandResult execCmd(final String command, final boolean isRoot) { - return execCmd(new String[]{ command }, isRoot, true); - } - - /** - * 是否是在 root 下执行命令 - * @param commands 多条命令链表 - * @param isRoot 是否需要 root 权限执行 - * @return CommandResult - */ - public static CommandResult execCmd(final List commands, final boolean isRoot) { - return execCmd(commands == null ? null : commands.toArray(new String[]{}), isRoot, true); - } - - /** - * 是否是在 root 下执行命令 - * @param commands 多条命令数组 - * @param isRoot 是否需要 root 权限执行 - * @return CommandResult - */ - public static CommandResult execCmd(final String[] commands, final boolean isRoot) { - return execCmd(commands, isRoot, true); - } - - /** - * 是否是在 root 下执行命令 - * @param command 命令 - * @param isRoot 是否需要 root 权限执行 - * @param isNeedResultMsg 是否需要结果消息 - * @return CommandResult - */ - public static CommandResult execCmd(final String command, final boolean isRoot, final boolean isNeedResultMsg) { - return execCmd(new String[]{ command }, isRoot, isNeedResultMsg); - } - - /** - * 是否是在 root 下执行命令 - * @param commands 命令链表 - * @param isRoot 是否需要 root 权限执行 - * @param isNeedResultMsg 是否需要结果消息 - * @return CommandResult - */ - public static CommandResult execCmd(final List commands, final boolean isRoot, final boolean isNeedResultMsg) { - return execCmd(commands == null ? null : commands.toArray(new String[]{}), isRoot, isNeedResultMsg); - } - - /** - * 是否是在 root 下执行命令 - * @param commands 命令数组 - * @param isRoot 是否需要 root 权限执行 - * @param isNeedResultMsg 是否需要结果消息 - * @return CommandResult - */ - public static CommandResult execCmd(final String[] commands, final boolean isRoot, final boolean isNeedResultMsg) { - int result = -1; - if (commands == null || commands.length == 0) { - return new CommandResult(result, null, null); - } - Process process = null; - BufferedReader successResult = null; - BufferedReader errorResult = null; - StringBuilder successMsg = null; - StringBuilder errorMsg = null; - DataOutputStream os = null; - try { - process = Runtime.getRuntime().exec(isRoot ? "su" : "sh"); - os = new DataOutputStream(process.getOutputStream()); - for (String command : commands) { - if (command == null) continue; - os.write(command.getBytes()); - os.writeBytes(LINE_SEP); - os.flush(); - } - os.writeBytes("exit" + LINE_SEP); - os.flush(); - result = process.waitFor(); - if (isNeedResultMsg) { - successMsg = new StringBuilder(); - errorMsg = new StringBuilder(); - successResult = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); - errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8")); - String line; - if ((line = successResult.readLine()) != null) { - successMsg.append(line); - while ((line = successResult.readLine()) != null) { - successMsg.append(LINE_SEP).append(line); - } - } - if ((line = errorResult.readLine()) != null) { - errorMsg.append(line); - while ((line = errorResult.readLine()) != null) { - errorMsg.append(LINE_SEP).append(line); - } - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "execCmd"); - } finally { - try { - os.close(); - successResult.close(); - errorResult.close(); - } catch (Exception e) { - } - if (process != null) { - process.destroy(); - } - } - return new CommandResult( - result, - successMsg == null ? null : successMsg.toString(), - errorMsg == null ? null : errorMsg.toString() - ); - } - - /** 返回的命令结果 */ - public static class CommandResult { - - /** 结果码 */ - public int result; - /** 成功信息 */ - public String successMsg; - /** 错误信息 */ - public String errorMsg; - - public CommandResult(final int result, final String successMsg, final String errorMsg) { - this.result = result; - this.successMsg = successMsg; - this.errorMsg = errorMsg; - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/ShortCutUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ShortCutUtils.java deleted file mode 100644 index 496652077d..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ShortCutUtils.java +++ /dev/null @@ -1,93 +0,0 @@ -package dev.utils.app; - -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.Intent.ShortcutIconResource; -import android.database.Cursor; -import android.net.Uri; - -import dev.utils.LogPrintUtils; - -/** - * detail: 创建删除快捷图标工具类 - * Created by Ttt - * 需要权限: - * com.android.launcher.permission.INSTALL_SHORTCUT - * com.android.launcher.permission.UNINSTALL_SHORTCUT - */ -public final class ShortCutUtils { - - private ShortCutUtils() { - } - - // 日志TAG - private static final String TAG = ShortCutUtils.class.getSimpleName(); - - /** - * 检测是否存在桌面快捷方式 - * @param context - * @param name - * @return 是否存在桌面图标 - */ - public static boolean hasShortcut(Context context, String name) { - boolean isInstallShortcut = false; - Cursor cursor = null; - try { - ContentResolver cr = context.getContentResolver(); - String AUTHORITY = "com.android.launcher.settings"; - Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/favorites?notify=true"); - cursor = cr.query(CONTENT_URI, new String[]{"title", "iconResource"}, "title=?", new String[]{ name }, null); - if (cursor != null && cursor.getCount() > 0) { - isInstallShortcut = true; - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "hasShortcut"); - } finally { - if (cursor != null) { - try { - cursor.close(); - } catch (Exception e) { - } - } - } - return isInstallShortcut; - } - - /** - * 为程序创建桌面快捷方式 - * @param context - * @param clasName - * @param name - * @param res - */ - public static void addShortcut(Context context, String clasName, String name, int res) { - Intent shortcut = new Intent( "com.android.launcher.action.INSTALL_SHORTCUT"); - shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); // 快捷方式的名称 - shortcut.putExtra("duplicate", false); // 不允许重复创建 - // 设置 快捷方式跳转页面 - Intent shortcutIntent = new Intent(Intent.ACTION_MAIN); - shortcutIntent.setClassName(context, clasName); - shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); - // 快捷方式的图标 - ShortcutIconResource iconRes = ShortcutIconResource.fromContext(context, res); - shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes); - context.sendBroadcast(shortcut); - } - - /** - * 删除程序的快捷方式 - * @param context - * @param name - */ - public static void delShortcut(Context context, String clasName, String name) { - Intent shortcut = new Intent( "com.android.launcher.action.UNINSTALL_SHORTCUT"); - // 快捷方式的名称 - shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); - String appClass = context.getPackageName() + "." + clasName; - ComponentName comp = new ComponentName(context.getPackageName(), appClass); - shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(Intent.ACTION_MAIN).setComponent(comp)); - context.sendBroadcast(shortcut); - } -} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/SignaturesUtils.java b/DevLibUtils/src/main/java/dev/utils/app/SignaturesUtils.java deleted file mode 100644 index 358e768846..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/SignaturesUtils.java +++ /dev/null @@ -1,232 +0,0 @@ -package dev.utils.app; - -import android.content.pm.Signature; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; -import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import javax.security.auth.x500.X500Principal; - -import dev.utils.LogPrintUtils; - -/** - * detail: 签名工具类(获取app,签名信息) - * Created by Ttt - */ -public final class SignaturesUtils { - - private SignaturesUtils() { - } - - // 日志TAG - private static final String TAG = SignaturesUtils.class.getSimpleName(); - - // 如需要小写则把ABCDEF改成小写 - private static final char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', - '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - - /** - * 检测应用程序是否是用"CN=Android Debug,O=Android,C=US"的debug信息来签名的 - * 判断签名是debug签名还是release签名 - */ - private final static X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US"); - - /** - * 进行转换 - * @param bData - * @return - */ - public static String toHexString(byte[] bData) { - StringBuilder sb = new StringBuilder(bData.length * 2); - for (int i = 0, len = bData.length; i < len; i++) { - sb.append(HEX_DIGITS[(bData[i] & 0xf0) >>> 4]); - sb.append(HEX_DIGITS[bData[i] & 0x0f]); - } - return sb.toString(); - } - - /** - * 返回MD5 - * @param signatures - * @return - */ - public static String signatureMD5(Signature[] signatures) { - try { - MessageDigest digest = MessageDigest.getInstance("MD5"); - if (signatures != null) { - for (Signature s : signatures) - digest.update(s.toByteArray()); - } - return toHexString(digest.digest()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "signatureMD5"); - return ""; - } - } - - /** - * SHA1 - */ - public static String signatureSHA1(Signature[] signatures) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - if (signatures != null) { - for (Signature s : signatures) - digest.update(s.toByteArray()); - } - return toHexString(digest.digest()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "signatureSHA1"); - return ""; - } - } - - /** - * SHA256 - */ - public static String signatureSHA256(Signature[] signatures) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - if (signatures != null) { - for (Signature s : signatures) - digest.update(s.toByteArray()); - } - return toHexString(digest.digest()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "signatureSHA256"); - return ""; - } - } - - /** - * 判断签名是debug签名还是release签名 - * @return true = 开发(debug.keystore),false = 上线发布(非.android默认debug.keystore) - */ - public static boolean isDebuggable(Signature[] signatures) { - // 判断是否默认key(默认是) - boolean debuggable = true; - try { - for (int i = 0, len = signatures.length; i < len; i++) { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream stream = new ByteArrayInputStream(signatures[i].toByteArray()); - X509Certificate cert = (X509Certificate) cf.generateCertificate(stream); - debuggable = cert.getSubjectX500Principal().equals(DEBUG_DN); - if (debuggable) { - break; - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "isDebuggable"); - } - return debuggable; - } - - /** - * 获取App 证书对象 - */ - public static X509Certificate getX509Certificate(Signature[] signatures){ - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream stream = new ByteArrayInputStream(signatures[0].toByteArray()); - X509Certificate cert = (X509Certificate) cf.generateCertificate(stream); - return cert; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getX509Certificate"); - } - return null; - } - - /** - * 打印签名信息 - * @param signatures - * @return - */ - public static void printSignatureName(Signature[] signatures){ - try { - for (int i = 0, len = signatures.length; i < len; i++) { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream stream = new ByteArrayInputStream(signatures[i].toByteArray()); - X509Certificate cert = (X509Certificate) cf.generateCertificate(stream); - - String pubKey = cert.getPublicKey().toString(); // 公钥 - String signNumber = cert.getSerialNumber().toString(); - - LogPrintUtils.dTag(TAG, "signName:" + cert.getSigAlgName());//算法名 - LogPrintUtils.dTag(TAG, "pubKey:" + pubKey); - LogPrintUtils.dTag(TAG, "signNumber:" + signNumber);//证书序列编号 - LogPrintUtils.dTag(TAG, "subjectDN:"+cert.getSubjectDN().toString()); - LogPrintUtils.dTag(TAG, cert.getNotAfter() + "--" + cert.getNotBefore()); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "signatureNameA"); - } - } - - // -- - - // Android的APK应用签名机制以及读取签名的方法 - // http://www.jb51.net/article/79894.htm - - /** - * 从APK中读取签名 - * @param file - * @return - * @throws IOException - */ - public static Signature[] getSignaturesFromApk(File file) { - try { - Certificate[] certificates = getCertificateFromApk(file); - Signature[] signatures = new Signature[] { new Signature(certificates[0].getEncoded()) }; - return signatures; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getSignaturesFromApk"); - } - return null; - } - - /** - * 从APK中读取签名 - * @param file - * @return - * @throws IOException - */ - public static Certificate[] getCertificateFromApk(File file) { - try { - JarFile jarFile = new JarFile(file); - JarEntry je = jarFile.getJarEntry("AndroidManifest.xml"); - byte[] readBuffer = new byte[8192]; - return loadCertificates(jarFile, je, readBuffer); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getCertificateFromApk"); - } - return null; - } - - /** - * 加载签名 - * @param jarFile - * @param je - * @param readBuffer - * @return - */ - private static Certificate[] loadCertificates(JarFile jarFile, JarEntry je, byte[] readBuffer) { - try { - InputStream is = jarFile.getInputStream(je); - while (is.read(readBuffer, 0, readBuffer.length) != -1) { - } - is.close(); - return je != null ? je.getCertificates() : null; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "loadCertificates"); - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/SizeUtils.java b/DevLibUtils/src/main/java/dev/utils/app/SizeUtils.java deleted file mode 100644 index 8b247c2290..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/SizeUtils.java +++ /dev/null @@ -1,208 +0,0 @@ -package dev.utils.app; - -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: dp,px,sp转换、View获取宽高等 - * Created by Ttt - */ -public final class SizeUtils { - - private SizeUtils() { - } - - // 日志TAG - private static final String TAG = SizeUtils.class.getSimpleName(); - - /** - * 根据手机的分辨率从 dp 的单位 转成为 px(像素) - * @param dpValue - */ - public static int dipConvertPx(float dpValue) { - try { - float scale = DevUtils.getContext().getResources().getDisplayMetrics().density; - return (int) (dpValue * scale + 0.5f); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "dipConvertPx"); - } - return -1; - } - - /** - * 根据手机的分辨率从 px(像素) 的单位 转成为 dp - * @param pxValue - */ - public static int pxConvertDip(float pxValue) { - try { - float scale = DevUtils.getContext().getResources().getDisplayMetrics().density; - return (int) (pxValue / scale + 0.5f); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "pxConvertDip"); - } - return -1; - } - - /** - * 根据手机的分辨率从 px(像素) 的单位 转成为 sp - * @param pxValue - */ - public static int pxConvertSp(float pxValue) { - try { - float scale = DevUtils.getContext().getResources().getDisplayMetrics().scaledDensity; - return (int) (pxValue / scale + 0.5f); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "pxConvertSp"); - } - return -1; - } - - /** - * 根据手机的分辨率从 sp 的单位 转成为 px - * @param spValue - */ - public static int spConvertPx(float spValue) { - try { - float scale = DevUtils.getContext().getResources().getDisplayMetrics().scaledDensity; - return (int) (spValue * scale + 0.5f); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "spConvertPx"); - } - return -1; - } - - /** - * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 第二种 - * @param dpValue - */ - public static int dipConvertPx2(float dpValue) { - try { - return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, DevUtils.getContext().getResources().getDisplayMetrics()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "dipConvertPx2"); - } - return -1; - } - - /** - * 根据手机的分辨率从 sp 的单位 转成为 px 第二种 - * @param spValue - */ - public static int spConvertPx2(float spValue) { - try { - // android.util.TypedValue - return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, DevUtils.getContext().getResources().getDisplayMetrics()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "spConvertPx2"); - } - return -1; - } - - /** - * 各种单位转换 - 该方法存在于 TypedValue - * @param unit 单位 - * @param value 值 - * @param metrics DisplayMetrics - * @return 转换结果 - */ - public static float applyDimension(final int unit, final float value, final DisplayMetrics metrics) { - switch (unit) { - case TypedValue.COMPLEX_UNIT_PX: - return value; - case TypedValue.COMPLEX_UNIT_DIP: - return value * metrics.density; - case TypedValue.COMPLEX_UNIT_SP: - return value * metrics.scaledDensity; - case TypedValue.COMPLEX_UNIT_PT: - return value * metrics.xdpi * (1.0f / 72); - case TypedValue.COMPLEX_UNIT_IN: - return value * metrics.xdpi; - case TypedValue.COMPLEX_UNIT_MM: - return value * metrics.xdpi * (1.0f / 25.4f); - } - return -1; - } - - // == - - /** - * 在 onCreate 中获取视图的尺寸 - 需回调 onGetSizeListener 接口,在 onGetSize 中获取 view 宽高 - * 用法示例如下所示 - * SizeUtils.forceGetViewSize(view, new SizeUtils.onGetSizeListener() { - * Override - * public void onGetSize(final View view) { - * view.getWidth(); - * } - * }); - * @param view 视图 - * @param listener 监听器 - */ - public static void forceGetViewSize(final View view, final onGetSizeListener listener) { - view.post(new Runnable() { - @Override - public void run() { - if (listener != null) { - listener.onGetSize(view); - } - } - }); - } - - /** 获取到 View 尺寸的监听 */ - public interface onGetSizeListener { - void onGetSize(View view); - } - - // == - - /** - * 测量视图尺寸 - * @param view 视图 - * @return arr[0]: 视图宽度, arr[1]: 视图高度 - */ - public static int[] measureView(final View view) { - try { - ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); - if (layoutParams == null) { - layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - } - int widthSpec = ViewGroup.getChildMeasureSpec(0, 0, layoutParams.width); - int lpHeight = layoutParams.height; - int heightSpec; - if (lpHeight > 0) { - heightSpec = View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY); - } else { - heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); - } - view.measure(widthSpec, heightSpec); - return new int[]{ view.getMeasuredWidth(), view.getMeasuredHeight() }; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "measureView"); - return new int[] { 0, 0 }; - } - } - - /** - * 获取测量视图宽度 - * @param view 视图 - * @return 视图宽度 - */ - public static int getMeasuredWidth(final View view) { - return measureView(view)[0]; - } - - /** - * 获取测量视图高度 - * @param view 视图 - * @return 视图高度 - */ - public static int getMeasuredHeight(final View view) { - return measureView(view)[1]; - } -} - diff --git a/DevLibUtils/src/main/java/dev/utils/app/StateListUtils.java b/DevLibUtils/src/main/java/dev/utils/app/StateListUtils.java deleted file mode 100644 index 9bb603c6d4..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/StateListUtils.java +++ /dev/null @@ -1,317 +0,0 @@ -package dev.utils.app; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.StateListDrawable; -import android.support.annotation.ColorRes; -import android.support.annotation.DrawableRes; -import android.support.v4.content.ContextCompat; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 颜色状态列表 工具类 - * Created by Ttt - * https://blog.csdn.net/wuqilianga/article/details/72964884 - * https://blog.csdn.net/CrazyMo_/article/details/53456541 - */ -public final class StateListUtils { - - private StateListUtils() { - } - - // 日志TAG - private static final String TAG = StateListUtils.class.getSimpleName(); - -// android:state_active 是否处于激活状态 -// android:state_checkable 是否可勾选 -// android:state_checked 是否已勾选 -// android:state_enabled 是否可用 -// android:state_first 是否开始状态 -// android:state_focused 是否已获取焦点 -// android:state_last 是否处于结束 -// android:state_middle 是否处于中间 -// android:state_pressed 是否处于按下状态 . -// android:state_selected 是否处于选中状态 -// android:state_window_focused 是否窗口已获得焦点 - - /** - * 通过上下文获取 - * @param id - * @return - */ - public static ColorStateList getColorStateList(int id) { - try { - return ContextCompat.getColorStateList(DevUtils.getContext(), id); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getColorStateList"); - } - return null; - } - - // === 设置字体颜色 === - - // == 字符串颜色 == - - /** - * 创建 颜色状态列表 => createColorStateList("#ffffffff", "#ff44e6ff") - * @param pressed - * @param normal - * @return - */ - public static ColorStateList createColorStateList(String pressed, String normal) { - // 颜色值 - int[] colors = new int[]{ Color.parseColor(pressed), Color.parseColor(normal) }; - // 状态值 - int[][] states = new int[2][]; - states[0] = new int[]{ android.R.attr.state_pressed }; // 选中颜色 - states[1] = new int[]{ }; // 默认颜色 - // 生成对应的List对象 - ColorStateList colorList = new ColorStateList(states, colors); - return colorList; - } - - /** - * 创建 颜色状态列表 - * @param selected - * @param pressed - * @param normal - * @return - */ - public static ColorStateList createColorStateList(String selected, String pressed, String normal) { - // 颜色值 - int[] colors = new int[]{ Color.parseColor(selected), Color.parseColor(pressed), Color.parseColor(normal) }; - // 状态值 - int[][] states = new int[3][]; - states[0] = new int[]{ android.R.attr.state_selected }; // 选中颜色 - states[1] = new int[]{ android.R.attr.state_pressed }; // 点击颜色 - states[2] = new int[]{ }; // 默认颜色 - // 生成对应的List对象 - ColorStateList colorList = new ColorStateList(states, colors); - return colorList; - } - - /** - * 创建 颜色状态列表 - * @param selected - * @param pressed - * @param focused - * @param checked - * @param normal - * @return - */ - public static ColorStateList createColorStateList(String selected, String pressed, String focused, String checked, String normal) { - // 颜色值 - int[] colors = new int[]{ Color.parseColor(selected), Color.parseColor(pressed), Color.parseColor(focused), Color.parseColor(checked), Color.parseColor(normal) }; - // 状态值 - int[][] states = new int[5][]; - states[0] = new int[]{ android.R.attr.state_selected }; // 选中颜色 - states[1] = new int[]{ android.R.attr.state_pressed }; // 点击颜色 - states[2] = new int[]{ android.R.attr.state_focused }; // 获取焦点 - states[3] = new int[]{ android.R.attr.state_checked }; // 选中 - states[4] = new int[]{ }; // 默认颜色 - // 生成对应的List对象 - ColorStateList colorList = new ColorStateList(states, colors); - return colorList; - } - - // == int == - - /** - * 创建 颜色状态列表 => createColorStateList(R.color.whilte, R.color.black) - * @param pressed - * @param normal - * @return - */ - public static ColorStateList createColorStateList(@ColorRes int pressed, @ColorRes int normal) { - Context context = DevUtils.getContext(); - // 颜色值 - int[] colors = new int[2]; - colors[0] = ContextCompat.getColor(context, pressed); - colors[1] = ContextCompat.getColor(context, normal); - // 状态值 - int[][] states = new int[2][]; - states[0] = new int[]{ android.R.attr.state_pressed }; // 选中颜色 - states[1] = new int[]{ }; // 默认颜色 - // 生成对应的List对象 - ColorStateList colorList = new ColorStateList(states, colors); - return colorList; - } - - /** - * 创建 颜色状态列表 - * @param selected - * @param pressed - * @param normal - * @return - */ - public static ColorStateList createColorStateList(@ColorRes int selected, @ColorRes int pressed, @ColorRes int normal) { - Context context = DevUtils.getContext(); - // 颜色值 - int[] colors = new int[3]; - colors[0] = ContextCompat.getColor(context, selected); - colors[1] = ContextCompat.getColor(context, pressed); - colors[2] = ContextCompat.getColor(context, normal); - // 状态值 - int[][] states = new int[3][]; - states[0] = new int[]{ android.R.attr.state_selected }; // 选中颜色 - states[1] = new int[]{ android.R.attr.state_pressed }; // 点击颜色 - states[2] = new int[]{ }; // 默认颜色 - // 生成对应的List对象 - ColorStateList colorList = new ColorStateList(states, colors); - return colorList; - } - - /** - * 创建 颜色状态列表 - * @param selected - * @param pressed - * @param focused - * @param checked - * @param normal - * @return - */ - public static ColorStateList createColorStateList(@ColorRes int selected, @ColorRes int pressed, @ColorRes int focused, @ColorRes int checked, @ColorRes int normal) { - Context context = DevUtils.getContext(); - // 颜色值 - int[] colors = new int[5]; - colors[0] = ContextCompat.getColor(context, selected); - colors[1] = ContextCompat.getColor(context, pressed); - colors[2] = ContextCompat.getColor(context, focused); - colors[3] = ContextCompat.getColor(context, checked); - colors[4] = ContextCompat.getColor(context, normal); - // 状态值 - int[][] states = new int[5][]; - states[0] = new int[]{ android.R.attr.state_selected }; // 选中颜色 - states[1] = new int[]{ android.R.attr.state_pressed }; // 点击颜色 - states[2] = new int[]{ android.R.attr.state_focused }; // 获取焦点 - states[3] = new int[]{ android.R.attr.state_checked }; // 选中 - states[4] = new int[]{ }; // 默认颜色 - // 生成对应的List对象 - ColorStateList colorList = new ColorStateList(states, colors); - return colorList; - } - - // === 设置背景切换 Drawable === - - /** - * 创建 Drawable选择切换 list => view.setBackground(Drawable) - * @param pressed - * @param normal - * @return - */ - public static StateListDrawable newSelector(@DrawableRes int pressed, @DrawableRes int normal) { - Context context = DevUtils.getContext(); - // == 获取 Drawable == - Drawable pressedDraw = ContextCompat.getDrawable(context, pressed); - Drawable normalDraw = ContextCompat.getDrawable(context, normal); - // 默认初始化 - StateListDrawable stateListDrawable = new StateListDrawable(); - stateListDrawable.addState(new int[] { android.R.attr.state_pressed }, pressedDraw); - stateListDrawable.addState(new int[] {}, normalDraw); - return stateListDrawable; - } - - /** - * 创建 Drawable选择切换 list - * @param selected - * @param pressed - * @param normal - * @return - */ - public static StateListDrawable newSelector(@DrawableRes int selected, @DrawableRes int pressed, @DrawableRes int normal) { - Context context = DevUtils.getContext(); - // == 获取 Drawable == - Drawable selectedDraw = ContextCompat.getDrawable(context, selected); - Drawable pressedDraw = ContextCompat.getDrawable(context, pressed); - Drawable normalDraw = ContextCompat.getDrawable(context, normal); - // 默认初始化 - StateListDrawable stateListDrawable = new StateListDrawable(); - stateListDrawable.addState(new int[] { android.R.attr.state_selected }, selectedDraw); - stateListDrawable.addState(new int[] { android.R.attr.state_pressed }, pressedDraw); - stateListDrawable.addState(new int[] {}, normalDraw); - return stateListDrawable; - } - - /** - * 创建 Drawable选择切换 list - * @param selected - * @param pressed - * @param focused - * @param checked - * @param normal - * @return - */ - public static StateListDrawable newSelector(@DrawableRes int selected, @DrawableRes int pressed, @DrawableRes int focused, @DrawableRes int checked, @DrawableRes int normal) { - Context context = DevUtils.getContext(); - // == 获取 Drawable == - Drawable selectedDraw = ContextCompat.getDrawable(context, selected); - Drawable pressedDraw = ContextCompat.getDrawable(context, pressed); - Drawable focusedDraw = ContextCompat.getDrawable(context, focused); - Drawable checkedDraw = ContextCompat.getDrawable(context, checked); - Drawable normalDraw = ContextCompat.getDrawable(context, normal); - // 默认初始化 - StateListDrawable stateListDrawable = new StateListDrawable(); - stateListDrawable.addState(new int[] { android.R.attr.state_selected }, selectedDraw); - stateListDrawable.addState(new int[] { android.R.attr.state_pressed }, pressedDraw); - stateListDrawable.addState(new int[] { android.R.attr.state_focused }, focusedDraw); - stateListDrawable.addState(new int[] { android.R.attr.state_checked }, checkedDraw); - stateListDrawable.addState(new int[] {}, normalDraw); - return stateListDrawable; - } - - // === 设置背景切换 Drawable === - - /** - * 创建 Drawable选择切换 list => view.setBackground(Drawable) - * @param pressed - * @param normal - * @return - */ - public static StateListDrawable newSelector(Drawable pressed, Drawable normal) { - // 默认初始化 - StateListDrawable stateListDrawable = new StateListDrawable(); - stateListDrawable.addState(new int[] { android.R.attr.state_pressed }, pressed); - stateListDrawable.addState(new int[] {}, normal); - return stateListDrawable; - } - - /** - * 创建 Drawable选择切换 list - * @param selected - * @param pressed - * @param normal - * @return - */ - public static StateListDrawable newSelector(Drawable selected, Drawable pressed, Drawable normal) { - // 默认初始化 - StateListDrawable stateListDrawable = new StateListDrawable(); - stateListDrawable.addState(new int[] { android.R.attr.state_selected }, selected); - stateListDrawable.addState(new int[] { android.R.attr.state_pressed }, pressed); - stateListDrawable.addState(new int[] {}, normal); - return stateListDrawable; - } - - /** - * 创建 Drawable选择切换 list - * @param selected - * @param pressed - * @param focused - * @param checked - * @param normal - * @return - */ - public static StateListDrawable newSelector(Drawable selected, Drawable pressed, Drawable focused, Drawable checked, Drawable normal) { - // 默认初始化 - StateListDrawable stateListDrawable = new StateListDrawable(); - stateListDrawable.addState(new int[] { android.R.attr.state_selected }, selected); - stateListDrawable.addState(new int[] { android.R.attr.state_pressed }, pressed); - stateListDrawable.addState(new int[] { android.R.attr.state_focused }, focused); - stateListDrawable.addState(new int[] { android.R.attr.state_checked }, checked); - stateListDrawable.addState(new int[] {}, normal); - return stateListDrawable; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/TextViewUtils.java b/DevLibUtils/src/main/java/dev/utils/app/TextViewUtils.java deleted file mode 100644 index 099624eec4..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/TextViewUtils.java +++ /dev/null @@ -1,582 +0,0 @@ -package dev.utils.app; - -import android.app.Activity; -import android.graphics.Paint; -import android.graphics.Rect; -import android.support.annotation.ColorInt; -import android.support.annotation.IdRes; -import android.text.Html; -import android.view.View; -import android.view.Window; -import android.widget.TextView; - -import dev.utils.LogPrintUtils; - -/** - * detail: TextView 工具类 - * Created by Ttt - */ -public final class TextViewUtils { - - private TextViewUtils() { - } - - // 日志TAG - private static final String TAG = TextViewUtils.class.getSimpleName(); - - /** - * 获取TextView - * @param view - * @return - */ - public static TextView getTextView(View view){ - if (view != null){ - try { - return (TextView) view; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getTextView"); - } - } - return null; - } - - /** - * 获取TextView - * @param view - * @return - */ - public static TextView getTextView(View view, @IdRes int id){ - if (view != null){ - try { - return view.findViewById(id); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getTextView"); - } - } - return null; - } - - /** - * 获取TextView - * @param activity - * @return - */ - public static TextView getTextView(Activity activity, @IdRes int id){ - if (activity != null){ - try { - return activity.findViewById(id); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getTextView"); - } - } - return null; - } - - /** - * 获取TextView - * @param window - * @return - */ - public static TextView getTextView(Window window, @IdRes int id){ - if (window != null){ - try { - return window.findViewById(id); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getTextView"); - } - } - return null; - } - - // = - - /** - * 获取文本 - * @param textView - * @return - */ - public static String getText(TextView textView){ - if (textView != null){ - return textView.getText().toString(); - } - return null; - } - - /** - * 获取文本 - * @param view - * @return - */ - public static String getText(View view) { - if (view != null){ - try { - return ((TextView) view).getText().toString(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getText"); - } - } - return null; - } - - - /** - * 获取文本 - * @param view - * @return - */ - public static String getText(View view, @IdRes int id){ - if (view != null){ - try { - return ((TextView) view.findViewById(id)).getText().toString(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getText"); - } - } - return null; - } - - /** - * 获取文本 - * @param activity - * @return - */ - public static String getText(Activity activity, @IdRes int id){ - if (activity != null){ - try { - return ((TextView) activity.findViewById(id)).getText().toString(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getText"); - } - } - return null; - } - - /** - * 获取文本 - * @param window - * @return - */ - public static String getText(Window window, @IdRes int id){ - if (window != null){ - try { - return ((TextView) window.findViewById(id)).getText().toString(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getText"); - } - } - return null; - } - - // = - - - /** - * 设置字体颜色 - * @param view - * @param color - */ - public static void setTextColor(View view, @ColorInt int color){ - if (view != null){ - if (view instanceof TextView){ - ((TextView) view).setTextColor(color); - } - } - } - - /** - * 设置字体颜色 - * @param textView - * @param color - */ - public static void setTextColor(TextView textView, @ColorInt int color){ - if (textView != null){ - textView.setTextColor(color); - } - } - - /** - * 设置字体颜色 - * @param view - * @param id - * @param color - */ - public static void setTextColor(View view, @IdRes int id, @ColorInt int color){ - if (view != null){ - try { - ((TextView) view.findViewById(id)).setTextColor(color); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setTextColor"); - } - } - } - - /** - * 设置字体颜色 - * @param activity - * @param id - * @param color - */ - public static void setTextColor(Activity activity, @IdRes int id, @ColorInt int color){ - if (activity != null){ - try { - ((TextView) activity.findViewById(id)).setTextColor(color); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setTextColor"); - } - } - } - - /** - * 设置字体颜色 - * @param window - * @param id - * @param color - */ - public static void setTextColor(Window window, @IdRes int id, @ColorInt int color){ - if (window != null){ - try { - ((TextView) window.findViewById(id)).setTextColor(color); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setTextColor"); - } - } - } - - // = - - /** - * 设置内容 - * @param view - * @param content - */ - public static void setText(View view, String content){ - if (view != null && content != null){ - if (view instanceof TextView){ - ((TextView) view).setText(content); - } - } - } - - /** - * 设置内容 - * @param view - * @param id - * @param content - */ - public static void setText(View view, @IdRes int id, String content){ - if (view != null){ - try { - ((TextView) view.findViewById(id)).setText(content); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setText"); - } - } - } - - /** - * 设置内容 - * @param activity - * @param id - * @param content - */ - public static void setText(Activity activity, @IdRes int id, String content){ - if (activity != null){ - try { - ((TextView) activity.findViewById(id)).setText(content); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setText"); - } - } - } - - /** - * 设置内容 - * @param window - * @param id - * @param content - */ - public static void setText(Window window, @IdRes int id, String content){ - if (window != null){ - try { - ((TextView) window.findViewById(id)).setText(content); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setText"); - } - } - } - - // = - - /** - * 设置 Html 内容 - * @param view - * @param content - */ - public static void setHtmlText(View view, String content){ - if (view != null && content != null){ - if (view instanceof TextView){ - ((TextView) view).setText(Html.fromHtml(content)); - } - } - } - - /** - * 设置 Html 内容 - * @param view - * @param id - * @param content - */ - public static void setHtmlText(View view, @IdRes int id, String content){ - if (view != null){ - try { - ((TextView) view.findViewById(id)).setText(Html.fromHtml(content)); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setHtmlText"); - } - } - } - - /** - * 设置 Html 内容 - * @param activity - * @param id - * @param content - */ - public static void setHtmlText(Activity activity, @IdRes int id, String content){ - if (activity != null){ - try { - ((TextView) activity.findViewById(id)).setText(Html.fromHtml(content)); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setHtmlText"); - } - } - } - - /** - * 设置 Html 内容 - * @param window - * @param id - * @param content - */ - public static void setHtmlText(Window window, @IdRes int id, String content){ - if (window != null){ - try { - ((TextView) window.findViewById(id)).setText(Html.fromHtml(content)); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setHtmlText"); - } - } - } - - // - - - /** - * 设置内容 - * @param textView - * @param content - */ - public static void setText(TextView textView, String content){ - if (textView != null && content != null){ - textView.setText(content); - } - } - - /** - * 设置 Html 内容 - * @param textView - * @param content - */ - public static void setHtmlText(TextView textView, String content){ - if (textView != null && content != null){ - textView.setText(Html.fromHtml(content)); - } - } - - /** - * 给TextView设置下划线 - * @param textView - */ - public static void setTVUnderLine(TextView textView) { - if (textView != null){ - Paint paint = textView.getPaint(); - paint.setFlags(Paint.UNDERLINE_TEXT_FLAG); - paint.setAntiAlias(true); - } - } - - /** - * 获取字体高度 - * @param textView - * @return - */ - public static int getTextHeight(TextView textView){ - if (textView != null){ - return getTextHeight(textView.getPaint()); - } - return -1; - } - - /** - * 获取字体高度 - * @param paint - * @return - */ - public static int getTextHeight(Paint paint){ - // https://blog.csdn.net/superbigcupid/article/details/47153139 - // 获取字体高度 - Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); - // 计算内容高度 - int tHeight = (int) Math.ceil((fontMetrics.descent - fontMetrics.ascent)); - // 返回字体高度 - return tHeight; - } - - /** - * 获取字体顶部偏移高度 - * @param textView - * @return - */ - public static int getTextTopOffsetHeight(TextView textView){ - if (textView != null){ - return getTextTopOffsetHeight(textView.getPaint()); - } - return -1; - } - - /** - * 获取字体顶部偏移高度 - * @param paint - * @return - */ - public static int getTextTopOffsetHeight(Paint paint){ - // 获取字体高度 - Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); - // 计算字体偏差(顶部偏差) - int baseLine = (int) Math.ceil(Math.abs(fontMetrics.top) - Math.abs(fontMetrics.ascent)); - // 返回顶部偏差 - return baseLine; - } - - /** - * 计算字体宽度 - * @param textView - * @return - */ - public static float getTextWidth(TextView textView){ - return getTextWidth(textView.getPaint(), textView.getText().toString()); - } - - /** - * 计算字体宽度 - * @param paint - * @param hintStr - * @return - */ - public static float getTextWidth(Paint paint, String hintStr){ - return paint.measureText(hintStr); - } - - /** - * 获取画布中间居中位置 - * @param targetRect - * @param paint - * @return - */ - public static int getCenterRectY(Rect targetRect, Paint paint){ - // 将字画在矩形背景的正中位置 - // https://blog.csdn.net/superbigcupid/article/details/47153139 - // 获取字体高度 - Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); - // 获取底部Y轴居中位置 - return targetRect.top + (targetRect.bottom - targetRect.top) / 2 - (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.top; - // canvas.drawText(testString, targetRect.centerX(), baseline, paint); - } - - /** - * 通过需要的高度, 计算字体大小 - * @param tHeight 需要的字体高度 - * @return - */ - public static float reckonTextSize(int tHeight){ - // 创建画笔 - Paint paint = new Paint(); - // 默认字体大小 - float textSize = 90.0f; - // 计算内容高度 - int calcTextHeight = -1; - // 循环计算 - while (true){ - // 设置画笔大小 - paint.setTextSize(textSize); - // 获取字体高度 - Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); - // 计算内容高度 - calcTextHeight = (int) Math.ceil((fontMetrics.descent - fontMetrics.ascent)); - // 符合条件则直接返回 - if (calcTextHeight == tHeight){ - return textSize; - } else if (calcTextHeight > tHeight){ // 如果计算的字体高度大于 - textSize -= 0.5f; - } else { - textSize += 0.5f; - } - } - } - - /** - * 计算第几位超过宽度 - * @param textView - * @param width - * @return -1 表示没超过 - */ - public static int calcTextWidth(TextView textView, float width){ - if (textView != null){ - return calcTextWidth(textView.getPaint(), textView.getText().toString(), width); - } - return -1; - } - - /** - * 计算第几位超过宽度 - * @param text - * @param width - * @return -1 表示没超过 - */ - public static int calcTextWidth(Paint paint, String text, float width){ - // 先获取宽度 - float textWidth = getTextWidth(paint, text); - // 判断是否超过 - if (textWidth <= width){ - return -1; // 表示没超过 - } - // 获取数据长度 - int length = text.length(); - // 循环除2 - while (true) { - // 数据至少为2位以上 - if (length < 2) { - return 0; // 表示第一位已经超过 - } - // 从中间取值 - length = length / 2; - // 计算宽度 - textWidth = getTextWidth(paint, text.substring(0, length)); - // 判断是否小于宽度 - 进行返回长度 - if (textWidth <= width){ - break; - } - } - // 遍历计算 - for (int i = length, len = text.length(); i < len; i++){ - // 获取字体内容宽度 - float tWidth = paint.measureText(text.substring(0, i)); - // 判断是否大于指定宽度 - if (tWidth > width){ - return i - 1; // 返回超过前的长度 - } else if (tWidth == width){ - return i; // 返回超过前的长度 - } - } - return -1; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/UriUtils.java b/DevLibUtils/src/main/java/dev/utils/app/UriUtils.java deleted file mode 100644 index c3f704c1c1..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/UriUtils.java +++ /dev/null @@ -1,70 +0,0 @@ -package dev.utils.app; - -import android.net.Uri; -import android.os.Build; -import android.support.v4.content.FileProvider; - -import java.io.File; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: Uri 工具类 - * Created by Blankj - * http://blankj.com - * Update to Ttt - */ -public final class UriUtils { - - private UriUtils() { - } - - // 日志TAG - private static final String TAG = UriUtils.class.getSimpleName(); - - /** - - - - */ - - /** - * 返回处理后的Uri, 单独传递名字, 自动添加包名 ${applicationId} - * @param file - * @param name - * @return - * getUriForFileToName(file, "fileProvider"); - * getUriForFile(file, "包名.fileProvider"); - */ - public static Uri getUriForFileToName(final File file, String name){ - try { - String authority = DevUtils.getContext().getPackageName() + "." + name; - return getUriForFile(file, authority); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getUriForFileToName"); - } - return null; - } - - - /** - * Return a content URI for a given file. - * @param file The file. - * @return a content URI for a given file - */ - public static Uri getUriForFile(final File file, final String authority) { - if (file == null) return null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - return FileProvider.getUriForFile(DevUtils.getContext(), authority, file); - } else { - return Uri.fromFile(file); - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/VibrationUtils.java b/DevLibUtils/src/main/java/dev/utils/app/VibrationUtils.java deleted file mode 100644 index 3e5715e4e7..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/VibrationUtils.java +++ /dev/null @@ -1,61 +0,0 @@ -package dev.utils.app; - -import android.content.Context; -import android.os.Vibrator; - -import dev.utils.LogPrintUtils; - -/** - * detail: 震动相关工具类 - * Created by Ttt - */ -public final class VibrationUtils { - - private VibrationUtils() { - } - - // 日志TAG - private static final String TAG = VibrationUtils.class.getSimpleName(); - - /** - * 震动 - * - * @param context 上下文 - * @param milliseconds 振动时长 - */ - public static void vibrate(final Context context, final long milliseconds) { - try { - Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); - vibrator.vibrate(milliseconds); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "vibrate"); - } - } - - /** - * 指定手机以pattern模式振动 - * @param context - * @param pattern new long[]{400,800,1200,1600},就是指定在 400ms、800ms、1200ms、1600ms 这些时间点交替启动、关闭手机振动器 - * @param repeat 指定pattern数组的索引,指定pattern数组中从repeat索引开始的振动进行循环。-1表示只振动一次,非-1表示从 pattern的指定下标开始重复振动。 - */ - public static void vibrate(final Context context, final long[] pattern, final int repeat) { - try { - Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); - vibrator.vibrate(pattern, repeat); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "vibrate"); - } - } - - /** - * 取消振动 - * @param context 上下文 - */ - public static void cancel(final Context context) { - try { - ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).cancel(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "cancel"); - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/ViewUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ViewUtils.java deleted file mode 100644 index ec4b9d6e3a..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/ViewUtils.java +++ /dev/null @@ -1,640 +0,0 @@ -package dev.utils.app; - -import android.app.Activity; -import android.content.Context; -import android.content.ContextWrapper; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.Window; -import android.widget.AbsListView; -import android.widget.GridView; -import android.widget.ImageView; -import android.widget.ListAdapter; -import android.widget.ListView; - -import dev.utils.LogPrintUtils; - -/** - * detail: View 操作相关工具类 - * Created by Ttt - */ -public final class ViewUtils { - - private ViewUtils() { - } - - // 日志TAG - private static final String TAG = ViewUtils.class.getSimpleName(); - - /** - * 获取上下文 - * @param view - * @return - */ - public static Context getContext(View view){ - if (view != null){ - return view.getContext(); - } - return null; - } - - /** - * 判断View 是否为null - * @param view - * @return - */ - public static boolean isEmpty(View view){ - return view == null; - } - - /** - * 判断View 是否为null - * @param views - * @return - */ - public static boolean isEmpty(View... views){ - if (views != null && views.length != 0){ - for (int i = 0, len = views.length; i < len; i++){ - View view = views[i]; - if (view == null){ - return true; - } - } - return false; - } - return true; - } - - /** - * 判断View 是否显示 - * @param view - * @return - */ - public static boolean isVisibility(View view){ - return isVisibility(view, true); - } - - /** - * 判断 View 是否显示 - * @param view - * @param isDf - * @return - */ - public static boolean isVisibility(View view, boolean isDf){ - if (view != null){ - // 判断是否显示 - return (view.getVisibility() == View.VISIBLE); - } - // 出现意外返回默认值 - return isDf; - } - - /** - * 判断 View 是否都显示显示 - * @param views - * @return - */ - public static boolean isVisibilitys(View... views){ - if (views != null && views.length != 0){ - for (int i = 0, len = views.length; i < len; i++){ - View view = views[i]; - if (view != null && view.getVisibility() == View.VISIBLE){ - } else { - return false; - } - } - return true; - } - return false; - } - - /** - * 判断View 是否隐藏占位 - * @param view - * @return - */ - public static boolean isVisibilityIN(View view){ - return isVisibilityIN(view, false); - } - - /** - * 判断View 是否隐藏占位 - * @param view - * @param isDf - * @return - */ - public static boolean isVisibilityIN(View view, boolean isDf){ - if (view != null){ - // 判断是否显示 - return (view.getVisibility() == View.INVISIBLE); - } - // 出现意外返回默认值 - return isDf; - } - - /** - * 判断View 是否隐藏 - * @param view - * @return - */ - public static boolean isVisibilityGone(View view){ - return isVisibilityGone(view, false); - } - - /** - * 判断View 是否隐藏 - * @param view - * @param isDf - * @return - */ - public static boolean isVisibilityGone(View view, boolean isDf){ - if (view != null){ - // 判断是否显示 - return (view.getVisibility() == View.GONE); - } - // 出现意外返回默认值 - return isDf; - } - - // == - - /** - * 获取显示的状态 (View.VISIBLE : View.GONE) - * @param isVisibility - * @return - */ - public static int getVisibility(boolean isVisibility){ - return isVisibility ? View.VISIBLE : View.GONE; - } - - /** - * 获取显示的状态 (View.VISIBLE : View.INVISIBLE) - * @param isVisibility - * @return - */ - public static int getVisibilityIN(boolean isVisibility){ - return isVisibility ? View.VISIBLE : View.INVISIBLE; - } - - // -- - - /** - * 设置View显示状态 - * @param isVisibility - * @param view - */ - public static boolean setVisibility(boolean isVisibility, View view){ - if (view != null){ - view.setVisibility(isVisibility ? View.VISIBLE : View.GONE); - } - return isVisibility; - } - - /** - * 设置View显示的状态 - * @param isVisibility - * @param view - */ - public static boolean setVisibility(int isVisibility, View view){ - if (view != null){ - view.setVisibility(isVisibility); - } - return (isVisibility == View.VISIBLE); - } - - // -- - - /** - * 设置View 显示的状态 - * @param isVisibility - * @param views - */ - public static boolean setVisibilitys(boolean isVisibility, View... views){ - return setVisibilitys(getVisibility(isVisibility), views); - } - - /** - * 设置View 显示的状态 - * @param isVisibility - * @param views - */ - public static boolean setVisibilitys(int isVisibility, View... views){ - if (views != null && views.length != 0){ - for (int i = 0, len = views.length; i < len; i++){ - View view = views[i]; - if (view != null){ - view.setVisibility(isVisibility); - } - } - } - return (isVisibility == View.VISIBLE); - } - - /** - * 切换View 显示的状态 - * @param view - * @param views - */ - public static void toggleVisibilitys(View view, View... views){ - if (view != null){ - view.setVisibility(View.VISIBLE); - } - setVisibilitys(View.GONE, views); - } - - /** - * 切换View 显示的状态 - * @param viewArys - * @param views - */ - public static void toggleVisibilitys(View[] viewArys, View... views){ - toggleVisibilitys(viewArys, View.GONE, views); - } - - /** - * 切换View 显示的状态 - * @param viewArys - * @param status - * @param views - */ - public static void toggleVisibilitys(View[] viewArys, int status, View... views){ - // 默认前面显示 - setVisibilitys(View.VISIBLE, viewArys); - // 更具状态处理 - setVisibilitys(status, views); - } - - // == - - /** - * 切换View状态 - * @param isChange 是否改变 - * @param isVisibility 是否显示 - * @param view 需要判断的View - * @return - */ - public static boolean toogleView(boolean isChange, int isVisibility, View view){ - if (isChange && view != null){ - view.setVisibility(isVisibility); - } - return isChange; - } - - // -- - - /** - * 设置View 图片资源 - * @param draw - * @param views - */ - public static void setViewImageRes(int draw, ImageView... views){ - setViewImageRes(draw, View.VISIBLE, views); - } - - /** - * 设置View 图片资源 - * @param draw - * @param isVisibility - * @param views - */ - public static void setViewImageRes(int draw, int isVisibility, ImageView... views){ - if (views != null && views.length != 0){ - for (int i = 0, len = views.length; i < len; i++){ - ImageView view = views[i]; - if (view != null){ - try { - // 设置背景 - view.setImageResource(draw); - // 是否显示 - view.setVisibility(isVisibility); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setViewImageRes"); - } - } - } - } - } - - // == 初始化View操作等 == - - /** - * 初始化View - * @param view - * @param id - * @param - * @return - */ - public static T findViewById(View view, int id) { - return view.findViewById(id); - } - - /** - * 初始化View - * @param window - * @param id - * @param - * @return - */ - public static T findViewById(Window window, int id){ - return window.findViewById(id); - } - - /** - * 初始化View - * @param activity - * @param id - * @param - * @return - */ - public static T findViewById(Activity activity, int id) { - return activity.findViewById(id); - } - - /** - * 把自身从父View中移除 - * @param view - */ - public static void removeSelfFromParent(View view) { - if (view != null) { - ViewParent parent = view.getParent(); - if (parent != null && parent instanceof ViewGroup) { - ViewGroup group = (ViewGroup) parent; - group.removeView(view); - } - } - } - - /** - * 判断触点是否落在该View上 - * @param ev - * @param v - * @return - */ - public static boolean isTouchInView(MotionEvent ev, View v) { - int[] vLoc = new int[2]; - v.getLocationOnScreen(vLoc); - float motionX = ev.getRawX(); - float motionY = ev.getRawY(); - return motionX >= vLoc[0] && motionX <= (vLoc[0] + v.getWidth()) - && motionY >= vLoc[1] && motionY <= (vLoc[1] + v.getHeight()); - } - - /** - * View 改变请求 - * @param view - * @param isAll - */ - public static void requestLayoutParent(View view, boolean isAll) { - ViewParent parent = view.getParent(); - while (parent != null && parent instanceof View) { - if (!parent.isLayoutRequested()) { - parent.requestLayout(); - if (!isAll) { - break; - } - } - parent = parent.getParent(); - } - } - - /** - * 测量 view - * @param view - */ - public static void measureView(View view) { - ViewGroup.LayoutParams p = view.getLayoutParams(); - if (p == null) { - p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - } - int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width); - int lpHeight = p.height; - int childHeightSpec; - if (lpHeight > 0) { - childHeightSpec = View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY); - } else { - childHeightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); - } - view.measure(childWidthSpec, childHeightSpec); - } - - /** - * 获取view的宽度 - * @param view - * @return - */ - public static int getViewWidth(View view) { - measureView(view); - return view.getMeasuredWidth(); - } - - /** - * 获取view的高度 - * @param view - * @return - */ - public static int getViewHeight(View view) { - measureView(view); - return view.getMeasuredHeight(); - } - - /** - * 获取view的上下文 - * @param view - * @return - */ - public static Activity getActivity(View view) { - try { - Context context = view.getContext(); - while (context instanceof ContextWrapper) { - if (context instanceof Activity) { - return (Activity) context; - } - context = ((ContextWrapper) context).getBaseContext(); - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getActivity"); - } - return null; - } - - // === ListView === - - /** - * 计算ListView Item 高度 - * @param listView - */ - public static int calcListViewItemHeight(ListView listView) { - return calcListViewItemHeight(listView, false); - } - - /** - * 计算ListView Item 高度 - * @param listView - * @param isSet 是否 setLayoutParams - * == - * hint: 解决 ScrollView 嵌套 ListView 时, 会无法正确的计算ListView的大小 - */ - public static int calcListViewItemHeight(ListView listView, boolean isSet) { - // 获取 Adapter - ListAdapter listAdapter = listView.getAdapter(); - // 防止为null - if (listAdapter == null) { - return 0 ; - } - // 获取总体高度 - int totalHeight = 0; - // 遍历累加 - for (int i = 0, len = listAdapter.getCount(); i < len; i++) { - // 累加 Item 高度 - totalHeight += getItemHeighet(listAdapter, listView, i); - } - // == - // 获取子项间分隔符占用的高度 - // listView.getDividerHeight(); - // 累加分割线高度 - totalHeight += (listView.getDividerHeight() * (listAdapter.getCount() - 1)); - // 判断是否需要设置高度 - if (isSet){ - ViewGroup.LayoutParams params = listView.getLayoutParams(); - params.height = totalHeight; - listView.setLayoutParams(params); - } - // 返回总体高度 - return totalHeight; - } - - // === GridView === - - /** - * 计算GridView Item 高度 - * @param gridView - * @param numColumns - */ - public static int calcGridViewItemHeight(GridView gridView, int numColumns) { - return calcGridViewItemHeight(gridView, numColumns, false); - } - - /** - * 计算GridView Item 高度 - * @param gridView - * @param numColumns - * @param isSet 是否 setLayoutParams - * == - * hint: 解决 ScrollView 嵌套 GridView 时, 会无法正确的计算ListView的大小 - */ - public static int calcGridViewItemHeight(GridView gridView, int numColumns, boolean isSet) { - // 获取 Adapter - ListAdapter listAdapter = gridView.getAdapter(); - // 防止为null - if (listAdapter == null) { - return 0 ; - } - // 最高高度 - int singleMax = 0; - // 获取总体高度 - int totalHeight = 0; - // 获取总数 - int count = listAdapter.getCount(); - // 判断是否整数 - count = (count % numColumns == 0) ? (count / numColumns) : (count / numColumns + 1); - // 遍历累加 - for (int i = 0; i < count; i++) { - // 默认表示第一个的高度 - singleMax = getItemHeighet(listAdapter, gridView, i * numColumns); - // 遍历判断 - for (int eqI = 1; eqI < numColumns; eqI++){ - // 临时高度 - int tempHeight = 0; - // 进行判断处理 - if (i * numColumns + eqI <= count){ - // 获取对应的高度 - tempHeight = getItemHeighet(listAdapter, gridView, i * numColumns + eqI); - } - // 判断是否在最大高度 - if (tempHeight > singleMax){ - singleMax = tempHeight; - } - } - // 累加 Item 高度 - totalHeight += singleMax; - } - // 每列之间的间隔 | - //int hSpaec = gridView.getHorizontalSpacing(); - // 每行之间的间隔 - - int vSpace = gridView.getVerticalSpacing(); - // 最后获取整个gridView完整显示需要的高度 - totalHeight += (vSpace * (count - 1)); - // 判断是否需要设置高度 - if (isSet){ - ViewGroup.LayoutParams params = gridView.getLayoutParams(); - params.height = totalHeight; - gridView.setLayoutParams(params); - } - // 返回总体高度 - return totalHeight; - } - - // == - - /** - * 获取单独一个Item 高度 - * @param absViews - * @param pos - * @return - */ - public static int getItemHeighet(AbsListView absViews, int pos){ - if (absViews != null){ - return getItemHeighet(absViews.getAdapter(), absViews, pos, 0); - } - return 0; - } - - /** - * 获取单独一个Item 高度 - * @param absViews - * @param pos - * @param dfHeight - * @return - */ - public static int getItemHeighet(AbsListView absViews, int pos, int dfHeight){ - if (absViews != null){ - return getItemHeighet(absViews.getAdapter(), absViews, pos, dfHeight); - } - return dfHeight; - } - - /** - * 获取单独一个Item 高度 - * @param listAdapter - * @param absViews - * @param pos - * @return - */ - public static int getItemHeighet(ListAdapter listAdapter, AbsListView absViews, int pos){ - return getItemHeighet(listAdapter, absViews, pos, 0); - } - - /** - * 获取单独一个Item 高度 - * @param listAdapter - * @param absViews - * @param pos - * @param dfHeight - * @return - */ - public static int getItemHeighet(ListAdapter listAdapter, AbsListView absViews, int pos, int dfHeight){ - try { - // listAdapter.getCount()返回数据项的数目 - View listItem = listAdapter.getView(pos, null, absViews); - // 计算子项View 的宽高 - listItem.measure(0, 0); - // 统计所有子项的总高度 - return listItem.getMeasuredHeight(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getItemHeighet"); - return dfHeight; - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/anim/AnimationUtils.java b/DevLibUtils/src/main/java/dev/utils/app/anim/AnimationUtils.java deleted file mode 100644 index 7bd4beb5be..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/anim/AnimationUtils.java +++ /dev/null @@ -1,263 +0,0 @@ -package dev.utils.app.anim; - -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.Animation.AnimationListener; -import android.view.animation.RotateAnimation; -import android.view.animation.ScaleAnimation; - -/** - * detail: 动画工具类 - * Created by Ttt - */ -public final class AnimationUtils { - - private AnimationUtils() { - } - - /** - * 默认动画持续时间 - */ - public static final long DEFAULT_ANIMATION_DURATION = 400; - - /** - * 获取一个旋转动画 - * @param fromDegrees 开始角度 - * @param toDegrees 结束角度 - * @param pivotXType 旋转中心点X轴坐标相对类型 - * @param pivotXValue 旋转中心点X轴坐标 - * @param pivotYType 旋转中心点Y轴坐标相对类型 - * @param pivotYValue 旋转中心点Y轴坐标 - * @param durationMillis 持续时间 - * @param animationListener 动画监听器 - * @return 一个旋转动画 - */ - public static RotateAnimation getRotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue, long durationMillis, AnimationListener animationListener) { - RotateAnimation rotateAnimation = new RotateAnimation(fromDegrees, toDegrees, pivotXType, pivotXValue, pivotYType, pivotYValue); - rotateAnimation.setDuration(durationMillis); - if (animationListener != null) { - rotateAnimation.setAnimationListener(animationListener); - } - return rotateAnimation; - } - - /** - * 获取一个根据视图自身中心点旋转的动画 - * @param durationMillis 动画持续时间 - * @param animationListener 动画监听器 - * @return 一个根据中心点旋转的动画 - */ - public static RotateAnimation getRotateAnimationByCenter(long durationMillis, AnimationListener animationListener) { - return getRotateAnimation(0f, 359f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f, durationMillis, animationListener); - } - - /** - * 获取一个根据中心点旋转的动画 - * @param duration 动画持续时间 - * @return 一个根据中心点旋转的动画 - */ - public static RotateAnimation getRotateAnimationByCenter(long duration) { - return getRotateAnimationByCenter(duration, null); - } - - /** - * 获取一个根据视图自身中心点旋转的动画 - * @param animationListener 动画监听器 - * @return 一个根据中心点旋转的动画 - */ - public static RotateAnimation getRotateAnimationByCenter(AnimationListener animationListener) { - return getRotateAnimationByCenter(DEFAULT_ANIMATION_DURATION, animationListener); - } - - /** - * 获取一个根据中心点旋转的动画 - * @return 一个根据中心点旋转的动画,默认持续时间为DEFAULT_ANIMATION_DURATION - */ - public static RotateAnimation getRotateAnimationByCenter() { - return getRotateAnimationByCenter(DEFAULT_ANIMATION_DURATION, null); - } - - /** - * 获取一个透明度渐变动画 - * @param fromAlpha 开始时的透明度 - * @param toAlpha 结束时的透明度都 - * @param durationMillis 持续时间 - * @param animationListener 动画监听器 - * @return 一个透明度渐变动画 - */ - public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha, long durationMillis, AnimationListener animationListener) { - AlphaAnimation alphaAnimation = new AlphaAnimation(fromAlpha, toAlpha); - alphaAnimation.setDuration(durationMillis); - if (animationListener != null) { - alphaAnimation.setAnimationListener(animationListener); - } - return alphaAnimation; - } - - /** - * 获取一个透明度渐变动画 - * @param fromAlpha 开始时的透明度 - * @param toAlpha 结束时的透明度都 - * @param durationMillis 持续时间 - * @return 一个透明度渐变动画 - */ - public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha, long durationMillis) { - return getAlphaAnimation(fromAlpha, toAlpha, durationMillis, null); - } - - /** - * 获取一个透明度渐变动画 - * @param fromAlpha 开始时的透明度 - * @param toAlpha 结束时的透明度都 - * @param animationListener 动画监听器 - * @return 一个透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION - */ - public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha, AnimationListener animationListener) { - return getAlphaAnimation(fromAlpha, toAlpha, DEFAULT_ANIMATION_DURATION, animationListener); - } - - /** - * 获取一个透明度渐变动画 - * @param fromAlpha 开始时的透明度 - * @param toAlpha 结束时的透明度都 - * @return 一个透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION - */ - public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha) { - return getAlphaAnimation(fromAlpha, toAlpha, DEFAULT_ANIMATION_DURATION, null); - } - - /** - * 获取一个由完全显示变为不可见的透明度渐变动画 - * @param durationMillis 持续时间 - * @param animationListener 动画监听器 - * @return 一个由完全显示变为不可见的透明度渐变动画 - */ - public static AlphaAnimation getHiddenAlphaAnimation(long durationMillis, AnimationListener animationListener) { - return getAlphaAnimation(1.0f, 0.0f, durationMillis, animationListener); - } - - /** - * 获取一个由完全显示变为不可见的透明度渐变动画 - * @param durationMillis 持续时间 - * @return 一个由完全显示变为不可见的透明度渐变动画 - */ - public static AlphaAnimation getHiddenAlphaAnimation(long durationMillis) { - return getHiddenAlphaAnimation(durationMillis, null); - } - - /** - * 获取一个由完全显示变为不可见的透明度渐变动画 - * @param animationListener 动画监听器 - * @return 一个由完全显示变为不可见的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION - */ - public static AlphaAnimation getHiddenAlphaAnimation(AnimationListener animationListener) { - return getHiddenAlphaAnimation(DEFAULT_ANIMATION_DURATION, animationListener); - } - - /** - * 获取一个由完全显示变为不可见的透明度渐变动画 - * @return 一个由完全显示变为不可见的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION - */ - public static AlphaAnimation getHiddenAlphaAnimation() { - return getHiddenAlphaAnimation(DEFAULT_ANIMATION_DURATION, null); - } - - /** - * 获取一个由不可见变为完全显示的透明度渐变动画 - * @param durationMillis 持续时间 - * @param animationListener 动画监听器 - * @return 一个由不可见变为完全显示的透明度渐变动画 - */ - public static AlphaAnimation getShowAlphaAnimation(long durationMillis, AnimationListener animationListener) { - return getAlphaAnimation(0.0f, 1.0f, durationMillis, animationListener); - } - - /** - * 获取一个由不可见变为完全显示的透明度渐变动画 - * @param durationMillis 持续时间 - * @return 一个由不可见变为完全显示的透明度渐变动画 - */ - public static AlphaAnimation getShowAlphaAnimation(long durationMillis) { - return getAlphaAnimation(0.0f, 1.0f, durationMillis, null); - } - - /** - * 获取一个由不可见变为完全显示的透明度渐变动画 - * @param animationListener 动画监听器 - * @return 一个由不可见变为完全显示的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION - */ - public static AlphaAnimation getShowAlphaAnimation(AnimationListener animationListener) { - return getAlphaAnimation(0.0f, 1.0f, DEFAULT_ANIMATION_DURATION, animationListener); - } - - /** - * 获取一个由不可见变为完全显示的透明度渐变动画 - * @return 一个由不可见变为完全显示的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION - */ - public static AlphaAnimation getShowAlphaAnimation() { - return getAlphaAnimation(0.0f, 1.0f, DEFAULT_ANIMATION_DURATION, null); - } - - /** - * 获取一个缩小动画 - * @param durationMillis - * @param animationListener - * @return - */ - public static ScaleAnimation getLessenScaleAnimation(long durationMillis, AnimationListener animationListener) { - ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f, ScaleAnimation.RELATIVE_TO_SELF, ScaleAnimation.RELATIVE_TO_SELF); - scaleAnimation.setDuration(durationMillis); - scaleAnimation.setAnimationListener(animationListener); - return scaleAnimation; - } - - /** - * 获取一个缩小动画 - * @param durationMillis - * @return - */ - public static ScaleAnimation getLessenScaleAnimation(long durationMillis) { - return getLessenScaleAnimation(durationMillis, null); - } - - /** - * 获取一个缩小动画 - * @param animationListener - * @return - */ - public static ScaleAnimation getLessenScaleAnimation(AnimationListener animationListener) { - return getLessenScaleAnimation(DEFAULT_ANIMATION_DURATION, animationListener); - } - - /** - * 获取一个放大动画 - * @param durationMillis - * @param animationListener - * @return - */ - public static ScaleAnimation getAmplificationAnimation(long durationMillis, AnimationListener animationListener) { - ScaleAnimation scaleAnimation = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, ScaleAnimation.RELATIVE_TO_SELF, ScaleAnimation.RELATIVE_TO_SELF); - scaleAnimation.setDuration(durationMillis); - scaleAnimation.setAnimationListener(animationListener); - return scaleAnimation; - } - - /** - * 获取一个放大动画 - * @param durationMillis - * @return - */ - public static ScaleAnimation getAmplificationAnimation(long durationMillis) { - return getAmplificationAnimation(durationMillis, null); - } - - /** - * 获取一个放大动画 - * @param animationListener - * @return - */ - public static ScaleAnimation getAmplificationAnimation(AnimationListener animationListener) { - return getAmplificationAnimation(DEFAULT_ANIMATION_DURATION, animationListener); - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/anim/ToolAnimation.java b/DevLibUtils/src/main/java/dev/utils/app/anim/ToolAnimation.java deleted file mode 100644 index 66890943b1..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/anim/ToolAnimation.java +++ /dev/null @@ -1,129 +0,0 @@ -package dev.utils.app.anim; - -import android.graphics.ColorMatrixColorFilter; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.widget.ImageView; - -/** - * detail: 控件点击效果动画工具类 - * @Description:主要功能:控件点击效果动画工具类 - * @Prject: CommonUtilLibrary - * @Package: com.jingewenku.abrahamcaijin.commonutil - * @author: AbrahamCaiJin - * @date: 2017年05月15日 11:42 - * @Copyright: 个人版权所有 - * @Company: - * @version: 1.0.0 - */ - -public class ToolAnimation { - /** - * 给视图添加点击效果,让背景变深 - * */ - public static void addTouchDrak(View view, boolean isClick) { - view.setOnTouchListener(VIEW_TOUCH_DARK); - - if (!isClick) { - view.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - } - }); - } - } - - /** - * 给视图添加点击效果,让背景变暗 - * */ - public static void addTouchLight(View view, boolean isClick) { - view.setOnTouchListener(VIEW_TOUCH_LIGHT); - - if (!isClick) { - view.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - } - }); - } - } - - /** - * 让控件点击时,颜色变深 - * */ - public static final OnTouchListener VIEW_TOUCH_DARK = new OnTouchListener() { - - public final float[] BT_SELECTED = new float[] { 1, 0, 0, 0, -50, 0, 1, - 0, 0, -50, 0, 0, 1, 0, -50, 0, 0, 0, 1, 0 }; - public final float[] BT_NOT_SELECTED = new float[] { 1, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0 }; - - @SuppressWarnings("deprecation") - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - if (v instanceof ImageView) { - ImageView iv = (ImageView) v; - iv.setColorFilter(new ColorMatrixColorFilter(BT_SELECTED)); - } else { - v.getBackground().setColorFilter( - new ColorMatrixColorFilter(BT_SELECTED)); - v.setBackgroundDrawable(v.getBackground()); - } - } else if (event.getAction() == MotionEvent.ACTION_UP) { - if (v instanceof ImageView) { - ImageView iv = (ImageView) v; - iv.setColorFilter(new ColorMatrixColorFilter( - BT_NOT_SELECTED)); - } else { - v.getBackground().setColorFilter( - new ColorMatrixColorFilter(BT_NOT_SELECTED)); - v.setBackgroundDrawable(v.getBackground()); - } - } - return false; - } - }; - - /** - * 让控件点击时,颜色变暗 - * */ - public static final OnTouchListener VIEW_TOUCH_LIGHT = new OnTouchListener() { - - public final float[] BT_SELECTED = new float[] { 1, 0, 0, 0, 50, 0, 1, - 0, 0, 50, 0, 0, 1, 0, 50, 0, 0, 0, 1, 0 }; - public final float[] BT_NOT_SELECTED = new float[] { 1, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0 }; - - @SuppressWarnings("deprecation") - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - if (v instanceof ImageView) { - ImageView iv = (ImageView) v; - iv.setDrawingCacheEnabled(true); - - iv.setColorFilter(new ColorMatrixColorFilter(BT_SELECTED)); - } else { - v.getBackground().setColorFilter( - new ColorMatrixColorFilter(BT_SELECTED)); - v.setBackgroundDrawable(v.getBackground()); - } - } else if (event.getAction() == MotionEvent.ACTION_UP) { - if (v instanceof ImageView) { - ImageView iv = (ImageView) v; - iv.setColorFilter(new ColorMatrixColorFilter( - BT_NOT_SELECTED)); - } else { - v.getBackground().setColorFilter( - new ColorMatrixColorFilter(BT_NOT_SELECTED)); - v.setBackgroundDrawable(v.getBackground()); - } - } - return false; - } - }; -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/anim/ViewAnimationUtils.java b/DevLibUtils/src/main/java/dev/utils/app/anim/ViewAnimationUtils.java deleted file mode 100644 index cbfaf649a1..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/anim/ViewAnimationUtils.java +++ /dev/null @@ -1,519 +0,0 @@ -package dev.utils.app.anim; - -import android.view.View; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.Animation.AnimationListener; -import android.view.animation.CycleInterpolator; -import android.view.animation.TranslateAnimation; - -/** - * detail: 视图动画工具箱,提供简单的控制视图的动画的工具方法 - * Created by Ttt - */ -public final class ViewAnimationUtils { - - private ViewAnimationUtils() { - } - - // ================== - // 视图透明度渐变动画 - // ================== - - /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) - * @param view 被处理的视图 - * @param isBanClick 在执行动画的过程中是否禁止点击 - * @param durationMillis 持续时间,毫秒 - * @param animationListener 动画监听器 - */ - public static void invisibleViewByAlpha(final View view, long durationMillis, final boolean isBanClick, final AnimationListener animationListener) { - if (view.getVisibility() != View.INVISIBLE) { - view.setVisibility(View.INVISIBLE); - AlphaAnimation hiddenAlphaAnimation = AnimationUtils.getHiddenAlphaAnimation(durationMillis); - hiddenAlphaAnimation.setAnimationListener(new AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - if (isBanClick) { - view.setClickable(false); - } - if (animationListener != null) { - animationListener.onAnimationStart(animation); - } - } - - @Override - public void onAnimationRepeat(Animation animation) { - if (animationListener != null) { - animationListener.onAnimationRepeat(animation); - } - } - - @Override - public void onAnimationEnd(Animation animation) { - if (isBanClick) { - view.setClickable(true); - } - if (animationListener != null) { - animationListener.onAnimationEnd(animation); - } - } - }); - view.startAnimation(hiddenAlphaAnimation); - } - } - - /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - * @param animationListener 动画监听器 - */ - public static void invisibleViewByAlpha(final View view, long durationMillis, final AnimationListener animationListener) { - invisibleViewByAlpha(view, durationMillis, false, animationListener); - } - - /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void invisibleViewByAlpha(final View view, long durationMillis, boolean isBanClick) { - invisibleViewByAlpha(view, durationMillis, isBanClick, null); - } - - /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - */ - public static void invisibleViewByAlpha(final View view, long durationMillis) { - invisibleViewByAlpha(view, durationMillis, false, null); - } - - /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - * @param isBanClick 在执行动画的过程中是否禁止点击 - * @param animationListener 动画监听器 - */ - public static void invisibleViewByAlpha(final View view, boolean isBanClick, final AnimationListener animationListener) { - invisibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, animationListener); - } - - /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - * @param animationListener 动画监听器 - */ - public static void invisibleViewByAlpha(final View view, final AnimationListener animationListener) { - invisibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION,false, animationListener); - } - - /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void invisibleViewByAlpha(final View view, boolean isBanClick) { - invisibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, null); - } - - /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - */ - public static void invisibleViewByAlpha(final View view) { - invisibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION,false, null); - } - - /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - * @param isBanClick 在执行动画的过程中是否禁止点击 - * @param animationListener 动画监听器 - */ - public static void goneViewByAlpha(final View view, long durationMillis, final boolean isBanClick, final AnimationListener animationListener) { - if (view.getVisibility() != View.GONE) { - view.setVisibility(View.GONE); - AlphaAnimation hiddenAlphaAnimation = AnimationUtils.getHiddenAlphaAnimation(durationMillis); - hiddenAlphaAnimation.setAnimationListener(new AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - if (isBanClick) { - view.setClickable(false); - } - if (animationListener != null) { - animationListener.onAnimationStart(animation); - } - } - - @Override - public void onAnimationRepeat(Animation animation) { - if (animationListener != null) { - animationListener.onAnimationRepeat(animation); - } - } - - @Override - public void onAnimationEnd(Animation animation) { - if (isBanClick) { - view.setClickable(true); - } - if (animationListener != null) { - animationListener.onAnimationEnd(animation); - } - } - }); - view.startAnimation(hiddenAlphaAnimation); - } - } - - /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - * @param animationListener 动画监听器 - */ - public static void goneViewByAlpha(final View view, long durationMillis, final AnimationListener animationListener) { - goneViewByAlpha(view, durationMillis, false, animationListener); - } - - /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void goneViewByAlpha(final View view, long durationMillis, final boolean isBanClick) { - goneViewByAlpha(view, durationMillis, isBanClick, null); - } - - /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - */ - public static void goneViewByAlpha(final View view, long durationMillis) { - goneViewByAlpha(view, durationMillis, false, null); - } - - /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - * @param isBanClick 在执行动画的过程中是否禁止点击 - * @param animationListener 动画监听器 - */ - public static void goneViewByAlpha(final View view, final boolean isBanClick, final AnimationListener animationListener) { - goneViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, animationListener); - } - - /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - * @param animationListener 动画监听器 - */ - public static void goneViewByAlpha(final View view, final AnimationListener animationListener) { - goneViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, false, animationListener); - } - - /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void goneViewByAlpha(final View view, final boolean isBanClick) { - goneViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, null); - } - - /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - */ - public static void goneViewByAlpha(final View view) { - goneViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, false,null); - } - - /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - * @param isBanClick 在执行动画的过程中是否禁止点击 - * @param animationListener 动画监听器 - */ - public static void visibleViewByAlpha(final View view, long durationMillis, final boolean isBanClick, final AnimationListener animationListener) { - if (view.getVisibility() != View.VISIBLE) { - view.setVisibility(View.VISIBLE); - AlphaAnimation showAlphaAnimation = AnimationUtils.getShowAlphaAnimation(durationMillis); - showAlphaAnimation.setAnimationListener(new AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - if (isBanClick) { - view.setClickable(false); - } - if (animationListener != null) { - animationListener.onAnimationStart(animation); - } - } - - @Override - public void onAnimationRepeat(Animation animation) { - if (animationListener != null) { - animationListener.onAnimationRepeat(animation); - } - } - - @Override - public void onAnimationEnd(Animation animation) { - if (isBanClick) { - view.setClickable(true); - } - if (animationListener != null) { - animationListener.onAnimationEnd(animation); - } - } - }); - view.startAnimation(showAlphaAnimation); - } - } - - /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - * @param animationListener 动画监听器 - */ - public static void visibleViewByAlpha(final View view, long durationMillis, final AnimationListener animationListener) { - visibleViewByAlpha(view, durationMillis, false, animationListener); - } - - /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void visibleViewByAlpha(final View view, long durationMillis, final boolean isBanClick) { - visibleViewByAlpha(view, durationMillis, isBanClick, null); - } - - /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) - * @param view 被处理的视图 - * @param durationMillis 持续时间,毫秒 - */ - public static void visibleViewByAlpha(final View view, long durationMillis) { - visibleViewByAlpha(view, durationMillis, false, null); - } - - /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - * @param animationListener 动画监听器 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void visibleViewByAlpha(final View view, final boolean isBanClick, final AnimationListener animationListener) { - visibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, animationListener); - } - - /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - * @param animationListener 动画监听器 - */ - public static void visibleViewByAlpha(final View view, final AnimationListener animationListener) { - visibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION,false, animationListener); - } - - /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void visibleViewByAlpha(final View view, final boolean isBanClick) { - visibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, null); - } - - /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), - * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION - * @param view 被处理的视图 - */ - public static void visibleViewByAlpha(final View view) { - visibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION,false, null); - } - - // ============ - // 视图移动动画 - // ============ - - /** - * 视图移动 - * @param view 要移动的视图 - * @param fromXDelta X轴开始坐标 - * @param toXDelta X轴结束坐标 - * @param fromYDelta Y轴开始坐标 - * @param toYDelta Y轴结束坐标 - * @param cycles 重复 - * @param durationMillis 持续时间 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void translate(final View view, float fromXDelta, float toXDelta, float fromYDelta, float toYDelta, float cycles, long durationMillis, final boolean isBanClick) { - TranslateAnimation translateAnimation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta); - translateAnimation.setDuration(durationMillis); - if (cycles > 0.0) { - translateAnimation.setInterpolator(new CycleInterpolator(cycles)); - } - translateAnimation.setAnimationListener(new AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - if (isBanClick) { - view.setClickable(false); - } - } - - @Override - public void onAnimationRepeat(Animation animation) { - - } - - @Override - public void onAnimationEnd(Animation animation) { - if (isBanClick) { - view.setClickable(true); - } - } - }); - view.startAnimation(translateAnimation); - } - - /** - * 视图移动 - * @param view 要移动的视图 - * @param fromXDelta X轴开始坐标 - * @param toXDelta X轴结束坐标 - * @param fromYDelta Y轴开始坐标 - * @param toYDelta Y轴结束坐标 - * @param cycles 重复 - * @param durationMillis 持续时间 - */ - public static void translate(final View view, float fromXDelta, float toXDelta, float fromYDelta, float toYDelta, float cycles, long durationMillis) { - translate(view, fromXDelta, toXDelta, fromYDelta, toYDelta, cycles, durationMillis, false); - } - - /** - * 视图摇晃 - * @param view 要摇动的视图 - * @param fromXDelta X轴开始坐标 - * @param toXDelta X轴结束坐标 - * @param cycles 重复次数 - * @param durationMillis 持续时间 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void shake(View view, float fromXDelta, float toXDelta, float cycles, long durationMillis, final boolean isBanClick) { - translate(view, fromXDelta, toXDelta, 0.0f, 0.0f, cycles, durationMillis, isBanClick); - } - - /** - * 视图摇晃 - * @param view 要摇动的视图 - * @param fromXDelta X轴开始坐标 - * @param toXDelta X轴结束坐标 - * @param cycles 重复次数 - * @param durationMillis 持续时间 - */ - public static void shake(View view, float fromXDelta, float toXDelta, float cycles, long durationMillis) { - translate(view, fromXDelta, toXDelta, 0.0f, 0.0f, cycles, durationMillis, false); - } - - /** - * 视图摇晃,默认摇晃幅度为10,重复7次 - * @param view - * @param cycles 重复次数 - * @param durationMillis 持续时间 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void shake(View view, float cycles, long durationMillis, final boolean isBanClick) { - translate(view, 0.0f, 10.0f, 0.0f, 0.0f, cycles, durationMillis, isBanClick); - } - - /** - * 视图摇晃,默认摇晃幅度为10,持续700毫秒 - * @param view - * @param cycles 重复次数 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void shake(View view, float cycles, final boolean isBanClick) { - translate(view, 0.0f, 10.0f, 0.0f, 0.0f, cycles, 700, isBanClick); - } - - /** - * 视图摇晃,默认摇晃幅度为10 - * @param view - * @param cycles 重复次数 - * @param durationMillis 持续时间 - */ - public static void shake(View view, float cycles, long durationMillis) { - translate(view, 0.0f, 10.0f, 0.0f, 0.0f, cycles, durationMillis, false); - } - - /** - * 视图摇晃,默认摇晃幅度为10,重复7次 - * @param view - * @param durationMillis 持续时间 - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void shake(View view, long durationMillis, final boolean isBanClick) { - translate(view, 0.0f, 10.0f, 0.0f, 0.0f, 7, durationMillis, isBanClick); - } - - /** - * 视图摇晃,默认摇晃幅度为10,持续700毫秒 - * @param view 要摇动的视图 - * @param cycles 重复次数 - */ - public static void shake(View view, float cycles) { - translate(view, 0.0f, 10.0f, 0.0f, 0.0f, cycles, 700, false); - } - - /** - * 视图摇晃,默认摇晃幅度为10,重复7次 - * @param view - * @param durationMillis 持续时间 - */ - public static void shake(View view, long durationMillis) { - translate(view, 0.0f, 10.0f, 0.0f, 0.0f, 7, durationMillis, false); - } - - /** - * 视图摇晃,默认摇晃幅度为10,重复7次,持续700毫秒 - * @param view - * @param isBanClick 在执行动画的过程中是否禁止点击 - */ - public static void shake(View view, final boolean isBanClick) { - translate(view, 0.0f, 10.0f, 0.0f, 0.0f, 7, 700, isBanClick); - } - - /** - * 视图摇晃,默认摇晃幅度为10,重复7次,持续700毫秒 - * @param view - */ - public static void shake(View view) { - translate(view, 0.0f, 10.0f, 0.0f, 0.0f, 7, 700, false); - } - -} - diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/AsyncExecutor.java b/DevLibUtils/src/main/java/dev/utils/app/assist/AsyncExecutor.java deleted file mode 100644 index 1dca496507..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/AsyncExecutor.java +++ /dev/null @@ -1,129 +0,0 @@ -package dev.utils.app.assist; - -import android.os.Handler; -import android.os.Looper; - -import java.util.concurrent.Callable; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; - -import dev.utils.LogPrintUtils; - -/** - * detail: 异步执行 - * @author MaTianyu - */ -public class AsyncExecutor { - - // 日志TAG - private final String TAG = AsyncExecutor.class.getSimpleName(); - // 线程池 - private static ExecutorService threadPool; - // 主线程 Hander - public static Handler handler = new Handler(Looper.getMainLooper()); - - public AsyncExecutor() { - this(null); - } - - public AsyncExecutor(ExecutorService threadPool) { - if (AsyncExecutor.threadPool != null) { - shutdownNow(); - } - if (threadPool == null) { - AsyncExecutor.threadPool = Executors.newCachedThreadPool(); - } else { - AsyncExecutor.threadPool = threadPool; - } - } - - public static synchronized void shutdownNow() { - if (threadPool != null && !threadPool.isShutdown()) threadPool.shutdownNow(); - threadPool = null; - } - - /** - * 将任务投入线程池执行 - * @param worker - * @return - */ - public FutureTask execute(final Worker worker) { - Callable call = new Callable() { - @Override - public T call() throws Exception { - return postResult(worker, worker.doInBackground()); - } - }; - FutureTask task = new FutureTask(call) { - @Override - protected void done() { - try { - get(); - } catch (InterruptedException e) { - LogPrintUtils.eTag(TAG, e, "execute"); - worker.abort(); - postCancel(worker); - } catch (ExecutionException e) { - LogPrintUtils.eTag(TAG, e, "execute"); - throw new RuntimeException("An error occured while executing doInBackground()", e.getCause()); - } catch (CancellationException e) { - worker.abort(); - postCancel(worker); - LogPrintUtils.eTag(TAG, e, "execute"); - } - } - }; - threadPool.execute(task); - return task; - } - - /** - * 将子线程结果传递到UI线程 - * @param worker - * @param result - * @return - */ - private T postResult(final Worker worker, final T result) { - handler.post(new Runnable() { - @Override - public void run() { - worker.onPostExecute(result); - } - }); - return result; - } - - /** - * 将子线程结果传递到UI线程 - * @param worker - * @return - */ - private void postCancel(final Worker worker) { - handler.post(new Runnable() { - @Override - public void run() { - worker.onCanceled(); - } - }); - } - - public FutureTask execute(Callable call) { - FutureTask task = new FutureTask(call); - threadPool.execute(task); - return task; - } - - public static abstract class Worker { - - protected abstract T doInBackground(); - - protected void onPostExecute(T data) {} - - protected void onCanceled() {} - - protected void abort() {} - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/BeepVibrateAssist.java b/DevLibUtils/src/main/java/dev/utils/app/assist/BeepVibrateAssist.java deleted file mode 100644 index 3f00033fde..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/BeepVibrateAssist.java +++ /dev/null @@ -1,279 +0,0 @@ -package dev.utils.app.assist; - -import android.app.Activity; -import android.content.Context; -import android.content.res.AssetFileDescriptor; -import android.media.AudioManager; -import android.media.MediaPlayer; -import android.os.Vibrator; -import android.support.annotation.RawRes; - -import java.io.Closeable; - -import dev.utils.LogPrintUtils; - -/** - * detail: 播放“bee”的声音, 并且震动 辅助类 - * Created by Ttt - */ -public final class BeepVibrateAssist implements Closeable { - - // 日志TAG - private static final String TAG = BeepVibrateAssist.class.getSimpleName(); - // 上下文 - private final Context context; - // 播放资源对象 - private MediaPlayer mediaPlayer = null; - // 是否需要震动 - private boolean vibrate = true; - // 震动时间 - private long vibrateDuration = 200L; - - /** - * 构造函数 - * @param context - */ - public BeepVibrateAssist(Context context) { - this.context = context; - } - - /** - * 构造函数 - * @param context - * @param rawId - */ - public BeepVibrateAssist(Context context, @RawRes int rawId) { - this.context = context; - this.mediaPlayer = buildMediaPlayer(context, rawId); - } - - /** - * 构造函数 - * @param context - * @param path 只支持本地资源 - */ - public BeepVibrateAssist(Context context, String path) { - this.context = context; - this.mediaPlayer = buildMediaPlayer(path); - } - - // == 内部判断方法 == - - /** - * 检查是否允许播放声音 - * @return - */ - private boolean shouldBeep() { - boolean shouldPlayBeep = true; - if (shouldPlayBeep) { - try { - // RINGER_MODE_NORMAL(普通)、RINGER_MODE_SILENT(静音)、RINGER_MODE_VIBRATE(震动) - AudioManager audioService = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) { - shouldPlayBeep = false; // 进入只有属于, 静音、震动,才不播放 - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "shouldBeep"); - } - } - return shouldPlayBeep; - } - - /** - * 内部检查更新 - */ - private synchronized void update() { - if (shouldBeep() && mediaPlayer != null) { - // The volume on STREAM_SYSTEM is not adjustable, and users found it too loud, - // so we now play on the music stream. - try { - ((Activity) context).setVolumeControlStream(AudioManager.STREAM_MUSIC); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "update"); - } - } - } - - // == 对外公开方法 == - - /** - * 判断是否允许播放声音 - * @return - */ - public boolean isPlayBeep() { - return shouldBeep(); - } - - /** - * 获取是否允许震动 - * @return - */ - public boolean isVibrate() { - return vibrate; - } - - /** - * 设置是否允许震动 - * @param vibrate - */ - public BeepVibrateAssist setVibrate(boolean vibrate) { - setVibrate(vibrate, 200l); - return this; - } - - /** - * 设置是否允许震动 - * @param vibrate - * @param vibrateDuration 震动时间 - */ - public BeepVibrateAssist setVibrate(boolean vibrate, long vibrateDuration) { - this.vibrate = vibrate; - return this; - } - - /** - * 设置播放资源对象 - * @param mediaPlayer - */ - public BeepVibrateAssist setMediaPlayer(MediaPlayer mediaPlayer) { - this.mediaPlayer = mediaPlayer; - // 进行更新 - update(); - return this; - } - - /** - * 进行播放声音, 并且振动 - */ - public synchronized void playBeepSoundAndVibrate() { - // 判断是否允许播放 - if (shouldBeep() && mediaPlayer != null) { - try { - // 播放 - mediaPlayer.start(); - } catch (Exception e){ - } - } - // 判断是否允许震动 - if (vibrate) { - try { - Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); - vibrator.vibrate(vibrateDuration); - } catch (Exception e){ - } - } - } - - @Override - public synchronized void close() { - if (mediaPlayer != null) { - mediaPlayer.release(); - mediaPlayer = null; - } - } - - // == 创建 MediaPlayer 处理 == - - /** - * 创建 MediaPlayer 对象 - * @param context 上下文 - * @param rawId 响声资源id - * @return - */ - public static final MediaPlayer buildMediaPlayer(Context context, @RawRes int rawId) { - return buildMediaPlayer(context, rawId, 0.1f); - } - - /** - * 创建 MediaPlayer 对象 - * @param context 上下文 - * @param rawId 响声资源id - * @param beepVolume 音量 - * @return - */ - public static final MediaPlayer buildMediaPlayer(Context context, @RawRes int rawId, float beepVolume) { - final MediaPlayer mediaPlayer = new MediaPlayer(); - mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); - mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - @Override - public void onCompletion(MediaPlayer mp) { - LogPrintUtils.dTag(TAG, "buildMediaPlayer - onCompletion"); -// if (mediaPlayer != null){ -// mediaPlayer.seekTo(0); -// } - } - }); - mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { - @Override - public synchronized boolean onError(MediaPlayer mp, int what, int extra) { - LogPrintUtils.dTag(TAG, "buildMediaPlayer - onError => what: " + what + ", extra: " + extra); - // 播放异常, 直接不处理 - return true; - } - }); - try { - AssetFileDescriptor file = context.getResources().openRawResourceFd(rawId); - try { - mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); - } finally { - file.close(); - } - mediaPlayer.setVolume(beepVolume, beepVolume); - mediaPlayer.prepare(); - return mediaPlayer; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "buildMediaPlayer"); - mediaPlayer.release(); - return null; - } - } - - // = - - /** - * 创建 MediaPlayer 对象 - * @param path 响声资源路径(只支持本地资源) - * @return - */ - public static final MediaPlayer buildMediaPlayer(String path) { - return buildMediaPlayer(path, 0.1f); - } - - /** - * 创建 MediaPlayer 对象 - * @param path 响声资源路径(只支持本地资源) - * @param beepVolume 音量 - * @return - */ - public static final MediaPlayer buildMediaPlayer(String path, float beepVolume) { - final MediaPlayer mediaPlayer = new MediaPlayer(); - mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); - mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - @Override - public void onCompletion(MediaPlayer mp) { - LogPrintUtils.dTag(TAG, "buildMediaPlayer - onCompletion"); -// if (mediaPlayer != null){ -// mediaPlayer.seekTo(0); -// } - } - }); - mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { - @Override - public synchronized boolean onError(MediaPlayer mp, int what, int extra) { - LogPrintUtils.dTag(TAG, "buildMediaPlayer - onError => what: " + what + ", extra: " + extra); - // 播放异常, 直接不处理 - return true; - } - }); - try { - mediaPlayer.setDataSource(path); - mediaPlayer.setVolume(beepVolume, beepVolume); - mediaPlayer.prepare(); - return mediaPlayer; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "buildMediaPlayer"); - mediaPlayer.release(); - return null; - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java b/DevLibUtils/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java deleted file mode 100644 index ecd5f9444c..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java +++ /dev/null @@ -1,145 +0,0 @@ -package dev.utils.app.assist; - -import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.AsyncTask; -import android.os.BatteryManager; -import android.os.Build; - -/** - * detail: Activity 无操作定时辅助类 - * Created by Ttt - * 需要注意的试, 需要在对应的生命周期内,调用对应的 onXXX 方法 - */ -public final class InactivityTimerAssist { - - // 无操作时间(到时间自动关闭) - 默认五分钟 - private long inactivityTime = 5 * 60 * 1000L; - // 对应的页面 - private Activity activity; - // 电池广播(充电中, 则不处理, 主要是为了省点) - private BroadcastReceiver powerStatusReceiver; - // 检查任务 - private AsyncTask inactivityTask; - - // == 构造函数 == - - public InactivityTimerAssist(Activity activity) { - this(activity, 5 * 60 * 1000L); - } - - public InactivityTimerAssist(Activity activity, long inactivityTime) { - this.activity = activity; - this.inactivityTime = inactivityTime; - // 电池广播监听 - powerStatusReceiver = new PowerStatusReceiver(); - // 关闭任务 - cancel(); - } - - // = 内部方法 = - - /** - * 开始任务 - */ - public synchronized void start() { - // 取消任务 - cancel(); - // 注册任务 - inactivityTask = new InactivityAsyncTask(); - // 开启任务 - if (Build.VERSION.SDK_INT >= 11) { - inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - inactivityTask.execute(); - } - } - - /** - * 取消任务 - */ - private synchronized void cancel() { - AsyncTask task = inactivityTask; - if (task != null) { - task.cancel(true); - // 重置为null - inactivityTask = null; - } - } - - // = - - /** - * 暂停检测 - */ - public synchronized void onPause() { - // 取消任务 - cancel(); - try { - // 取消注册广播 - activity.unregisterReceiver(powerStatusReceiver); - } catch (Exception e){ - } - } - - /** - * 回到页面处理 - */ - public synchronized void onResume() { - try { - // 注册广播 - activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - } catch (Exception e){ - } - // 开始检测 - start(); - } - - /** - * 页面销毁处理 - */ - public void onDestroy() { - cancel(); - } - - // = - - /** - * 电池广播 - */ - private class PowerStatusReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { - // 0 indicates that we're on battery - boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0; - if (onBatteryNow) { // 属于非充电才进行记时 - InactivityTimerAssist.this.start(); - } else { // 充电中, 则不处理 - InactivityTimerAssist.this.cancel(); - } - } - } - } - - /** - * 定时检测任务 - */ - private class InactivityAsyncTask extends AsyncTask { - @Override - protected Object doInBackground(Object... objects) { - try { - Thread.sleep(inactivityTime); - // 关闭页面 - if (activity != null){ - activity.finish(); - } - } catch (InterruptedException e) { - } - return null; - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java b/DevLibUtils/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java deleted file mode 100644 index 0828bbd4ff..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java +++ /dev/null @@ -1,254 +0,0 @@ -package dev.utils.app.assist; - -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.Handler; -import android.os.Message; - -import dev.utils.LogPrintUtils; - -/** - * detail: 屏幕传感器(监听是否横竖屏) - * Created by Ttt - */ -public final class ScreenSensorAssist { - - // 日志 TAG - private final String TAG = ScreenSensorAssist.class.getSimpleName(); - - // ========= 重力传感器监听对象 ============= - /** 传感器管理对象 */ - private SensorManager sMamager; - /** 重力传感器 */ - private Sensor sensor; - /** 重力传感器监听事件 */ - private OrientationSensorListener sListener; - // ========= 重力传感器监听对象(改变方向后,具体判断参数不同) ============= - /** 传感器管理对象(切屏后) */ - private SensorManager sManagerChange; - /** 重力传感器(切屏后) */ - private Sensor sensorChange; - /** 重力传感器监听事件(切屏后) */ - private OrientationSensorChangeListener slistenerChange; - // ========= 常量 ========= - /** 坐标索引常量 */ - private final int _DATA_X = 0; - private final int _DATA_Y = 1; - private final int _DATA_Z = 2; - /** 方向未知常量 */ - private final int ORIENTATION_UNKNOWN = -1; - /** 触发屏幕方向改变回调 */ - public static final int CHANGE_ORIENTATION_WHAT = 9919; - // ========= 变量 ============= - /** 是否允许切屏 */ - private boolean isAllowChange = false; - /** 是否是竖屏 */ - private boolean isPortrait = true; - /** 回调操作 */ - private Handler handler; - /** 角度处理Handler */ - private Handler rotateHandler = new Handler(){ - public void handleMessage(Message msg) { - switch (msg.what) { - case CHANGE_ORIENTATION_WHAT: - // 获取角度 - int rotation = msg.arg1; - // - - LogPrintUtils.dTag(TAG,"当前角度: " + rotation); - // 判断角度 - if (rotation > 45 && rotation < 135) { // 横屏 - 屏幕对着别人 - LogPrintUtils.dTag(TAG, "切换成横屏 - 屏幕对着自己"); - // - - if (isPortrait) { - isPortrait = false; - if(handler != null){ - Message vMsg = new Message(); - vMsg.what = CHANGE_ORIENTATION_WHAT; - vMsg.arg1 = 1; - handler.sendMessage(vMsg); - } - } - } else if (rotation > 135 && rotation < 225) { // 竖屏 - 屏幕对着别人 - LogPrintUtils.dTag(TAG,"切换成竖屏 - 屏幕对着别人"); - // - - if (!isPortrait) { - isPortrait = true; - if(handler != null){ - Message vMsg = new Message(); - vMsg.what = CHANGE_ORIENTATION_WHAT; - vMsg.arg1 = 2; - handler.sendMessage(vMsg); - } - } - } else if (rotation > 225 && rotation < 315) { // 横屏 - 屏幕对着自己 - LogPrintUtils.dTag(TAG, "切换成横屏 - 屏幕对着自己"); - // - - if (isPortrait) { - isPortrait = false; - if(handler != null){ - Message vMsg = new Message(); - vMsg.what = CHANGE_ORIENTATION_WHAT; - vMsg.arg1 = 1; - handler.sendMessage(vMsg); - } - } - } else if ((rotation > 315 && rotation < 360) || (rotation > 0 && rotation < 45)) { // 竖屏 - 屏幕对着自己 - LogPrintUtils.dTag(TAG,"切换成竖屏 - 屏幕对着自己"); - // - - if (!isPortrait) { - isPortrait = true; - if(handler != null){ - Message vMsg = new Message(); - vMsg.what = CHANGE_ORIENTATION_WHAT; - vMsg.arg1 = 2; - handler.sendMessage(vMsg); - } - } - } else { - LogPrintUtils.dTag(TAG,"其他角度: " + rotation); - } - break; - } - }; - }; - - // === - /** - * 初始化操作 - * @param context 上下文 - * @param handler 回调Handler - */ - private void init(Context context, Handler handler){ - this.handler = handler; - // 注册重力感应器,监听屏幕旋转 - sMamager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - sensor = sMamager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - sListener = new OrientationSensorListener(); - - // 根据 旋转之后/点击全屏之后 两者方向一致,激活sm. - sManagerChange = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - sensorChange = sManagerChange.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - slistenerChange = new OrientationSensorChangeListener(); - } - - /** - * 开始监听 - * @param context - */ - public void start(Context context, Handler handler) { - isAllowChange = true; - try { - LogPrintUtils.dTag(TAG, "start orientation listener."); - // 初始化操作 - init(context, handler); - // 监听重力传感器 - sMamager.registerListener(sListener, sensor, SensorManager.SENSOR_DELAY_UI); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "start"); - } - } - - /** 停止监听 */ - public void stop() { - isAllowChange = false; - LogPrintUtils.dTag(TAG, "stop orientation listener."); - try { - sMamager.unregisterListener(sListener); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e ,"stop"); - } - try { - sManagerChange.unregisterListener(slistenerChange); - } catch (Exception e) { - } - } - - /** 是否竖屏 */ - public boolean isPortrait(){ - return this.isPortrait; - } - - /** 是否允许切屏 */ - public boolean isAllowChange(){ - return this.isAllowChange; - } - - // === - - /** 重力传感器监听事件 */ - class OrientationSensorListener implements SensorEventListener { - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - } - @Override - public void onSensorChanged(SensorEvent event) { - float[] values = event.values; - int orientation = ORIENTATION_UNKNOWN; - float X = -values[_DATA_X]; - float Y = -values[_DATA_Y]; - float Z = -values[_DATA_Z]; - float magnitude = X * X + Y * Y; - // Don't trust the angle if the magnitude is small compared to the y value - if (magnitude * 4 >= Z * Z) { - // 屏幕旋转时 - float OneEightyOverPi = 57.29577957855f; - float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi; - orientation = 90 - (int) Math.round(angle); - // normalize to 0 - 359 range - while (orientation >= 360) { - orientation -= 360; - } - while (orientation < 0) { - orientation += 360; - } - } - if (rotateHandler != null) { - rotateHandler.obtainMessage(CHANGE_ORIENTATION_WHAT, orientation, 0).sendToTarget(); - } - } - } - - /** 重力传感器监听事件(切屏后) */ - class OrientationSensorChangeListener implements SensorEventListener { - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - } - @Override - public void onSensorChanged(SensorEvent event) { - float[] values = event.values; - int orientation = ORIENTATION_UNKNOWN; - float X = -values[_DATA_X]; - float Y = -values[_DATA_Y]; - float Z = -values[_DATA_Z]; - float magnitude = X * X + Y * Y; - // Don't trust the angle if the magnitude is small compared to the y value - if (magnitude * 4 >= Z * Z) { - // 屏幕旋转时 - float OneEightyOverPi = 57.29577957855f; - float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi; - orientation = 90 - (int) Math.round(angle); - // normalize to 0 - 359 range - while (orientation >= 360) { - orientation -= 360; - } - while (orientation < 0) { - orientation += 360; - } - } - if (orientation > 225 && orientation < 315) {// 检测到当前实际是横屏 - if (!isPortrait) { - sMamager.registerListener(sListener, sensor, SensorManager.SENSOR_DELAY_UI); - sManagerChange.unregisterListener(slistenerChange); - } - } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) {// 检测到当前实际是竖屏 - if (isPortrait) { - sMamager.registerListener(sListener, sensor, SensorManager.SENSOR_DELAY_UI); - sManagerChange.unregisterListener(slistenerChange); - } - } - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraSizeAssist.java b/DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraSizeAssist.java deleted file mode 100644 index 1093b6b1b2..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraSizeAssist.java +++ /dev/null @@ -1,990 +0,0 @@ -package dev.utils.app.assist.camera; - -import android.graphics.Point; -import android.hardware.Camera; - -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -import dev.utils.LogPrintUtils; -import dev.utils.app.ScreenUtils; - -/** - * detail: 摄像头 预览、输出大小 辅助类 - * Created by Ttt - * 摄像头 - * 震动 - * 手电筒 - * 正常只需要摄像头权限 - */ -public final class CameraSizeAssist { - - // 日志TAG - private final String TAG = CameraSizeAssist.class.getSimpleName(); - // 摄像头 Camera - private Camera mCamera; - // 默认最大的偏差 - private final double MAX_ASPECT_DISTORTION = 0.15; - // 最小尺寸, 小于该尺寸则不处理 - private final int MIN_PREVIEW_PIXELS = 480 * 320; - - // == 构造函数 == - - public CameraSizeAssist(Camera camera) { - this.mCamera = camera; - } - - /** - * 获取摄像头 - * @return - */ - public Camera getCamera() { - return mCamera; - } - - // ================== - // == 预览大小相关 == - // ================== - - /** - * 设置预览大小 - * @param previewSize - * @return - */ - public CameraSizeAssist setPreviewSize(Camera.Size previewSize){ - return setPreviewSize(mCamera, previewSize); - } - - /** - * 设置预览大小 - * @param camera - * @param previewSize - * @return - */ - public CameraSizeAssist setPreviewSize(Camera camera, Camera.Size previewSize){ - if (camera != null && previewSize != null){ - try { - // 设置预览大小 - camera.getParameters().setPreviewSize(previewSize.width, previewSize.height); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setPreviewSize"); - } - } - return this; - } - - // = - - /** - * 根据手机支持的预览分辨率计算,设置预览尺寸 - * @return - */ - public Camera.Size getPreviewSize(){ - return getPreviewSize(null, -1d); - } - - /** - * 根据手机支持的预览分辨率计算,设置预览尺寸 - * @param point - * @return - */ - public Camera.Size getPreviewSize(Point point) { - return getPreviewSize(point, -1d); - } - - /** - * 根据手机支持的预览分辨率计算,设置预览尺寸 - * @distortion point - * @return - */ - public Camera.Size getPreviewSize(double distortion) { - return getPreviewSize(null, distortion); - } - - /** - * 根据手机支持的预览分辨率计算,设置预览尺寸(无任何操作, 单独把Camera显示到SurfaceView 预览尺寸) - * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) - * @param distortion 偏差比例值 - * @return - */ - public Camera.Size getPreviewSize(Point point, double distortion) { - if (mCamera == null){ - LogPrintUtils.dTag(TAG, "camera is null"); - return null; - } - try { - // 计算大小并返回 - return calcPreviewSize(point, distortion); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getPreviewSize"); - } - return null; - } - - // ================== - // == 拍照大小相关 == - // ================== - - /** - * 设置拍照图片大小 - * @param pictureSize - * @return - */ - public CameraSizeAssist setPictureSize(Camera.Size pictureSize){ - return setPictureSize(mCamera, pictureSize); - } - - /** - * 设置拍照图片大小 - * @param camera - * @param pictureSize - * @return - */ - public CameraSizeAssist setPictureSize(Camera camera, Camera.Size pictureSize){ - if (camera != null && pictureSize != null){ - try { - // 设置预览大小 - camera.getParameters().setPictureSize(pictureSize.width, pictureSize.height); -// // 设置拍照输出格式 -// camera.getParameters().setPictureFormat(PixelFormat.JPEG); -// // 照片质量 -// camera.getParameters().set("jpeg-quality", 70); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "setPictureSize"); - } - } - return this; - } - - // = - - /** - * 根据手机支持的拍照分辨率计算 - * @return - */ - public Camera.Size getPictureSize(){ - return getPictureSize(false, null, -1d); - } - - /** - * 根据手机支持的拍照分辨率计算 - * @param max - * @return - */ - public Camera.Size getPictureSize(boolean max){ - return getPictureSize(max, null, -1d); - } - - /** - * 根据手机支持的拍照分辨率计算 - * @param point - */ - public Camera.Size getPictureSize(Point point) { - return getPictureSize(false, point, -1d); - } - - /** - * 根据手机支持的拍照分辨率计算 - * @param distortion - */ - public Camera.Size getPictureSize(double distortion) { - return getPictureSize(false, null, distortion); - } - - /** - * 根据手机支持的拍照分辨率计算 - * @param point - * @param distortion - */ - public Camera.Size getPictureSize(Point point, double distortion) { - return getPictureSize(false, point, distortion); - } - - /** - * 根据手机支持的拍照分辨率计算,设置预览尺寸 - * @param max 是否使用最大的尺寸 - * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) - * @param distortion 偏差比例值 - */ - public Camera.Size getPictureSize(boolean max, Point point, double distortion) { - if (mCamera == null){ - LogPrintUtils.dTag(TAG, "camera is null"); - return null; - } - try { - // 计算大小并返回 - return calcPictureSize(max, point, distortion); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getPictureSize"); - } - return null; - } - - // ====================== - // == 视频录制大小相关 == - // ====================== - - /** - * 根据手机支持的视频录制分辨率计算 - * @return - */ - public Camera.Size getVideoSize(){ - return getVideoSize(false, null, -1d, false); - } - - /** - * 根据手机支持的视频录制分辨率计算 - * @param max - * @return - */ - public Camera.Size getVideoSize(boolean max){ - return getVideoSize(max, null, -1d, false); - } - - /** - * 根据手机支持的视频录制分辨率计算 - * @param point - */ - public Camera.Size getVideoSize(Point point) { - return getVideoSize(false, point, -1d, false); - } - - /** - * 根据手机支持的视频录制分辨率计算 - * @param distortion - */ - public Camera.Size getVideoSize(double distortion) { - return getVideoSize(false, null, distortion, false); - } - - /** - * 根据手机支持的视频录制分辨率计算 - * @param point - * @param distortion - */ - public Camera.Size getVideoSize(Point point, double distortion) { - return getVideoSize(false, point, distortion, false); - } - - /** - * 根据手机支持的视频录制分辨率计算,设置预览尺寸 - * @param max 是否使用最大的尺寸 - * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) - * @param distortion 偏差比例值 - * @param minAccord 是否存在最小使用最小 - */ - public Camera.Size getVideoSize(boolean max, Point point, double distortion, boolean minAccord) { - if (mCamera == null){ - LogPrintUtils.dTag(TAG, "camera is null"); - return null; - } - try { - // 计算大小并返回 - return calcVideoSize(max, point, distortion, minAccord); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getVideoSize"); - } - return null; - } - - // ================== - // == 内部处理方法 == - // ================== - - // 暂时不使用 - /** - * 根据对应的尺寸, 计算相应最符合的大小 - * @param lists 摄像头尺寸大小(预览、拍照、视频) - * @param point point.x = > 宽, point.y => 高 (从指定的宽高, 开始往下(超过的不处理)选择最符合的尺寸) - */ - private Camera.Size calcSize(List lists, Point point) { - if (lists == null){ - return null; - } - // 判断是否竖屏 - boolean isPortrait = false; - // 尺寸大小 - Camera.Size mSize = null; - try { - // 获取屏幕宽、高 - int sWidth = (point != null) ? point.x : ScreenUtils.getScreenWidth(); - int sHeight = (point != null) ? point.y : ScreenUtils.getScreenHeight(); - // 如果高度大于宽度, 则表示属于竖屏 - isPortrait = sHeight > sWidth; - // 进行排序处理 -> 并以宽度为基准降序排序 - Collections.sort(lists, new Comparator() { - @Override - public int compare(Camera.Size lhs, Camera.Size rhs) { - if (lhs.width > rhs.width) { - return -1; - } else if (lhs.width == rhs.width) { - return 0; - } else { - return 1; - } - } - }); - // 遍历尺寸大小 - for (Camera.Size size : lists) { - // 判断横竖屏 - if (isPortrait){ // 属于竖屏 => 高度 > 宽度 - // 因为是竖屏, 所以判断需要倒着过来 - if (sWidth == size.height && sHeight == size.width){ - // 保存符合比例的大小 - mSize = size; - break; - } - // 计算合适的比例 - if (size.width >= sHeight){ - mSize = size; - } - } else { // 属于横屏 => 宽度 > 高度 - // 因为是横屏, 所以判断需要正常 - if (sWidth == size.width && sHeight == size.height){ - // 保存符合比例的大小 - mSize = size; - break; - } - // 计算合适的比例 - if (size.height >= sWidth){ - mSize = size; - } - } - } - // 获取最合适的比例 - LogPrintUtils.dTag(TAG, "返回 calcSize -> 宽度: " + mSize.width + ", 高度: " + mSize.height + ", 是否竖屏: " + isPortrait); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "异常 calcSize - 是否竖屏: " + isPortrait); - } - return mSize; - } - - // ============ - // = 预览大小 = - // ============ - - /** - * 根据对应的尺寸, 计算相应最符合的大小 - * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) - * @param distortion 偏差比例值 - * hint: point.x = > 宽, point.y => 高 - */ - private Camera.Size calcPreviewSize(Point point, double distortion) { - // 判断是否为null - if (point == null){ - point = ScreenUtils.getScreenWidthHeightToPoint(); - } - // 如果误差为负数, 则使用默认值 - if (distortion < 0){ - distortion = MAX_ASPECT_DISTORTION; - } - // 获取 Camera 参数 - Camera.Parameters params = mCamera.getParameters(); - // 获取手机支持的分辨率集合,并以宽度为基准降序排序 - List listPreviewSizes = params.getSupportedPreviewSizes(); - // 防止数据为null - if (listPreviewSizes == null){ - // 获取默认预览大小 - Camera.Size defaultSize = params.getPreviewSize(); - return defaultSize; - } - - // 进行排序处理 -> 并以宽度 * 高度 为基准降序排序 - Collections.sort(listPreviewSizes, new Comparator() { - @Override - public int compare(Camera.Size lhs, Camera.Size rhs) { - int leftPixels = lhs.height * lhs.width; - int rightPixels = rhs.height * rhs.width; - - if (leftPixels > rightPixels) { - return -1; - } else if (leftPixels == rightPixels) { - return 0; - } else { - return 1; - } - } - }); - - // ==== 打印信息 ==== - if (LogPrintUtils.isPrintLog()){ - StringBuilder builder = new StringBuilder(); - builder.append("预览支持尺寸: \r\n"); - // 打印信息 - for (Camera.Size previewSize : listPreviewSizes){ - // 例: 1080x1920 - builder.append(previewSize.width).append("x").append(previewSize.height).append("\r\n"); - } - // 打印尺寸信息 - LogPrintUtils.dTag(TAG, builder.toString()); - } - - // == - - // 判断是否竖屏 point.x = > 宽, point.y => 高 - boolean isPortrait = point.y > point.x; - // 如果是竖屏, 则修改 - if (isPortrait){ - int tempY = point.y; - int tempX = point.x; - // 进行转换 - point.x = tempY; - point.y = tempX; - } - - // 计算比例值 宽 / 高 - double screenAspectRatio = (double) point.x / (double) point.y; - - // 循环遍历判断 - Iterator iterator = listPreviewSizes.iterator(); - while (iterator.hasNext()) { - // 获取预览大小 - Camera.Size previewSize = iterator.next(); - // 获取宽、高 - int realWidth = previewSize.width; - int realHeight = previewSize.height; - // 小于最小尺寸, 则不处理 - if (realWidth * realHeight < MIN_PREVIEW_PIXELS) { - iterator.remove(); - continue; - } - - // 判断预选的尺寸是否竖屏 - boolean isCandidatePortrait = realWidth < realHeight; - // 翻转宽、高 - int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; - int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; - - // 计算比例 - double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight; - double calcDistortion = Math.abs(aspectRatio - screenAspectRatio); - - // 如果大于指定的尺寸比例差, 则跳过 - if (calcDistortion > distortion) { - iterator.remove(); - continue; - } - - // 如果相符, 则直接跳过 - if (maybeFlippedWidth == point.x && maybeFlippedHeight == point.y) { - return previewSize; - } - } - - // 如果没有精确匹配,请使用最大预览大小。这对于旧设备来说不是一个好主意,因为需要额外的计算。我们很可能会在新的Android 4 + 设备上运行,那里的CPU功能更强大。 - if (!listPreviewSizes.isEmpty()) { - // 获取最大的尺寸 - Camera.Size largestPreview = listPreviewSizes.get(0); - return largestPreview; - } - - // = 都不匹配, 则用默认分辨率 = - // 获取默认预览大小 - Camera.Size defaultSize = params.getPreviewSize(); - return defaultSize; - } - - // ============ - // = 拍照大小 = - // ============ - - /** - * 根据对应的尺寸, 计算相应最符合的大小 - * @param max 是否使用最大的尺寸 - * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) - * @param distortion 偏差比例值 - * hint: point.x = > 宽, point.y => 高 - */ - private Camera.Size calcPictureSize(boolean max, Point point, double distortion) { - // 判断是否为null - if (point == null){ - point = ScreenUtils.getScreenWidthHeightToPoint(); - } - // 如果误差为负数, 则使用默认值 - if (distortion < 0){ - distortion = MAX_ASPECT_DISTORTION; - } - // 获取 Camera 参数 - Camera.Parameters params = mCamera.getParameters(); - // 获取手机支持的分辨率集合,并以宽度为基准降序排序 - List listPictureSizes = params.getSupportedPictureSizes(); - // 防止数据为null - if (listPictureSizes == null){ - // 获取默认拍照大小 - Camera.Size defaultSize = params.getPictureSize(); - return defaultSize; - } - - // 进行排序处理 -> 并以宽度 * 高度 为基准降序排序 - Collections.sort(listPictureSizes, new Comparator() { - @Override - public int compare(Camera.Size lhs, Camera.Size rhs) { - int leftPixels = lhs.height * lhs.width; - int rightPixels = rhs.height * rhs.width; - - if (leftPixels > rightPixels) { - return -1; - } else if (leftPixels == rightPixels) { - return 0; - } else { - return 1; - } - } - }); - - // ==== 打印信息 ==== - if (LogPrintUtils.isPrintLog()) { - StringBuilder builder = new StringBuilder(); - builder.append("拍照支持尺寸: \r\n"); - // 打印信息 - for (Camera.Size pictureSize : listPictureSizes) { - // 例: 1080x1920 - builder.append(pictureSize.width).append("x").append(pictureSize.height).append("\r\n"); - } - // 打印尺寸信息 - LogPrintUtils.dTag(TAG, builder.toString()); - } - - // == - - // 判断是否拿最大支持的尺寸 - if (max){ - if (!listPictureSizes.isEmpty()) { - // 获取最大的尺寸 - Camera.Size largestPicture = listPictureSizes.get(0); - return largestPicture; - } else { - // 获取默认拍照大小 - Camera.Size defaultSize = params.getPictureSize(); - return defaultSize; - } - } - - // 判断是否竖屏 point.x = > 宽, point.y => 高 - boolean isPortrait = point.y > point.x; - // 如果是竖屏, 则修改 - if (isPortrait){ - int tempY = point.y; - int tempX = point.x; - // 进行转换 - point.x = tempY; - point.y = tempX; - } - - // 计算比例值 宽 / 高 - double pictureAspectRatio = (double) point.x / (double) point.y; - - // 判断最大符合 - Camera.Size maxAccordSize = null; - - // 循环遍历判断 - Iterator iterator = listPictureSizes.iterator(); - while (iterator.hasNext()) { - // 获取拍照大小 - Camera.Size pictureSize = iterator.next(); - // 获取宽、高 - int realWidth = pictureSize.width; - int realHeight = pictureSize.height; - // 小于最小尺寸, 则不处理 - if (realWidth * realHeight < MIN_PREVIEW_PIXELS) { - iterator.remove(); - continue; - } - - // 判断预选的尺寸是否竖屏 - boolean isCandidatePortrait = realWidth < realHeight; - // 翻转宽、高 - int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; - int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; - - // 计算比例 - double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight; - double calcDistortion = Math.abs(aspectRatio - pictureAspectRatio); - - // 如果大于指定的尺寸比例差, 则跳过 - if (calcDistortion > distortion) { - iterator.remove(); - continue; - } - - // 如果相符, 则直接跳过 - if (maybeFlippedWidth == point.x && maybeFlippedHeight == point.y) { - return pictureSize; - } - - // 保存最大相符的尺寸 - if (maxAccordSize == null){ - maxAccordSize = pictureSize; - } - } - - // 如果存在最相符的则返回 - if (maxAccordSize != null){ - return maxAccordSize; - } - - // 如果没有精确匹配,请使用最大尺寸大小 - if (!listPictureSizes.isEmpty()) { - // 获取最大的尺寸 - Camera.Size largestPicture = listPictureSizes.get(0); - return largestPicture; - } - - // = 都不匹配, 则用默认分辨率 = - // 获取默认拍照大小 - Camera.Size defaultSize = params.getPictureSize(); - return defaultSize; - } - - // ================ - // = 视频录制尺寸 = - // ================ - - /** - * 根据对应的尺寸, 计算相应最符合的大小 - * @param max 是否使用最大的尺寸 - * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) - * @param distortion 偏差比例值 - * @param minAccord 是否判断存在最小使用最小 - * hint: point.x = > 宽, point.y => 高 - */ - private Camera.Size calcVideoSize(boolean max, Point point, double distortion, boolean minAccord) { - // 判断是否为null - if (point == null){ - point = ScreenUtils.getScreenWidthHeightToPoint(); - } - // 如果误差为负数, 则使用默认值 - if (distortion < 0){ - distortion = MAX_ASPECT_DISTORTION; - } - // 获取 Camera 参数 - Camera.Parameters params = mCamera.getParameters(); - // 获取手机支持的分辨率集合,并以宽度为基准降序排序 - List listVideoSizes = params.getSupportedVideoSizes(); - // 防止数据为null - if (listVideoSizes == null){ - // 获取默认拍照大小 - Camera.Size defaultSize = params.getPreferredPreviewSizeForVideo(); - return defaultSize; - } - - // 进行排序处理 -> 并以宽度 * 高度 为基准降序排序 - Collections.sort(listVideoSizes, new Comparator() { - @Override - public int compare(Camera.Size lhs, Camera.Size rhs) { - int leftPixels = lhs.height * lhs.width; - int rightPixels = rhs.height * rhs.width; - - if (leftPixels > rightPixels) { - return -1; - } else if (leftPixels == rightPixels) { - return 0; - } else { - return 1; - } - } - }); - - // ==== 打印信息 ==== - if (LogPrintUtils.isPrintLog()) { - StringBuilder builder = new StringBuilder(); - builder.append("视频录制支持尺寸: \r\n"); - // 打印信息 - for (Camera.Size videoSize : listVideoSizes) { - // 例: 1080x1920 - builder.append(videoSize.width).append("x").append(videoSize.height).append("\r\n"); - } - // 打印尺寸信息 - LogPrintUtils.dTag(TAG, builder.toString()); - } - - // == - - // 判断是否拿最大支持的尺寸 - if (max){ - if (!listVideoSizes.isEmpty()) { - // 获取最大的尺寸 - Camera.Size largestVideo = listVideoSizes.get(0); - return largestVideo; - } else { - // 获取默认视频大小 - Camera.Size defaultSize = params.getPreferredPreviewSizeForVideo(); - return defaultSize; - } - } - - // 判断是否竖屏 point.x = > 宽, point.y => 高 - boolean isPortrait = point.y > point.x; - // 如果是竖屏, 则修改 - if (isPortrait){ - int tempY = point.y; - int tempX = point.x; - // 进行转换 - point.x = tempY; - point.y = tempX; - } - - // 计算比例值 宽 / 高 - double videoAspectRatio = (double) point.x / (double) point.y; - - // 判断最大符合 - Camera.Size maxAccordSize = null; - // 判断最小符合 - Camera.Size minAccordSize = null; - - // 循环遍历判断 - Iterator iterator = listVideoSizes.iterator(); - while (iterator.hasNext()) { - // 获取视频大小 - Camera.Size videoSize = iterator.next(); - // 获取宽、高 - int realWidth = videoSize.width; - int realHeight = videoSize.height; - // 小于最小尺寸, 则不处理 - if (realWidth * realHeight < MIN_PREVIEW_PIXELS) { - iterator.remove(); - continue; - } - - // 判断预选的尺寸是否竖屏 - boolean isCandidatePortrait = realWidth < realHeight; - // 翻转宽、高 - int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; - int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; - - // 计算比例 - double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight; - double calcDistortion = Math.abs(aspectRatio - videoAspectRatio); - - // 如果大于指定的尺寸比例差, 则跳过 - if (calcDistortion > distortion) { - iterator.remove(); - continue; - } - - // 如果相符, 则直接跳过 - if (maybeFlippedWidth == point.x && maybeFlippedHeight == point.y) { - return videoSize; - } - - // 保存最大相符的尺寸 - if (maxAccordSize == null){ - maxAccordSize = videoSize; - } - // 保存最小符合的 - minAccordSize = videoSize; - } - - if (minAccord && minAccordSize != null){ - return minAccordSize; - } - - // 如果存在最相符的则返回 - if (maxAccordSize != null){ - return maxAccordSize; - } - - // 如果没有精确匹配,请使用最大尺寸大小 - if (!listVideoSizes.isEmpty()) { - // 获取最大的尺寸 - Camera.Size largestVideo = listVideoSizes.get(0); - return largestVideo; - } - - // = 都不匹配, 则用默认分辨率 = - // 获取默认视频大小 - Camera.Size defaultSize = params.getPreferredPreviewSizeForVideo(); - return defaultSize; - } - -// /** -// * 根据手机支持的视频分辨率,设置录制尺寸 -// * @param point point.x = > 宽, point.y => 高 (从指定的宽高, 开始往下(超过的不处理)选择最符合的尺寸) -// * @param designated 指定的尺寸 - 可多个如 竖屏 => new Point(480, 640), new Point(360, 640), 横屏 => new Point(640, 480), new Point(640, 360) -// */ -// public Camera.Size getVideoSize(Point point, Point... designated) { -// if (mCamera == null){ -// // 打印支持的尺寸 -// LogPrintUtils.dTag(TAG, "camera is null"); -// return null; -// } -// // 判断是否竖屏 -// boolean isPortrait = false; -// // 视频尺寸大小 -// Camera.Size mVideoSize = null; -// try { -// // 获取 Camera 参数 -// Camera.Parameters params = mCamera.getParameters(); -// // 小数点处理, 只要后两位 -// DecimalFormat decimalFormat = new DecimalFormat("0.00"); -// decimalFormat.setRoundingMode(RoundingMode.FLOOR); -// // 获取屏幕宽、高 -// int sWidth = (point != null) ? point.x : ScreenUtils.getScreenWidth(); -// int sHeight = (point != null) ? point.y : ScreenUtils.getScreenHeight(); -// // 如果高度大于宽度, 则表示属于竖屏 -// isPortrait = sHeight > sWidth; -// // 判断是否存在指定尺寸 -// boolean isDesignated = (designated != null && designated.length != 0); -// // 打印准备计算的信息 -// LogPrintUtils.dTag(TAG, "getVideoSize - sWidth: " + sWidth + ", sHeight: " + sHeight + ", isPortrait: " + isPortrait + ", isDesignated(是否存在指定尺寸): " + isDesignated); -// // 打印信息 -// if (isDesignated){ -// for (int i = 0, len = designated.length; i < len; i++){ -// Point appoint = designated[i]; -// if (appoint != null){ -// LogPrintUtils.dTag(TAG, "appoint.x: " + appoint.x + ", appoint.y: " + appoint.y); -// } -// } -// } else { -// LogPrintUtils.dTag(TAG, "designated is null or leng == 0"); -// } -// // 获取支持录制的视频尺寸 -// List listVideoSizes = params.getSupportedVideoSizes(); -// // 获取手机支持的分辨率集合, 并以宽度为基准降序排序 -// Collections.sort(listVideoSizes, new Comparator() { -// @Override -// public int compare(Camera.Size lhs, Camera.Size rhs) { -// if (lhs.width > rhs.width) { -// return -1; -// } else if (lhs.width == rhs.width) { -// return 0; -// } else { -// return 1; -// } -// } -// }); -// // 是否跳出循环 -// boolean isBreak = false; -// // 默认是否支持固定的大小 -// Camera.Size fixedSize = null; -// // 计算比例(竖屏 以高度为基准, 高:宽), (横屏 以宽度为基准, 宽:高) -// float ratio = (isPortrait ? ((float) sHeight / (float) sWidth) : ((float) sWidth / (float) sHeight)) - 1; -// // 转换保留两位小数点 -// ratio = Float.parseFloat(decimalFormat.format(ratio)); -// // 遍历预览大小 -// for (Camera.Size size : listVideoSizes) { -// if (isBreak){ -// break; -// } -// // 打印支持的尺寸 -// LogPrintUtils.dTag(TAG, "VideoSizes - 宽度: " + size.width + ", 高度: " + size.height); -// // 判断横竖屏 -// if (isPortrait) { // 属于竖屏 => 高度 > 宽度 -// // 因为是竖屏, 所以判断需要倒着过来 -// if (sWidth == size.height && sHeight == size.width){ -// // 保存符合比例的大小 -// mVideoSize = size; -// } -// // 处理宽大于高的, 因为是使用竖屏, 参数判断都反着处理 -// if (size.width > size.height){ -// // 获取比例 -// float ratioCalc = ((float) size.width / (float) size.height) - 1; -// // 转换保留两位小数点 -// ratioCalc = Float.parseFloat(decimalFormat.format(ratioCalc)); -// // 判断符合规则的 -// if (ratioCalc == ratio){ -// // 保存尺寸 -// fixedSize = size; -// // 如果存在才处理 -// if (isDesignated){ -// for (int i = 0, len = designated.length; i < len; i++){ -// Point appoint = designated[i]; -// if (appoint != null){ -// // 判断是否支持固定的大小 -// if (size.width == appoint.y && size.height == appoint.x) { -// isBreak = true; -// break; -// } -// } -// } -// } -// } else { -// // 最小支持到640 -// if (size.width < 640){ -// break; -// } -// // 保存尺寸 -// fixedSize = size; -// // 如果存在才处理 -// if (isDesignated){ -// for (int i = 0, len = designated.length; i < len; i++){ -// Point appoint = designated[i]; -// if (appoint != null){ -// // 判断是否支持固定的大小 -// if (size.width == appoint.y && size.height == appoint.x) { -// isBreak = true; -// break; -// } -// } -// } -// } -// } -// } -// } else { // 属于横屏 => 宽度 > 高度 -// // 因为是横屏, 所以判断需要正常 -// if (sWidth == size.width && sHeight == size.height){ -// // 保存符合比例的大小 -// mVideoSize = size; -// } -// // 处理高大于宽的, 因为是使用横屏, 参数判断需要正常 -// if (size.height > size.width){ -// // 获取比例 -// float ratioCalc = ((float) size.height / (float) size.width) - 1; -// // 转换保留两位小数点 -// ratioCalc = Float.parseFloat(decimalFormat.format(ratioCalc)); -// // 判断符合规则的 -// if (ratioCalc == ratio){ -// // 保存尺寸 -// fixedSize = size; -// // 如果存在才处理 -// if (isDesignated){ -// for (int i = 0, len = designated.length; i < len; i++){ -// Point appoint = designated[i]; -// if (appoint != null){ -// // 判断是否支持固定的大小 -// if (size.width == appoint.y && size.height == appoint.x) { -//// isBreak = true; -// break; -// } -// } -// } -// } -// } else { -// // 最小支持到 640 -// if (size.height < 640){ -// break; -// } -// // 保存尺寸 -// fixedSize = size; -// // 如果存在才处理 -// if (isDesignated){ -// for (int i = 0, len = designated.length; i < len; i++){ -// Point appoint = designated[i]; -// if (appoint != null){ -// // 判断是否支持固定的大小 -// if (size.width == appoint.y && size.height == appoint.x) { -//// isBreak = true; -// break; -// } -// } -// } -// } -// } -// } -// } -// } -// // 如果支持固定的大小, 则进行处理 -// if (fixedSize != null) { -// // 保存固定支持的尺寸 -// mVideoSize = fixedSize; -// } -// // 获取最合适的比例 -// LogPrintUtils.dTag(TAG, "getVideoSize -> 宽度: " + mVideoSize.width + ", 高度: " + mVideoSize.height + ", 是否竖屏: " + isPortrait); -// } catch (Exception e) { -// LogPrintUtils.eTag(TAG, e, "getVideoSize - 是否竖屏: " + isPortrait); -// } -// return mVideoSize; -// } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/manager/ActivityManager.java b/DevLibUtils/src/main/java/dev/utils/app/assist/manager/ActivityManager.java deleted file mode 100644 index 3100bece34..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/manager/ActivityManager.java +++ /dev/null @@ -1,436 +0,0 @@ -package dev.utils.app.assist.manager; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; - -import java.util.Iterator; -import java.util.Stack; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 应用程序Activity管理类:用于Activity管理和应用程序 - * Created by Ttt - */ -public final class ActivityManager { - - /** 禁止构造对象,保证只有一个实例 */ - private ActivityManager() { - } - - // 日志TAG - private static final String TAG = ActivityManager.class.getSimpleName(); - - /** ActivityManager 实例 */ - private static ActivityManager INSTANCE = new ActivityManager(); - - /** 获取 ActivityManager 实例 ,单例模式 */ - public static ActivityManager getInstance() { - return INSTANCE; - } - - /** - * 通过上下文 获取Activity - * @param context - * @return - */ - public static Activity getActivity(Context context) { - try { - Activity activity = (Activity) context; - return activity; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getActivity"); - } - return null; - } - - // == 页面判断处理 == - - /** - * 判断页面是否关闭 - * @param activity - * @return - */ - public static boolean isFinishing(Activity activity){ - if (activity != null){ - return activity.isFinishing(); - } - return false; - } - - /** - * 判断页面是否关闭 - * @param context - * @return - */ - public static boolean isFinishingCtx(Context context){ - if (context != null){ - try { - return ((Activity) context).isFinishing(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "isFinishingCtx"); - } - } - return false; - } - - // ============== - - /** Activity 栈(后进先出) */ - private final Stack activityStacks = new Stack<>(); - - /** - * 获取 Activity 栈 - * @return - */ - public Stack getActivityStacks() { - return activityStacks; - } - - /** - * 保存 Activity - * @param activity - */ - public void addActivity(Activity activity) { - if (activity == null) { - return; - } - synchronized (activityStacks) { - if (activityStacks.contains(activity)) { - return; - } - activityStacks.add(activity); - } - } - - /** - * 移除 Activity - * @param activity - */ - public void removeActivity(Activity activity) { - if (activity == null) { - return; - } - synchronized (activityStacks) { - int index = activityStacks.indexOf(activity); - if (index == -1) { - return; - } - activityStacks.remove(index); - } - } - - /** - * 移除 Activity - * @param activitys - */ - public void removeActivity(Activity... activitys) { - if (activitys != null && activitys.length != 0){ - for (int i = 0, len = activitys.length; i < len; i++){ - removeActivity(activitys[i]); - } - } - } - - /** - * 获取当前Activity - * @return - */ - public Activity currentActivity() { - return activityStacks.lastElement(); - } - - /** - * 结束当前Activity - */ - public void finishActivity() { - finishActivity(activityStacks.lastElement()); - } - - /** - * 结束指定的 Activity - * @param activity - */ - public void finishActivity(Activity activity) { - // 先移除Activity - removeActivity(activity); - // Activity 不为null,并且属于未销毁状态 - if (activity != null && !activity.isFinishing()) { - // finish到Activity - activity.finish(); - } - } - - /** - * 结束指定的 Activity - * @param activitys - */ - public void finishActivity(Activity... activitys) { - if (activitys != null && activitys.length != 0){ - for (int i = 0, len = activitys.length; i < len; i++){ - finishActivity(activitys[i]); - } - } - } - - /** - * 结束指定类名的 Activity - * @param cls Activity.class - */ - public void finishActivity(Class cls) { - synchronized (activityStacks) { - // 保存新的任务,防止出现同步问题 - Stack aStacks = new Stack<>(); - aStacks.addAll(activityStacks); - // 清空全部,便于后续操作处理 - activityStacks.clear(); - // 进行遍历移除 - Iterator iterator = aStacks.iterator(); - while (iterator.hasNext()) { - Activity activity = iterator.next(); - // 判断是否想要关闭的Activity - if (activity != null) { - if (activity.getClass() == cls){ - // 如果页面没有finish 则进行finish - if (!activity.isFinishing()) { - activity.finish(); - } - // 删除对应的Item - iterator.remove(); - } - } else { - // 删除对应的Item - iterator.remove(); - } - } - // 把不符合条件的保存回去 - activityStacks.addAll(aStacks); - // 移除,并且清空内存 - aStacks.clear(); - aStacks = null; - } - } - - /** - * 结束指定类名的 Activity - * @param clss Activity.class, x.class - */ - public void finishActivity(Class... clss) { -// if (clss != null && clss.length != 0) { -// for (int i = 0, len = clss.length; i < len; i++){ -// finishActivity(clss[i]); -// } -// } - if (clss != null && clss.length != 0){ - synchronized (activityStacks) { - // 保存新的任务,防止出现同步问题 - Stack aStacks = new Stack<>(); - aStacks.addAll(activityStacks); - // 清空全部,便于后续操作处理 - activityStacks.clear(); - // 判断是否销毁 - boolean isRemove; - // 进行遍历移除 - Iterator iterator = aStacks.iterator(); - while (iterator.hasNext()) { - Activity activity = iterator.next(); - // 判断是否想要关闭的Activity - if (activity != null) { - // 默认不需要销毁 - isRemove = false; - // 循环判断 - for (int i = 0, len = clss.length; i < len; i++){ - // 判断是否相同 - if (activity.getClass() == clss[i]){ - isRemove = true; - break; - } - } - // 判断是否销毁 - if (isRemove){ - // 如果页面没有finish 则进行finish - if (!activity.isFinishing()) { - activity.finish(); - } - // 删除对应的Item - iterator.remove(); - } - } else { - // 删除对应的Item - iterator.remove(); - } - } - // 把不符合条件的保存回去 - activityStacks.addAll(aStacks); - // 移除,并且清空内存 - aStacks.clear(); - aStacks = null; - } - } - } - - /** - * 结束全部Activity 除忽略的页面外 - * @param cls - */ - public void finishAllActivityToIgnore(Class cls){ - synchronized (activityStacks) { - // 保存新的任务,防止出现同步问题 - Stack aStacks = new Stack<>(); - aStacks.addAll(activityStacks); - // 清空全部,便于后续操作处理 - activityStacks.clear(); - // 进行遍历移除 - Iterator iterator = aStacks.iterator(); - while (iterator.hasNext()) { - Activity activity = iterator.next(); - // 判断是否想要关闭的Activity - if (activity != null) { - if (!(activity.getClass() == cls)){ - // 如果页面没有finish 则进行finish - if (!activity.isFinishing()) { - activity.finish(); - } - // 删除对应的Item - iterator.remove(); - } - } else { - // 删除对应的Item - iterator.remove(); - } - } - // 把不符合条件的保存回去 - activityStacks.addAll(aStacks); - // 移除,并且清空内存 - aStacks.clear(); - aStacks = null; - } - } - - /** - * 结束全部Activity 除忽略的页面外 - * @param clss - */ - public void finishAllActivityToIgnore(Class... clss){ - if (clss != null && clss.length != 0){ - synchronized (activityStacks) { - // 保存新的任务,防止出现同步问题 - Stack aStacks = new Stack<>(); - aStacks.addAll(activityStacks); - // 清空全部,便于后续操作处理 - activityStacks.clear(); - // 判断是否销毁 - boolean isRemove; - // 进行遍历移除 - Iterator iterator = aStacks.iterator(); - while (iterator.hasNext()) { - Activity activity = iterator.next(); - // 判断是否想要关闭的Activity - if (activity != null) { - // 默认需要销毁 - isRemove = true; - // 循环判断 - for (int i = 0, len = clss.length; i < len; i++){ - // 判断是否相同 - if (activity.getClass() == clss[i]){ - isRemove = false; - break; - } - } - // 判断是否销毁 - if (isRemove){ - // 如果页面没有finish 则进行finish - if (!activity.isFinishing()) { - activity.finish(); - } - // 删除对应的Item - iterator.remove(); - } - } else { - // 删除对应的Item - iterator.remove(); - } - } - // 把不符合条件的保存回去 - activityStacks.addAll(aStacks); - // 移除,并且清空内存 - aStacks.clear(); - aStacks = null; - } - } - } - - /** - * 结束所有Activity - */ - public void finishAllActivity() { - synchronized (activityStacks) { - // 保存新的任务,防止出现同步问题 - Stack aStacks = new Stack<>(); - aStacks.addAll(activityStacks); - // 清空全部,便于后续操作处理 - activityStacks.clear(); - // 进行遍历移除 - Iterator iterator = aStacks.iterator(); - while (iterator.hasNext()) { - Activity activity = iterator.next(); - if (activity != null && !activity.isFinishing()) { - activity.finish(); - // 删除对应的Item - iterator.remove(); - } - } - // 移除,并且清空内存 - aStacks.clear(); - aStacks = null; - } - } - - /** - * 退出应用程序 - * @param context - */ - public void appExit(Context context) { - try { - finishAllActivity(); - // -- - android.app.ActivityManager activityMgr = (android.app.ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - activityMgr.restartPackage(context.getPackageName()); - System.exit(0); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "appExit"); - } - } - - /** - * 退出应用程序 - */ - public void appExit() { - try { - finishAllActivity(); - //退出JVM(java虚拟机),释放所占内存资源,0表示正常退出(非0的都为异常退出) - System.exit(0); - //从操作系统中结束掉当前程序的进程 - android.os.Process.killProcess(android.os.Process.myPid()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "appExit"); - // - - System.exit(-1); - } - } - - /** - * 重启app - */ - public static void restartApplication() { - try { - Intent intent = DevUtils.getContext().getPackageManager().getLaunchIntentForPackage(DevUtils.getContext().getPackageName()); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - DevUtils.getContext().startActivity(intent); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "restartApplication"); - } - } -} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/manager/ThreadManager.java b/DevLibUtils/src/main/java/dev/utils/app/assist/manager/ThreadManager.java deleted file mode 100644 index 0347d24ef1..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/manager/ThreadManager.java +++ /dev/null @@ -1,132 +0,0 @@ -package dev.utils.app.assist.manager; - -import java.lang.reflect.Method; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import dev.utils.LogPrintUtils; - -/** - * detail: 线程管理类 - 统一使用DevThreadManager, 抛弃该类 - * Created by Ttt - */ -public final class ThreadManager { - - // 日志TAG - private final String TAG = ThreadManager.class.getSimpleName(); - - // 线程池对象 - private final ExecutorService threadPool = Executors.newFixedThreadPool(getThreads()); - - // ThreadManager 实例 - private static ThreadManager INSTANCE = new ThreadManager(); - - /** 禁止构造对象,保证只有一个实例 */ - private ThreadManager() { - } - - /** 获取 ThreadManager 实例 ,单例模式 */ - public static ThreadManager getInstance() { - return INSTANCE; - } - - // == - - /** - * 获取线程数 - * @return - */ - private final int getThreads() { - // 使用计算过后的 - return getCaclThreads(); - // 使用固定自定义的线程数量 - //return 10; - } - - /** - * 获取线程数 - * @return - */ - private final int getCaclThreads() { - // return Runtime.getRuntime().availableProcessors() * 2 + 1 - // -- - // 获取CPU核心数 - int cNumber = Runtime.getRuntime().availableProcessors(); - // 如果小于等于5,则返回5 - if (cNumber <= 5) { - return 5; - } else { // 大于5的情况 - if (cNumber * 2 + 1 >= 10) { // 防止线程数量过大,当大于10 的时候,返回 10 - return 10; - } else { // 不大于10的时候,默认返回 支持的数量 * 2 + 1 - return cNumber * 2 + 1; - } - } - } - - // == - - /** - * 加入到线程池任务队列 - * @param runnable - */ - public void addTask(Runnable runnable) { - threadPool.execute(runnable); - } - - - /** - * 通过反射,调用某个类的方法 - * @param method - * @param _class - */ - public void addTask(final Method method, final Object _class) { - threadPool.execute(new Runnable() { - @Override - public void run() { - try { - method.invoke(_class); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "addTask"); - } - } - }); - } - - // == - - /** - * shutdown 会等待所有提交的任务执行完成,不管是正在执行还是保存在任务队列中的已提交任务 - */ - public void shutdown() { - threadPool.shutdown(); - } - - /** - * shutdownNow会尝试中断正在执行的任务(其主要是中断一些指定方法如sleep方法),并且停止执行等待队列中提交的任务。 - * @return - */ - public List shutdownNow() { - return threadPool.shutdownNow(); - } - - /** - * isShutDown当调用shutdown()方法后返回为true。 - * @return - */ - public boolean isShutdown() { - return threadPool.isShutdown(); - } - - /** - * 若关闭后所有任务都已完成,则返回true. - * 注意除非首先调用shutdown或shutdownNow, 否则isTerminated 永不为true. - * // -- - * isTerminated当调用shutdown()方法后,并且所有提交的任务完成后返回为true - * @return - */ - public boolean isTerminated() { - return threadPool.isTerminated(); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/manager/TimerManager.java b/DevLibUtils/src/main/java/dev/utils/app/assist/manager/TimerManager.java deleted file mode 100644 index dd342e52ea..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/manager/TimerManager.java +++ /dev/null @@ -1,490 +0,0 @@ -package dev.utils.app.assist.manager; - -import android.os.Handler; -import android.os.Message; -import android.text.TextUtils; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Timer; - -import dev.utils.LogPrintUtils; - -/** - * detail: 定时器工具类 - * Created by Ttt - * --- - * hint: - * 主要是为了控制整个项目的定时器,防止定时器混乱,或者导致忘记关闭等情况,以及减少初始化等操作代码 - * 主要实现是 AbsTimer、TimerTask 这两个类, - * AbsTimer -> 定时器抽象类,对外提供该类对象,以及内部方法,便于内部实现方法的隐藏,以及达到对定时器任务的控制处理 - * TimerTask -> 内部私有类,实现了具体的定时器操作,以及代码控制等,防止外部直接new,导致定时器混乱 - * --- - * 如果外部想要实现定时器,但是通过内部 ArrayList 控制,也可以通过 实现AbsTimer接口,内部的startTimer()、closeTimer() 进行了对AbsTimer的保存,标记等操作 - * 需要注意的是,实现start(close)Timer() 方法,必须保留 super.start(close)Timer(); -> 内部 ArrayList 进行了操作,而不对外开放(不需要主动调用) - * --- - * startTimer() -> 主要进行添加到 ArrayList, 并且标记不需要回收 - * closeTimer() -> 不直接操作remove,防止出现ConcurrentModificationException 异常, 而是做一个标记,便于后续回收 - */ -public final class TimerManager { - - private TimerManager() { - } - - // 日志TAG - private static final String TAG = TimerManager.class.getSimpleName(); - - /** 内部保存定时器对象,防止忘记关闭等其他情况,以及便于控制处理 */ - private static final ArrayList listAbsTimers = new ArrayList(); - - // ============= ArrayList 对外公开的方法 ============= - /** 回收资源 */ - public static void gc() { - synchronized (listAbsTimers) { - // 临时数据源 - ArrayList lists = new ArrayList(listAbsTimers); - // 清空旧的数据 - listAbsTimers.clear(); - // 开始删除无用资源 - Iterator iterator = lists.iterator(); - while (iterator.hasNext()) { - if (iterator.next().isMarkSweep) { // 需要回收,则进行回收 - iterator.remove(); - } - } - // 把不需要回收的保存回去 - listAbsTimers.addAll(lists); - // 移除旧的 - lists.clear(); - lists = null; - } - } - - /** 获取全部任务总数 */ - public static int timerSize() { - return listAbsTimers.size(); - } - - /** - * 获取属于对应字符串标记的定时器任务(优先获取符合的) - * @param markStr - * @return - */ - public static AbsTimer getTimer(String markStr) { - try { - for (int i = 0, size = listAbsTimers.size(); i < size; i++) { - AbsTimer absTimer = listAbsTimers.get(i); - // 判断是否符合标记 , 原本标记不为null,并且符合条件的 - if (!TextUtils.isEmpty(absTimer.getMarkStr()) && absTimer.getMarkStr().equals(markStr)) { - return absTimer; - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getTimer"); - } - return null; - } - - /** - * 获取属于标记id的定时器任务(优先获取符合的) - * @param markId - * @return - */ - public static AbsTimer getTimer(int markId) { - try { - for (int i = 0, size = listAbsTimers.size(); i < size; i++) { - AbsTimer absTimer = listAbsTimers.get(i); - // 判断是否符合标记 , 原本标记不为null,并且符合条件的 - if (absTimer.getMarkId() == markId) { - return absTimer; - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getTimer"); - } - return null; - } - - /** 关闭全部任务 */ - public static void closeAll() { - try { - for (int i = 0, size = listAbsTimers.size(); i < size; i++) { - listAbsTimers.get(i).closeTimer(); // 关闭定时器 - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "closeAll"); - } - } - - /** 关闭所有无限循环的任务 */ - public static void closeInfiniteTask() { - try { - for (int i = 0, size = listAbsTimers.size(); i < size; i++) { - AbsTimer absTimer = listAbsTimers.get(i); - // 判断是否无限运行 - if (absTimer.isInfinite()) { - absTimer.closeTimer(); // 关闭定时器 - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "closeInfiniteTask"); - } - } - - /** - * 关闭所有符合对应的字符串标记的定时器任务 - * @param markStr - */ - public static void closeMark(String markStr) { - if (markStr == null) { - return; - } - try { - for (int i = 0, size = listAbsTimers.size(); i < size; i++) { - AbsTimer absTimer = listAbsTimers.get(i); - // 判断是否符合标记 , 原本标记不为null,并且符合条件的 - if (!TextUtils.isEmpty(absTimer.getMarkStr()) && absTimer.getMarkStr().equals(markStr)) { - absTimer.closeTimer(); // 关闭定时器 - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "closeMark"); - } - } - - /** - * 关闭所有符合对应的标记id的定时器任务 - * @param markId - */ - public static void closeMark(int markId) { - try { - for (int i = 0, size = listAbsTimers.size(); i < size; i++) { - AbsTimer absTimer = listAbsTimers.get(i); - if (absTimer.getMarkId() == markId) { - absTimer.closeTimer(); // 关闭定时器 - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "closeMark"); - } - } - - // =============== 对外公开初始化AbsTimer方法(内部控制对 TimerTask的生成) ============= - - /** 创建定时器 => 立即执行,无限循环,通知默认what */ - public static AbsTimer creTimer(Handler handler, long period) { - return creTimer(handler, AbsTimer.TIMER_NOTIFY_WHAT, 0l, period, -1); - } - - /** 创建定时器 => 无限循环,通知默认what */ - public static AbsTimer creTimer(Handler handler, long delay, long period) { - return creTimer(handler, AbsTimer.TIMER_NOTIFY_WHAT, delay, period, -1); - } - - /** 创建定时器 => 立即执行,通知默认what */ - public static AbsTimer creTimer(Handler handler, long period, int triggerLimit) { - return creTimer(handler, AbsTimer.TIMER_NOTIFY_WHAT, 0l, period, triggerLimit); - } - - /** 创建定时器 => 立即执行,无限循环 */ - public static AbsTimer creTimer(Handler handler, int what, long period) { - return creTimer(handler, what, 0l, period, -1); - } - - /** 创建定时器 => 无限循环 */ - public static AbsTimer creTimer(Handler handler, int what, long delay, long period) { - return creTimer(handler, what, delay, period, -1); - } - - /** 创建定时器 => 立即执行 */ - public static AbsTimer creTimer(Handler handler, int what, long period, int triggerLimit) { - return creTimer(handler, what, 0l, period, triggerLimit); - } - - /** - * 创建定时器 - * @param handler 通知的Handler - * @param what 通知的what - * @param delay 延迟时间 - 多少毫秒后开始执行 - * @param period 循环时间 - 每隔多少秒执行一次 - * @param triggerLimit 触发次数上限(-1,表示无限循环) - * @return - */ - public static AbsTimer creTimer(Handler handler, int what, long delay, long period, int triggerLimit) { - return new TimerTask(handler, what, delay, period, triggerLimit); - } - - // =================== 定时器抽象类,开放对定时器操作方法 ================= - - /** - * 定时器抽象类,主要对内部Timer参数进行控制,以及防止外部直接new TimerTask,照成不必要的失误 - */ - public static abstract class AbsTimer { - /** 默认通知的what */ - public static final int TIMER_NOTIFY_WHAT = 50000; - /** 状态标识 - 是否标记清除 */ - private boolean isMarkSweep = true; - // -- - /** int 标记 */ - private int markId = -1; - /** String 标记 */ - private String markStr = null; - - public final int getMarkId() { - return markId; - } - - public final String getMarkStr() { - return markStr; - } - - // -- - - public final AbsTimer setMarkId(int markId) { - this.markId = markId; - return this; - } - - public final AbsTimer setMarkStr(String markStr) { - this.markStr = markStr; - return this; - } - - // ======= 定时器任务,功能实现方法 ========= - - /** 运行定时器 */ - public void startTimer() { // 如果外部通过了creTimer或者直接new AbsTimer 初始化了对象,没有调用startTimer,都不会保存到 listAbsTimers 并不影响对定时器的控制 - // 标记状态 - 不需要回收 - this.isMarkSweep = false; - synchronized (listAbsTimers) { - // 不存在才进行添加 - if (!listAbsTimers.contains(this)) { - listAbsTimers.add(this); - } - } - } - - /** 关闭定时器 */ - public void closeTimer() { - // 标记状态 - 需要回收 - this.isMarkSweep = true; - } - - /** 判断是否运行中 */ - public abstract boolean isRunTimer(); - - /** 获取已经触发的次数 */ - public abstract int getTriggerNumber(); - - /** 获取允许触发的上限次数 */ - public abstract int getTriggerLimit(); - - /** 是否触发结束(到达最大次数) */ - public abstract boolean isTriggerEnd(); - - /** 是否无限循环 */ - public abstract boolean isInfinite(); - - /** - * 设置通知的Handler - * @param handler - */ - public abstract AbsTimer setHandler(Handler handler); - - /** - * 设置通知的What - * @param notifyWhat - */ - public abstract AbsTimer setNotifyWhat(int notifyWhat); - - /** - * 设置通知的Obj - * @param notifyObj - */ - public abstract AbsTimer setNotifyObject(Object notifyObj); - - /** - * 设置时间 - * @param delay 延迟时间 - 多少毫秒后开始执行 - * @param period 循环时间 - 每隔多少秒执行一次 - */ - public abstract AbsTimer setTime(long delay, long period); - - /** - * 设置触发次数上限 - * @param triggerLimit - */ - public abstract AbsTimer setTriggerLimit(int triggerLimit); - } - - // =========== 定时器 具体实现类(不对外开放) =============== - - /** - * 定时器内部封装类 - 定时器任务类 - * 便于快捷使用,并且防止外部new,从而达到对整个项目定时器的控制 - */ - private static final class TimerTask extends AbsTimer { - /** 定时器 */ - private Timer timer; - /** 定时器任务栈 */ - private java.util.TimerTask timerTask; - /** 通知Handler */ - private Handler handler; - /** 通知的数据 */ - private Object notifyObj = null; - /** 通知类型 */ - private int notifyWhat = AbsTimer.TIMER_NOTIFY_WHAT; - /** 延迟时间 - 多少毫秒后开始执行 */ - private long delay; - /** 循环时间 - 每隔多少秒执行一次 */ - private long period; - /** 触发次数上限 */ - private int triggerLimit = 1; - /** 触发次数 */ - private int triggerNumber = 0; - /** 定时器是否运行中 */ - private boolean isRunTimer = false; - - public TimerTask (Handler handler, int what, long delay, long period, int triggerLimit) { - this.handler = handler; - this.notifyWhat = what; - this.delay = delay; - this.period = period; - this.triggerLimit = triggerLimit; - } - - /** 开始定时器任务 */ - private void start() { - // 先关闭旧的定时器 - close(); - // 表示运行定时器中 - isRunTimer = true; - // 每次重置触发次数 - triggerNumber = 0; - // 开启定时器 - timer = new Timer(); // 每次重新new 防止被取消 - // 重新生成定时器 防止出现TimerTask is scheduled already 所以同一个定时器任务只能被放置一次 - timerTask = new java.util.TimerTask() { - @Override - public void run() { - // 表示运行定时器中 - isRunTimer = true; - // 累积触发次数 - triggerNumber++; - // 进行通知 - if(handler != null) { - // 从Message池中返回一个新的Message实例 - 通知what, arg1 = 触发次数, arg2 = 触发上限, obj = notifyObj - Message msg = handler.obtainMessage(notifyWhat, triggerNumber, triggerLimit, notifyObj); - handler.sendMessage(msg); - } - // 如果大于触发次数,则关闭 - if(triggerLimit >= 0 && triggerNumber >= triggerLimit) { - // 关闭任务,进行标记需要回收 - closeTimer(); - } - } - }; - try { - // xx毫秒后执行,每隔xx毫秒再执行一次 - timer.schedule(timerTask, delay, period); - } catch (Exception e) { - // 表示非运行定时器中 - isRunTimer = false; - // 关闭任务,进行标记需要回收 - closeTimer(); // 启动失败,则进行标记,标记需要回收 - } - } - - /** 关闭定时器任务 */ - private void close() { - // 表示非运行定时器中 - isRunTimer = false; - // 取消定时器任务 - try { - if (timer != null) { - timer.cancel(); - timer = null; - } - // -- - if (timerTask != null) { - timerTask.cancel(); - timerTask = null; - } - } catch (Exception e) { - } - } - - // ================ 实现抽象类方法 ================ - - @Override - public void startTimer() { - super.startTimer(); // 必须保留这句话 - // 开始定时器任务 - start(); - } - - @Override - public void closeTimer() { - super.closeTimer(); // 必须保留这句话 - // 关闭定时器任务 - close(); - } - - @Override - public boolean isRunTimer() { - return isRunTimer; - } - - @Override - public int getTriggerNumber() { - return triggerNumber; - } - - @Override - public int getTriggerLimit() { - return triggerLimit; - } - - @Override - public boolean isTriggerEnd() { // 如果为无限触发,则会返回true ,因为触发次数大于 -1 - return (triggerNumber >= triggerLimit); - //return (triggerLimit >= 0 && triggerNumber >= triggerLimit); - } - - @Override - public boolean isInfinite() { - return (triggerLimit <= -1); - } - - @Override - public AbsTimer setHandler(Handler handler) { - this.handler = handler; - return this; - } - - @Override - public AbsTimer setNotifyWhat(int notifyWhat) { - this.notifyWhat = notifyWhat; - return this; - } - - @Override - public AbsTimer setNotifyObject(Object notifyObj) { - this.notifyObj = notifyObj; - return this; - } - - @Override - public AbsTimer setTime(long delay, long period) { - this.delay = delay; - this.period = period; - return this; - } - - @Override - public AbsTimer setTriggerLimit(int triggerLimit) { - this.triggerLimit = triggerLimit; - return this; - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCache.java b/DevLibUtils/src/main/java/dev/utils/app/cache/DevCache.java deleted file mode 100644 index 4d958e1af6..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCache.java +++ /dev/null @@ -1,692 +0,0 @@ -package dev.utils.app.cache; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; - -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 缓存工具类 - * Created by 杨福海(michael) www.yangfuhai.com - * https://github.com/yangfuhai/ASimpleCache - * Update to Ttt - */ -public final class DevCache { - - private DevCache() { - } - -// // ==== 缓存状态标记 ==== -// // 存在数据 -// public static final int EXIST = 1; -// // 不存在数据 -// public static final int NOT_EXIST = 2; -// // 存在数据 - 但是已过期 -// public static final int OVERDUE = 3; -// // 保存成功 -// public static final int SAVE_SUC = 4; -// // 保存失败 -// public static final int SAVE_FAIL = 5; - // ========= - - // 日志Tag - private static final String TAG = DevCache.class.getSimpleName(); - // 缓存文件名 - private static final String DF_FILE_NAME = DevCache.class.getSimpleName(); - // 过期小时(单位秒) = 1小时 - public static final int TIME_HOUR = 60 * 60; - // 一天24小时 - public static final int TIME_DAY = TIME_HOUR * 24; - // 缓存最大值(50mb) - private static final int MAX_SIZE = 1000 * 1000 * 50; - // 不限制存放数据的数量 - private static final int MAX_COUNT = Integer.MAX_VALUE; - // 不同地址配置缓存对象 - private static Map mInstanceMap = new HashMap(); - // 缓存管理类 - private DevCacheManager mCache; - // 缓存地址 - private static File ctxCacheDir = null; - - /** - * 内部处理防止 Context 为null奔溃问题 - * @return - */ - private static Context getContext(Context context){ - if (context != null){ - return context; - } else { - // 设置全局Context - return DevUtils.getContext(); - } - } - - /** - * 获取缓存地址 - * @param ctx - * @return - */ - public static File getCacheDir(Context ctx){ - if (ctxCacheDir == null){ - ctxCacheDir = getContext(ctx).getCacheDir(); - } - return ctxCacheDir; - } - - /** - * 默认缓存地址 - * @param ctx - * @return - */ - public static DevCache get(Context ctx) { - return get(ctx, DF_FILE_NAME); - } - - /** - * 获取缓存地址 - * @param ctx - * @param cacheName - * @return - */ - public static DevCache get(Context ctx, String cacheName) { - // 进行处理 - File file = new File(getCacheDir(ctx), cacheName); - // 获取默认地址 - return get(file, MAX_SIZE, MAX_COUNT); - } - - /** - * 设置自定义缓存地址 - * @param cacheDir - * @return - */ - public static DevCache get(File cacheDir) { - return get(cacheDir, MAX_SIZE, MAX_COUNT); - } - - /** - * 自定义缓存大小 - * @param ctx - * @param max_zise - * @param max_count - * @return - */ - public static DevCache get(Context ctx, long max_zise, int max_count) { - File file = new File(getCacheDir(ctx), DF_FILE_NAME); - // - - return get(file, max_zise, max_count); - } - - /** - * 自定义缓存地址、大小等 - * @param cacheDir - * @param max_zise - * @param max_count - * @return - */ - public static DevCache get(File cacheDir, long max_zise, int max_count) { - // 判断是否存在缓存信息 - DevCache manager = mInstanceMap.get(cacheDir.getAbsoluteFile() + myPid()); - if (manager == null) { - // 初始化新的缓存信息, 并且保存 - manager = new DevCache(cacheDir, max_zise, max_count); - mInstanceMap.put(cacheDir.getAbsolutePath() + myPid(), manager); - } - return manager; - } - - /** - * 获取进程pid - * @return - */ - private static String myPid() { - return "_" + android.os.Process.myPid(); - } - - /** - * 最终初始化方法 - * @param cacheDir - * @param max_size - * @param max_count - */ - private DevCache(File cacheDir, long max_size, int max_count) { - if (cacheDir == null){ - new Exception("cacheDir is null"); - } else if (!cacheDir.exists() && !cacheDir.mkdirs()) { - new Exception("can't make dirs in " + cacheDir.getAbsolutePath()); - } - mCache = new DevCacheManager(cacheDir, max_size, max_count); - } - - /** - * Provides a means to save a cached file before the data are available. - * Since writing about the file is complete, and its close method is called, - * its contents will be registered in the cache. Example of use: - * DevCache cache = new DevCache(this) try { OutputStream stream = - * cache.put("myFileName") stream.write("some bytes".getBytes()); // now - * update cache! stream.close(); } catch(FileNotFoundException e){ - * e.printStackTrace() } - */ - class xFileOutputStream extends FileOutputStream { - File file; - - public xFileOutputStream(File file) throws FileNotFoundException { - super(file); - this.file = file; - } - - public void close() throws IOException { - super.close(); - mCache.put(file); - } - } - - // ===================================== - // ========== String数据 读写 ========== - // ===================================== - - /** - * 保存 String 数据到缓存中 - * @param key 保存的key - * @param value 保存的String数据 - */ - public void put(String key, String value) { - File file = mCache.newFile(key); - if (file == null || value == null){ - return; - } - BufferedWriter out = null; - try { - out = new BufferedWriter(new FileWriter(file), 1024); - out.write(value); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "put"); - } finally { - if (out != null) { - try { - out.flush(); - out.close(); - } catch (IOException e) { - } - } - mCache.put(file); - } - } - - /** - * 保存 String 数据到缓存中 - * @param key 保存的key - * @param value 保存的String数据 - * @param saveTime 保存的时间,单位:秒 - */ - public void put(String key, String value, int saveTime) { - if (key != null && value != null){ - put(key, DevCacheUtils.newStringWithDateInfo(saveTime, value)); - } - } - - /** - * 读取 String 数据 - * @param key - * @return String 数据 - */ - public String getAsString(String key) { - File file = mCache.get(key); - if (file == null){ - return null; - } - if (!file.exists()) - return null; - boolean removeFile = false; - BufferedReader in = null; - try { - in = new BufferedReader(new FileReader(file)); - String readString = ""; - String currentLine; - while ((currentLine = in.readLine()) != null) { - readString += currentLine; - } - if (!DevCacheUtils.isDue(readString)) { - return DevCacheUtils.clearDateInfo(readString); - } else { - LogPrintUtils.dTag(TAG, "getAsString key: " + key + " -> 文件已过期"); - removeFile = true; - return null; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAsString"); - return null; - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - } - } - if (removeFile) - remove(key); - } - } - - // ===================================== - // ======= JSONObject 数据 读写 ======== - // ===================================== - - /** - * 保存 JSONObject 数据到缓存中 - * @param key 保存的key - * @param value 保存的JSON数据 - */ - public void put(String key, JSONObject value) { - if (value != null){ - try { - put(key, value.toString()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "put JSONObject"); - } - } - } - - /** - * 保存 JSONObject 数据到缓存中 - * @param key 保存的key - * @param value 保存的JSONObject数据 - * @param saveTime 保存的时间,单位:秒 - */ - public void put(String key, JSONObject value, int saveTime) { - if (value != null){ - try { - put(key, value.toString(), saveTime); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "put JSONObject"); - } - } - } - - /** - * 读取 JSONObject 数据 - * @param key - * @return JSONObject 数据 - */ - public JSONObject getAsJSONObject(String key) { - String JSONString = getAsString(key); - if (JSONString != null){ - try { - JSONObject obj = new JSONObject(JSONString); - return obj; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAsJSONObject"); - } - } - return null; - } - - // ===================================== - // ======== JSONArray 数据 读写 ======== - // ===================================== - - /** - * 保存 JSONArray 数据到缓存中 - * @param key 保存的key - * @param value 保存的JSONArray数据 - */ - public void put(String key, JSONArray value) { - if (value != null){ - try { - put(key, value.toString()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "put JSONArray"); - } - } - } - - /** - * 保存 JSONArray 数据到缓存中 - * @param key 保存的key - * @param value 保存的JSONArray数据 - * @param saveTime 保存的时间,单位:秒 - */ - public void put(String key, JSONArray value, int saveTime) { - if (value != null){ - try { - put(key, value.toString(), saveTime); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "put JSONArray"); - } - } - } - - /** - * 读取 JSONArray 数据 - * @param key - * @return JSONArray 数据 - */ - public JSONArray getAsJSONArray(String key) { - String JSONString = getAsString(key); - if (JSONString != null) { - try { - JSONArray obj = new JSONArray(JSONString); - return obj; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAsJSONArray"); - } - } - return null; - } - - // ===================================== - // ========== byte 数据 读写 =========== - // ===================================== - - /** - * 保存 byte 数据到缓存中 - * @param key 保存的key - * @param value 保存的数据 - */ - public void put(String key, byte[] value) { - if (key == null || value == null){ - return; - } - File file = mCache.newFile(key); - FileOutputStream out = null; - try { - out = new FileOutputStream(file); - out.write(value); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "put byte[]"); - } finally { - if (out != null) { - try { - out.flush(); - out.close(); - } catch (IOException e) { - } - } - mCache.put(file); - } - } - - /** - * Cache for a stream - * @param key the file name. - * @return OutputStream stream for writing data. - * @throws FileNotFoundException if the file can not be created. - */ - public OutputStream put(String key) throws FileNotFoundException { - File file = mCache.newFile(key); - if (file != null){ - return new xFileOutputStream(file); - } - return null; - } - - /** - * @param key the file name. - * @return (InputStream or null) stream previously saved in cache. - * @throws FileNotFoundException if the file can not be opened - */ - public InputStream get(String key) throws FileNotFoundException { - File file = mCache.get(key); - if (file != null && file.exists()){ - return new FileInputStream(file); - } - return null; - } - - /** - * 保存 byte 数据到缓存中 - * @param key 保存的key - * @param value 保存的数据 - * @param saveTime 保存的时间,单位:秒 - */ - public void put(String key, byte[] value, int saveTime) { - put(key, DevCacheUtils.newByteArrayWithDateInfo(saveTime, value)); - } - - /** - * 获取 byte 数据 - * @param key - * @return byte 数据 - */ - public byte[] getAsBinary(String key) { - RandomAccessFile RAFile = null; - boolean removeFile = false; - try { - File file = mCache.get(key); - if (!file.exists()) - return null; - RAFile = new RandomAccessFile(file, "r"); - byte[] byteArray = new byte[(int) RAFile.length()]; - RAFile.read(byteArray); - if (!DevCacheUtils.isDue(byteArray)) { - return DevCacheUtils.clearDateInfo(byteArray); - } else { - LogPrintUtils.dTag(TAG, "getAsBinary - key: " + key + " -> 文件已过期"); - removeFile = true; - return null; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAsBinary"); - return null; - } finally { - if (RAFile != null) { - try { - RAFile.close(); - } catch (IOException e) { - } - } - if (removeFile) - remove(key); - } - } - - // ===================================== - // ========== 序列化 数据 读写 ========= - // ===================================== - - /** - * 保存 Serializable 数据到缓存中 - * @param key 保存的key - * @param value 保存的value - */ - public void put(String key, Serializable value) { - put(key, value, -1); - } - - /** - * 保存 Serializable 数据到缓存中 - * @param key 保存的key - * @param value 保存的value - * @param saveTime 保存的时间,单位:秒 - */ - public void put(String key, Serializable value, int saveTime) { - ByteArrayOutputStream baos = null; - ObjectOutputStream oos = null; - try { - baos = new ByteArrayOutputStream(); - oos = new ObjectOutputStream(baos); - oos.writeObject(value); - byte[] data = baos.toByteArray(); - if (saveTime != -1) { - put(key, data, saveTime); - } else { - put(key, data); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "put"); - } finally { - if (baos != null) { - try { - baos.close(); - } catch (IOException e) { - } - } - if (oos != null) { - try { - oos.close(); - } catch (IOException e) { - } - } - } - } - - /** - * 读取 Serializable 数据 - * @param key - * @return Serializable 数据 - */ - public Object getAsObject(String key) { - byte[] data = getAsBinary(key); - if (data != null) { - ByteArrayInputStream bais = null; - ObjectInputStream ois = null; - try { - bais = new ByteArrayInputStream(data); - ois = new ObjectInputStream(bais); - Object reObject = ois.readObject(); - return reObject; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAsObject"); - return null; - } finally { - if (bais != null) { - try { - bais.close(); - } catch (IOException e) { - } - } - if (ois != null) { - try { - ois.close(); - } catch (IOException e) { - } - } - } - } - return null; - } - - // ===================================== - // ========== bitmap 数据 读写 ========= - // ===================================== - - /** - * 保存 bitmap 到缓存中 - * @param key 保存的key - * @param value 保存的bitmap数据 - */ - public void put(String key, Bitmap value) { - put(key, DevCacheUtils.bitmap2Bytes(value)); - } - - /** - * 保存 bitmap 到缓存中 - * @param key 保存的key - * @param value 保存的 bitmap 数据 - * @param saveTime 保存的时间,单位:秒 - */ - public void put(String key, Bitmap value, int saveTime) { - put(key, DevCacheUtils.bitmap2Bytes(value), saveTime); - } - - /** - * 读取 bitmap 数据 - * @param key - * @return bitmap 数据 - */ - public Bitmap getAsBitmap(String key) { - byte[] bytes = getAsBinary(key); - if (bytes == null) { - return null; - } - return DevCacheUtils.bytes2Bimap(bytes); - } - - // ===================================== - // ======== drawable 数据 读写 ========= - // ===================================== - - /** - * 保存 drawable 到缓存中 - * @param key 保存的key - * @param value 保存的drawable数据 - */ - public void put(String key, Drawable value) { - put(key, DevCacheUtils.drawable2Bitmap(value)); - } - - /** - * 保存 drawable 到缓存中 - * @param key 保存的key - * @param value 保存的 drawable 数据 - * @param saveTime 保存的时间,单位:秒 - */ - public void put(String key, Drawable value, int saveTime) { - put(key, DevCacheUtils.drawable2Bitmap(value), saveTime); - } - - /** - * 读取 Drawable 数据 - * @param key - * @return Drawable 数据 - */ - public Drawable getAsDrawable(String key) { - byte[] bytes = getAsBinary(key); - if (bytes == null) { - return null; - } - return DevCacheUtils.bitmap2Drawable(DevCacheUtils.bytes2Bimap(bytes)); - } - - /** - * 获取缓存文件 - * @param key - * @return value 缓存的文件 - */ - public File file(String key) { - File f = mCache.newFile(key); - if (f != null && f.exists()){ - return f; - } - return null; - } - - /** - * 移除某个key - * @param key - * @return 是否移除成功 - */ - public boolean remove(String key) { - return mCache.remove(key); - } - - /** 清除所有数据 */ - public void clear() { - mCache.clear(); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheUtils.java b/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheUtils.java deleted file mode 100644 index 230204ac58..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheUtils.java +++ /dev/null @@ -1,274 +0,0 @@ -package dev.utils.app.cache; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.PixelFormat; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; - -import java.io.ByteArrayOutputStream; - -import dev.utils.LogPrintUtils; - -/** - * detail: 缓存检查(时间)工具类 - * Created by 杨福海(michael) www.yangfuhai.com - * https://github.com/yangfuhai/ASimpleCache - * Update to Ttt - */ -final class DevCacheUtils { - - private DevCacheUtils(){ - } - - // 日志Tag - private static final String TAG = DevCacheUtils.class.getSimpleName(); - - /** - * 判断缓存的 String 数据是否到期 - * @param str - * @return true: 到期了 false: 还没有到期 - */ - public static boolean isDue(String str) { - return isDue(str.getBytes()); - } - - /** - * 判断缓存的 byte 数据是否到期 - * @param data - * @return true: 到期了 false: 还没有到期 - */ - public static boolean isDue(byte[] data) { - // 获取时间数据信息 - String[] strs = getDateInfoFromDate(data); - // 判断是否过期 - if (strs != null && strs.length == 2) { - // 保存的时间 - String saveTimeStr = strs[0]; - // 判断是否0开头,是的话裁剪 - while (saveTimeStr.startsWith("0")) { - saveTimeStr = saveTimeStr.substring(1, saveTimeStr.length()); - } - // 转换时间 - long saveTime = Long.valueOf(saveTimeStr); // 保存时间 - long deleteAfter = Long.valueOf(strs[1]); // 过期时间 - // 判断当前时间是否大于 保存时间 + 过期时间 - if (System.currentTimeMillis() > saveTime + deleteAfter * 1000) { - return true; - } - } - return false; - } - - // - - - /** - * 保存数据, 创建时间信息 - * @param second - * @param strInfo - * @return - */ - public static String newStringWithDateInfo(int second, String strInfo) { - return createDateInfo(second) + strInfo; - } - - /** - * 保存数据, 创建时间信息 - * @param second - * @param data - * @return - */ - public static byte[] newByteArrayWithDateInfo(int second, byte[] data) { - if (data != null){ - try { - byte[] dataArys = createDateInfo(second).getBytes(); - byte[] retData = new byte[dataArys.length + data.length]; - System.arraycopy(dataArys, 0, retData, 0, dataArys.length); - System.arraycopy(data, 0, retData, dataArys.length, data.length); - return retData; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "newByteArrayWithDateInfo"); - } - } - return null; - } - - private static final char mSeparator = ' '; - - /** - * 创建时间信息 - * @param second - * @return - */ - private static String createDateInfo(int second) { - String currentTime = System.currentTimeMillis() + ""; - while (currentTime.length() < 13) { - currentTime = "0" + currentTime; - } - return currentTime + "-" + second + mSeparator; - } - - /** - * 清空时间信息 - * @param strInfo - * @return - */ - public static String clearDateInfo(String strInfo) { - if (strInfo != null && hasDateInfo(strInfo.getBytes())) { - strInfo = strInfo.substring(strInfo.indexOf(mSeparator) + 1, strInfo.length()); - } - return strInfo; - } - - /** - * 清空时间信息 - * @param data - * @return - */ - public static byte[] clearDateInfo(byte[] data) { - if (hasDateInfo(data)) { - try { - return copyOfRange(data, indexOf(data, mSeparator) + 1, data.length); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "clearDateInfo"); - } - } - return data; - } - - /** - * 检验时间信息 - * @param data - * @return - */ - private static boolean hasDateInfo(byte[] data) { - return data != null && data.length > 15 && data[13] == '-' && indexOf(data, mSeparator) > 14; - } - - /** - * 获取时间信息 - 保存时间、过期时间 - * @param data - * @return - */ - private static String[] getDateInfoFromDate(byte[] data) { - if (hasDateInfo(data)) { - try { - // 保存时间 - String saveDate = new String(copyOfRange(data, 0, 13)); - // 过期时间 - String deleteAfter = new String(copyOfRange(data, 14, indexOf(data, mSeparator))); - // 返回数据 - return new String[]{saveDate, deleteAfter}; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getDateInfoFromDate"); - } - } - return null; - } - - private static int indexOf(byte[] data, char c) { - if (data != null) { - for (int i = 0; i < data.length; i++) { - if (data[i] == c) { - return i; - } - } - } - return -1; - } - - private static byte[] copyOfRange(byte[] original, int from, int to) throws Exception { - int newLength = to - from; - if (newLength < 0) - throw new IllegalArgumentException(from + " > " + to); - - byte[] copy = new byte[newLength]; - System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); - return copy; - } - - /** - * Bitmap → byte[] - * @param bm - * @return - */ - public static byte[] bitmap2Bytes(Bitmap bm) { - if (bm == null) { - return null; - } - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - bm.compress(Bitmap.CompressFormat.PNG, 100, baos); - return baos.toByteArray(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "bitmap2Bytes"); - } - return null; - } - - /** - * byte[] → Bitmap - * @param bytes - * @return - */ - public static Bitmap bytes2Bimap(byte[] bytes) { - if (bytes != null && bytes.length != 0) { - try { - return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "bytes2Bimap"); - } - } - return null; - } - - /** - * Drawable → Bitmap - * @param drawable - * @return - */ - public static Bitmap drawable2Bitmap(Drawable drawable) { - if (drawable == null) { - return null; - } - try { - // 取 drawable 的长宽 - int w = drawable.getIntrinsicWidth(); - int h = drawable.getIntrinsicHeight(); - // 取 drawable 的颜色格式 - Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; - // 建立对应 bitmap - Bitmap bitmap = Bitmap.createBitmap(w, h, config); - // 建立对应 bitmap 的画布 - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, w, h); - // 把 drawable 内容画到画布中 - drawable.draw(canvas); - return bitmap; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "drawable2Bitmap"); - } - return null; - } - - /** - * Bitmap → Drawable - * @param bm - * @return - */ - @SuppressWarnings("deprecation") - public static Drawable bitmap2Drawable(Bitmap bm) { - if (bm == null) { - return null; - } - try { - BitmapDrawable bd = new BitmapDrawable(bm); - bd.setTargetDensity(bm.getDensity()); - return new BitmapDrawable(bm); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "bitmap2Drawable"); - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapCropUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/BitmapCropUtils.java deleted file mode 100644 index 5482538c1f..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapCropUtils.java +++ /dev/null @@ -1,88 +0,0 @@ -package dev.utils.app.image; - -import android.graphics.Bitmap; -import android.text.TextUtils; - -import dev.utils.LogPrintUtils; - -/** - * detail: 图片裁剪工具类 - * Created by Ttt - */ -public final class BitmapCropUtils { - - private BitmapCropUtils(){ - } - - // 日志Tag - private static final String TAG = BitmapCropUtils.class.getSimpleName(); - - // =============== 裁剪图片,裁剪中间部分,防止全图压缩 =============== - /** - * 裁剪图片(默认比例16:9) - * @param fPath 保存路径 - * @param bitmap 图片 - */ - public static void cropBitmap(String fPath, Bitmap bitmap){ - cropBitmap(fPath, bitmap, 16.0f, 9.0f); - } - - /** - * 裁剪图片 - * @param fPath 保存路径 - * @param bitmap 图片 - * @param wScale 宽度比例 - * @param hScale 高度比例 - */ - public static void cropBitmap(String fPath, Bitmap bitmap, float wScale, float hScale){ - if(TextUtils.isEmpty(fPath)){ - return; // 防止保存路径为null - } else if (bitmap == null){ - return; // 防止图片为null - } - // 裁剪处理后的图片 - Bitmap cBitmap = null; - try { - // 获取图片宽度 - int iWidth = bitmap.getWidth(); - // 获取图片高度 - int iHeight = bitmap.getHeight(); - // 获取需要裁剪的高度 - int rHeight = (int) ((iWidth * hScale) / wScale); - // 判断需要裁剪的高度与偏移差距 - int dHeight = iHeight - rHeight; - // -- - // 判断裁剪方式 - if (dHeight >= 0){ // 属于宽度 * 对应比例 >= 高度 -> 以高度做偏移 - // 计算偏移的y轴 - int offsetY = dHeight / 2; - // 创建图片 - cBitmap = Bitmap.createBitmap(bitmap, 0, offsetY, iWidth, rHeight, null, false); - } else { // 以宽度做偏移 - // 获取需要裁剪的宽度 - int rWidth = (int) ((iHeight * wScale) / hScale); - // 判断需要裁剪的宽度与偏移差距 - int dWidth = iWidth - rWidth; - // 计算偏移的X轴 - int offsetX = dWidth / 2; - // 创建图片 - cBitmap = Bitmap.createBitmap(bitmap, offsetX, 0, rWidth, iHeight, null, false); - } - if (cBitmap != null){ - // 保存图片 - BitmapUtils.saveBitmapToSDCardPNG(cBitmap, fPath, 85); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "cropBitmap"); - } finally { - // -- 清空资源 -- - try { - if(cBitmap != null && !cBitmap.isRecycled()){ - cBitmap.recycle(); - } - } catch (Exception e) { - } - cBitmap = null; - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapExtendUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/BitmapExtendUtils.java deleted file mode 100644 index a8891f11ce..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapExtendUtils.java +++ /dev/null @@ -1,1224 +0,0 @@ -package dev.utils.app.image; - -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.LinearGradient; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.PaintFlagsDrawFilter; -import android.graphics.PorterDuff.Mode; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Shader.TileMode; -import android.media.ExifInterface; -import android.net.Uri; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: Bitmap工具类主要包括获取Bitmap和对Bitmap的操作 - * Created by Ttt - */ -public final class BitmapExtendUtils { - - private BitmapExtendUtils() { - } - - // 日志Tag - private static final String TAG = BitmapExtendUtils.class.getSimpleName(); - - /** - * 图片压缩处理(使用Options的方法) - * ================ - * 说明 使用方法: - * 首先你要将Options的inJustDecodeBounds属性设置为true,BitmapFactory.decode一次图片 。 - * 然后将Options连同期望的宽度和高度一起传递到到本方法中。 - * 之后再使用本方法的返回值做参数调用BitmapFactory.decode创建图片。 - * ================ - * 说明 BitmapFactory创建bitmap会尝试为已经构建的bitmap分配内存 - * ,这时就会很容易导致OOM出现。为此每一种创建方法都提供了一个可选的Options参数 - * ,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存 - * ,返回值也不再是一个Bitmap对象, 而是null。虽然Bitmap是null了,但是Options的outWidth、 outHeight和outMimeType属性都会被赋值。 - * - * @param reqWidth 目标宽度,这里的宽高只是阀值,实际显示的图片将小于等于这个值 - * @param reqHeight 目标高度,这里的宽高只是阀值,实际显示的图片将小于等于这个值 - */ - public static BitmapFactory.Options calculateInSampleSize(final BitmapFactory.Options options, final int reqWidth, final int reqHeight) { - // 源图片的高度和宽度 - final int height = options.outHeight; - final int width = options.outWidth; - int inSampleSize = 1; - if (height > 400 || width > 450) { - if (height > reqHeight || width > reqWidth) { - // 计算出实际宽高和目标宽高的比率 - final int heightRatio = Math.round((float) height / (float) reqHeight); - final int widthRatio = Math.round((float) width / (float) reqWidth); - // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 - // 一定都会大于等于目标的宽和高。 - inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; - } - } - // 设置压缩比例 - options.inSampleSize = inSampleSize; - options.inJustDecodeBounds = false; - return options; - } - - /** - * 获取一个指定大小的bitmap - * @param res Resources - * @param resId 图片ID - * @param reqWidth 目标宽度 - * @param reqHeight 目标高度 - */ - public static Bitmap getBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { - // BitmapFactory.Options options = new BitmapFactory.Options(); - // options.inJustDecodeBounds = true; - // BitmapFactory.decodeResource(res, resId, options); - // options = BitmapHelper.calculateInSampleSize(options, reqWidth, reqHeight); - // return BitmapFactory.decodeResource(res, resId, options); - - // 通过JNI的形式读取本地图片达到节省内存的目的 - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Config.RGB_565; - options.inPurgeable = true; - options.inInputShareable = true; - InputStream is = res.openRawResource(resId); - return getBitmapFromStream(is, null, reqWidth, reqHeight); - } - - /** - * 获取一个指定大小的bitmap - * @param reqWidth 目标宽度 - * @param reqHeight 目标高度 - */ - public static Bitmap getBitmapFromFile(String pathName, int reqWidth, int reqHeight) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(pathName, options); - options = calculateInSampleSize(options, reqWidth, reqHeight); - return BitmapFactory.decodeFile(pathName, options); - } - - /** - * 获取一个指定大小的bitmap - * @param data Bitmap的byte数组 - * @param offset image从byte数组创建的起始位置 - * @param length the number of bytes, 从offset处开始的长度 - * @param reqWidth 目标宽度 - * @param reqHeight 目标高度 - */ - public static Bitmap getBitmapFromByteArray(byte[] data, int offset, int length, int reqWidth, int reqHeight) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeByteArray(data, offset, length, options); - options = calculateInSampleSize(options, reqWidth, reqHeight); - return BitmapFactory.decodeByteArray(data, offset, length, options); - } - - // == - - - /** - * Stream转换成Byte - * @param inputStream InputStream - * @return Byte数组 - */ - public static byte[] getBytesFromStream(InputStream inputStream) { - ByteArrayOutputStream os = new ByteArrayOutputStream(1024); - byte[] buffer = new byte[1024]; - int len; - try { - while ((len = inputStream.read(buffer)) >= 0) { - os.write(buffer, 0, len); - } - } catch (IOException e) { - LogPrintUtils.eTag(TAG, e, "getBytesFromStream"); - } - return os.toByteArray(); - } - - - /** - * 获取一个指定大小的bitmap - * @param is 从输入流中读取Bitmap - * @param outPadding If not null, return the padding rect for the bitmap if it - * exists, otherwise set padding to [-1,-1,-1,-1]. If no bitmap - * is returned (null) then padding is unchanged. - * @param reqWidth 目标宽度 - * @param reqHeight 目标高度 - */ - public static Bitmap getBitmapFromStream(InputStream is, Rect outPadding, int reqWidth, int reqHeight) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(is, outPadding, options); - options = calculateInSampleSize(options, reqWidth, reqHeight); - return BitmapFactory.decodeStream(is, outPadding, options); - } - - // == - - /** - * 合并Bitmap - * @param bgd 背景Bitmap - * @param fg 前景Bitmap - * @return 合成后的Bitmap - */ - public static Bitmap combineImages(Bitmap bgd, Bitmap fg) { - Bitmap bmp; - - int width = bgd.getWidth() > fg.getWidth() ? bgd.getWidth() : fg.getWidth(); - int height = bgd.getHeight() > fg.getHeight() ? bgd.getHeight() : fg.getHeight(); - - bmp = Bitmap.createBitmap(width, height, Config.ARGB_8888); - Paint paint = new Paint(); - paint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP)); - - Canvas canvas = new Canvas(bmp); - canvas.drawBitmap(bgd, 0, 0, null); - canvas.drawBitmap(fg, 0, 0, paint); - - return bmp; - } - - /** - * 合并Bitmap - * @param bgd 后景Bitmap - * @param fg 前景Bitmap - * @return 合成后Bitmap - */ - public static Bitmap combineImagesToSameSize(Bitmap bgd, Bitmap fg) { - Bitmap bmp; - - int width = bgd.getWidth() < fg.getWidth() ? bgd.getWidth() : fg.getWidth(); - int height = bgd.getHeight() < fg.getHeight() ? bgd.getHeight() : fg.getHeight(); - - if (fg.getWidth() != width && fg.getHeight() != height) { - fg = zoom(fg, width, height); - } - if (bgd.getWidth() != width && bgd.getHeight() != height) { - bgd = zoom(bgd, width, height); - } - - bmp = Bitmap.createBitmap(width, height, Config.ARGB_8888); - Paint paint = new Paint(); - paint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP)); - - Canvas canvas = new Canvas(bmp); - canvas.drawBitmap(bgd, 0, 0, null); - canvas.drawBitmap(fg, 0, 0, paint); - - return bmp; - } - - /** - * 放大缩小图片 - * @param bitmap 源Bitmap - * @param w 宽 - * @param h 高 - * @return 目标Bitmap - */ - public static Bitmap zoom(Bitmap bitmap, int w, int h) { - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Matrix matrix = new Matrix(); - float scaleWidht = ((float) w / width); - float scaleHeight = ((float) h / height); - matrix.postScale(scaleWidht, scaleHeight); - Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); - return newbmp; - } - - /** - * 获得圆角图片的方法 - * @param bitmap 源Bitmap - * @param roundPx 圆角大小 - * @return 期望Bitmap - */ - public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) { - Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); - Canvas canvas = new Canvas(output); - - final int color = 0xff424242; - final Paint paint = new Paint(); - final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); - final RectF rectF = new RectF(rect); - - paint.setAntiAlias(true); - canvas.drawARGB(0, 0, 0, 0); - paint.setColor(color); - canvas.drawRoundRect(rectF, roundPx, roundPx, paint); - - paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); - canvas.drawBitmap(bitmap, rect, rect, paint); - - return output; - } - - /** - * 获得带倒影的图片方法 - * @param bitmap 源Bitmap - * @return 带倒影的Bitmap - */ - public static Bitmap createReflectionBitmap(Bitmap bitmap) { - final int reflectionGap = 4; - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - - Matrix matrix = new Matrix(); - matrix.preScale(1, -1); - - Bitmap reflectionImage = Bitmap.createBitmap(bitmap, 0, height / 2, width, height / 2, matrix, false); - - Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height / 2), Config.ARGB_8888); - - Canvas canvas = new Canvas(bitmapWithReflection); - canvas.drawBitmap(bitmap, 0, 0, null); - Paint deafalutPaint = new Paint(); - canvas.drawRect(0, height, width, height + reflectionGap, deafalutPaint); - - canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null); - - Paint paint = new Paint(); - LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0, - bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff,0x00ffffff, TileMode.CLAMP); - paint.setShader(shader); - // Set the Transfer mode to be porter duff and destination in - paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); - // Draw a rectangle using the paint with our linear gradient - canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint); - - return bitmapWithReflection; - } - - /** - * 压缩图片大小 - * @param image 源Bitmap - * @param size kb为单位, 大于xx大小, 则一直压缩 - * @return 压缩后的Bitmap - */ - public static Bitmap compressImage(Bitmap image, long size) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 - int options = 100; - while (baos.toByteArray().length / 1024 > size) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩 - baos.reset();// 重置baos即清空baos - image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中 - options -= 10;// 每次都减少10 - } - ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中 - Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片 - return bitmap; - } - - /** - * 将彩色图转换为灰度图 - * @param img 源Bitmap - * @return 返回转换好的位图 - */ - public static Bitmap convertGreyImg(Bitmap img) { - int width = img.getWidth(); // 获取位图的宽 - int height = img.getHeight(); // 获取位图的高 - - int[] pixels = new int[width * height]; // 通过位图的大小创建像素点数组 - - img.getPixels(pixels, 0, width, 0, 0, width, height); - int alpha = 0xFF << 24; - for (int i = 0; i < height; i++) { - for (int j = 0; j < width; j++) { - int grey = pixels[width * i + j]; - - int red = ((grey & 0x00FF0000) >> 16); - int green = ((grey & 0x0000FF00) >> 8); - int blue = (grey & 0x000000FF); - - grey = (int) ((float) red * 0.3 + (float) green * 0.59 + (float) blue * 0.11); - grey = alpha | (grey << 16) | (grey << 8) | grey; - pixels[width * i + j] = grey; - } - } - Bitmap result = Bitmap.createBitmap(width, height, Config.RGB_565); - result.setPixels(pixels, 0, width, 0, 0, width, height); - return result; - } - - /** - * 转换图片成圆形 - * @param bitmap 传入Bitmap对象 - * @return 圆形Bitmap - */ - public static Bitmap getRoundBitmap(Bitmap bitmap) { - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - float roundPx; - float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom; - if (width <= height) { - roundPx = width / 2; - top = 0; - bottom = width; - left = 0; - right = width; - height = width; - dst_left = 0; - dst_top = 0; - dst_right = width; - dst_bottom = width; - } else { - roundPx = height / 2; - float clip = (width - height) / 2; - left = clip; - right = width - clip; - top = 0; - bottom = height; - width = height; - dst_left = 0; - dst_top = 0; - dst_right = height; - dst_bottom = height; - } - - Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888); - Canvas canvas = new Canvas(output); - - final int color = 0xff424242; - final Paint paint = new Paint(); - final Rect src = new Rect((int) left, (int) top, (int) right, (int) bottom); - final Rect dst = new Rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom); - final RectF rectF = new RectF(dst); - - paint.setAntiAlias(true); - - canvas.drawARGB(0, 0, 0, 0); - paint.setColor(color); - canvas.drawRoundRect(rectF, roundPx, roundPx, paint); - - paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); - canvas.drawBitmap(bitmap, src, dst, paint); - return output; - } - - /** - * Returns a Bitmap representing the thumbnail of the specified Bitmap. The - * size of the thumbnail is defined by the dimension - * android.R.dimen.launcher_application_icon_size. - * This method is not thread-safe and should be invoked on the UI thread only. - * @param bitmap The bitmap to get a thumbnail of. - * @return A thumbnail for the specified bitmap or the bitmap itself if the thumbnail could not be created. - */ - public static Bitmap createThumbnailBitmap(Bitmap bitmap) { - int sIconWidth = -1; - int sIconHeight = -1; - sIconWidth = sIconHeight = (int) DevUtils.getContext().getResources().getDimension(android.R.dimen.app_icon_size); - - final Paint sPaint = new Paint(); - final Rect sBounds = new Rect(); - final Rect sOldBounds = new Rect(); - Canvas sCanvas = new Canvas(); - - int width = sIconWidth; - int height = sIconHeight; - - sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG)); - - final int bitmapWidth = bitmap.getWidth(); - final int bitmapHeight = bitmap.getHeight(); - - if (width > 0 && height > 0) { - if (width < bitmapWidth || height < bitmapHeight) { - final float ratio = (float) bitmapWidth / bitmapHeight; - - if (bitmapWidth > bitmapHeight) { - height = (int) (width / ratio); - } else if (bitmapHeight > bitmapWidth) { - width = (int) (height * ratio); - } - - final Config c = (width == sIconWidth && height == sIconHeight) ? bitmap.getConfig() : Config.ARGB_8888; - final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c); - final Canvas canvas = sCanvas; - final Paint paint = sPaint; - canvas.setBitmap(thumb); - paint.setDither(false); - paint.setFilterBitmap(true); - sBounds.set((sIconWidth - width) / 2,(sIconHeight - height) / 2, width, height); - sOldBounds.set(0, 0, bitmapWidth, bitmapHeight); - canvas.drawBitmap(bitmap, sOldBounds, sBounds, paint); - return thumb; - } else if (bitmapWidth < width || bitmapHeight < height) { - final Config c = Config.ARGB_8888; - final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c); - final Canvas canvas = sCanvas; - final Paint paint = sPaint; - canvas.setBitmap(thumb); - paint.setDither(false); - paint.setFilterBitmap(true); - canvas.drawBitmap(bitmap, (sIconWidth - bitmapWidth) / 2,(sIconHeight - bitmapHeight) / 2, paint); - return thumb; - } - } - return bitmap; - } - - /** - * 生成水印图片 水印在右下角 - * @param src the bitmap object you want proecss - * @param watermark the water mark above the src - * @return return a bitmap object ,if paramter's length is 0,return null - */ - public static Bitmap createWatermarkBitmap(Bitmap src, Bitmap watermark) { - if (src == null) { - return null; - } - int w = src.getWidth(); - int h = src.getHeight(); - int ww = watermark.getWidth(); - int wh = watermark.getHeight(); - // create the new blank bitmap - Bitmap newb = Bitmap.createBitmap(w, h, Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图 - Canvas cv = new Canvas(newb); - // draw src into - cv.drawBitmap(src, 0, 0, null);// 在 0,0坐标开始画入src - // draw watermark into - cv.drawBitmap(watermark, w - ww + 5, h - wh + 5, null);// 在src的右下角画入水印 - // save all clip - cv.save(Canvas.ALL_SAVE_FLAG);// 保存 - // store - cv.restore();// 存储 - return newb; - } - - /** - * 重新编码Bitmap - * @param src 需要重新编码的Bitmap - * @param format 编码后的格式(目前只支持png和jpeg这两种格式) - * @param quality 重新生成后的bitmap的质量 - * @return 返回重新生成后的bitmap - */ - public static Bitmap codec(Bitmap src, Bitmap.CompressFormat format, int quality) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - src.compress(format, quality, os); - byte[] array = os.toByteArray(); - return BitmapFactory.decodeByteArray(array, 0, array.length); - } - - /** - * 图片压缩方法:(使用compress的方法) - * 说明 如果bitmap本身的大小小于maxSize,则不作处理 - * @param bitmap 要压缩的图片 - * @param maxSize 压缩后的大小,单位kb - */ - public static void compress(Bitmap bitmap, double maxSize) { - // 将bitmap放至数组中,意在获得bitmap的大小(与实际读取的原文件要大) - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - // 格式、质量、输出流 - bitmap.compress(Bitmap.CompressFormat.PNG, 70, baos); - byte[] b = baos.toByteArray(); - // 将字节换成KB - double mid = b.length / 1024; - // 获取bitmap大小 是允许最大大小的多少倍 - double i = mid / maxSize; - // 判断bitmap占用空间是否大于允许最大空间 如果大于则压缩 小于则不压缩 - if (i > 1) { - // 缩放图片 此处用到平方根 将宽带和高度压缩掉对应的平方根倍 - // (保持宽高不变,缩放后也达到了最大占用空间的大小) - bitmap = scale(bitmap, bitmap.getWidth() / Math.sqrt(i),bitmap.getHeight() / Math.sqrt(i)); - } - } - - /** - * 图片的缩放方法 - * @param src 源图片资源 - * @param newWidth 缩放后宽度 - * @param newHeight 缩放后高度 - */ - public static Bitmap scale(Bitmap src, double newWidth, double newHeight) { - // 记录src的宽高 - float width = src.getWidth(); - float height = src.getHeight(); - // 创建一个matrix容器 - Matrix matrix = new Matrix(); - // 计算缩放比例 - float scaleWidth = ((float) newWidth) / width; - float scaleHeight = ((float) newHeight) / height; - // 开始缩放 - matrix.postScale(scaleWidth, scaleHeight); - // 创建缩放后的图片 - return Bitmap.createBitmap(src, 0, 0, (int) width, (int) height, matrix, true); - } - - /** - * 图片的缩放方法 - * @param src 源图片资源 - * @param scaleMatrix 缩放规则 - */ - public static Bitmap scale(Bitmap src, Matrix scaleMatrix) { - return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), scaleMatrix, true); - } - - /** - * 图片的缩放方法 - * @param src 源图片资源 - * @param scaleX 横向缩放比例 - * @param scaleY 纵向缩放比例 - */ - public static Bitmap scale(Bitmap src, float scaleX, float scaleY) { - Matrix matrix = new Matrix(); - matrix.postScale(scaleX, scaleY); - return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); - } - - /** - * 图片的缩放方法 - * @param src 源图片资源 - * @param scale 缩放比例 - */ - public static Bitmap scale(Bitmap src, float scale) { - return scale(src, scale, scale); - } - - /** - * 旋转图片 - * @param angle 旋转角度 - * @param bitmap 要旋转的图片 - * @return 旋转后的图片 - */ - public static Bitmap rotate(Bitmap bitmap, int angle) { - Matrix matrix = new Matrix(); - matrix.postRotate(angle); - return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); - } - - /** - * 水平翻转处理 - * @param bitmap 原图 - * @return 水平翻转后的图片 - */ - public static Bitmap reverseByHorizontal(Bitmap bitmap) { - Matrix matrix = new Matrix(); - matrix.preScale(-1, 1); - return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); - } - - /** - * 垂直翻转处理 - * @param bitmap 原图 - * @return 垂直翻转后的图片 - */ - public static Bitmap reverseByVertical(Bitmap bitmap) { - Matrix matrix = new Matrix(); - matrix.preScale(1, -1); - return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); - } - - /** - * 更改图片色系,变亮或变暗 - * @param delta 图片的亮暗程度值,越小图片会越亮,取值范围(0,24) - * @return - */ - public static Bitmap adjustTone(Bitmap src, int delta) { - if (delta >= 24 || delta <= 0) { - return null; - } - // 设置高斯矩阵 - int[] gauss = new int[]{1, 2, 1, 2, 4, 2, 1, 2, 1}; - int width = src.getWidth(); - int height = src.getHeight(); - Bitmap bitmap = Bitmap.createBitmap(width, height, Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - int pixColor = 0; - int newR = 0; - int newG = 0; - int newB = 0; - int idx = 0; - int[] pixels = new int[width * height]; - - src.getPixels(pixels, 0, width, 0, 0, width, height); - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - idx = 0; - for (int m = -1; m <= 1; m++) { - for (int n = -1; n <= 1; n++) { - pixColor = pixels[(i + m) * width + k + n]; - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - newR += (pixR * gauss[idx]); - newG += (pixG * gauss[idx]); - newB += (pixB * gauss[idx]); - idx++; - } - } - newR /= delta; - newG /= delta; - newB /= delta; - newR = Math.min(255, Math.max(0, newR)); - newG = Math.min(255, Math.max(0, newG)); - newB = Math.min(255, Math.max(0, newB)); - pixels[i * width + k] = Color.argb(255, newR, newG, newB); - newR = 0; - newG = 0; - newB = 0; - } - } - bitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return bitmap; - } - - /** - * 将彩色图转换为黑白图 - * @param bmp 位图 - * @return 返回转换好的位图 - */ - public static Bitmap convertToBlackWhite(Bitmap bmp) { - int width = bmp.getWidth(); - int height = bmp.getHeight(); - int[] pixels = new int[width * height]; - bmp.getPixels(pixels, 0, width, 0, 0, width, height); - - int alpha = 0xFF << 24; // 默认将bitmap当成24色图片 - for (int i = 0; i < height; i++) { - for (int j = 0; j < width; j++) { - int grey = pixels[width * i + j]; - - int red = ((grey & 0x00FF0000) >> 16); - int green = ((grey & 0x0000FF00) >> 8); - int blue = (grey & 0x000000FF); - - grey = (int) (red * 0.3 + green * 0.59 + blue * 0.11); - grey = alpha | (grey << 16) | (grey << 8) | grey; - pixels[width * i + j] = grey; - } - } - Bitmap newBmp = Bitmap.createBitmap(width, height, Config.RGB_565); - newBmp.setPixels(pixels, 0, width, 0, 0, width, height); - return newBmp; - } - - /** - * 读取图片属性 图片被旋转的角度 - * @param path 图片绝对路径 - * @return 旋转的角度 - */ - public static int getImageDegree(String path) { - int degree = 0; - try { - ExifInterface exifInterface = new ExifInterface(path); - int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); - switch (orientation) { - case ExifInterface.ORIENTATION_ROTATE_90: - degree = 90; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - degree = 180; - break; - case ExifInterface.ORIENTATION_ROTATE_270: - degree = 270; - break; - } - } catch (IOException e) { - LogPrintUtils.eTag(TAG, e, "getImageDegree"); - } - return degree; - } - - /** - * 饱和度处理 - * @param bitmap 原图 - * @param saturationValue 新的饱和度值 - * @return 改变了饱和度值之后的图片 - */ - public static Bitmap saturation(Bitmap bitmap, int saturationValue) { - // 计算出符合要求的饱和度值 - float newSaturationValue = saturationValue * 1.0F / 127; - // 创建一个颜色矩阵 - ColorMatrix saturationColorMatrix = new ColorMatrix(); - // 设置饱和度值 - saturationColorMatrix.setSaturation(newSaturationValue); - // 创建一个画笔并设置其颜色过滤器 - Paint paint = new Paint(); - paint.setColorFilter(new ColorMatrixColorFilter(saturationColorMatrix)); - // 创建一个新的图片并创建画布 - Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); - Canvas canvas = new Canvas(newBitmap); - // 将原图使用给定的画笔画到画布上 - canvas.drawBitmap(bitmap, 0, 0, paint); - return newBitmap; - } - - /** - * 亮度处理 - * @param bitmap 原图 - * @param lumValue 新的亮度值 - * @return 改变了亮度值之后的图片 - */ - public static Bitmap lum(Bitmap bitmap, int lumValue) { - // 计算出符合要求的亮度值 - float newlumValue = lumValue * 1.0F / 127; - // 创建一个颜色矩阵 - ColorMatrix lumColorMatrix = new ColorMatrix(); - // 设置亮度值 - lumColorMatrix.setScale(newlumValue, newlumValue, newlumValue, 1); - // 创建一个画笔并设置其颜色过滤器 - Paint paint = new Paint(); - paint.setColorFilter(new ColorMatrixColorFilter(lumColorMatrix)); - // 创建一个新的图片并创建画布 - Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); - Canvas canvas = new Canvas(newBitmap); - // 将原图使用给定的画笔画到画布上 - canvas.drawBitmap(bitmap, 0, 0, paint); - return newBitmap; - } - - /** - * 色相处理 - * @param bitmap 原图 - * @param hueValue 新的色相值 - * @return 改变了色相值之后的图片 - */ - public static Bitmap hue(Bitmap bitmap, int hueValue) { - // 计算出符合要求的色相值 - float newHueValue = (hueValue - 127) * 1.0F / 127 * 180; - // 创建一个颜色矩阵 - ColorMatrix hueColorMatrix = new ColorMatrix(); - // 控制让红色区在色轮上旋转的角度 - hueColorMatrix.setRotate(0, newHueValue); - // 控制让绿红色区在色轮上旋转的角度 - hueColorMatrix.setRotate(1, newHueValue); - // 控制让蓝色区在色轮上旋转的角度 - hueColorMatrix.setRotate(2, newHueValue); - // 创建一个画笔并设置其颜色过滤器 - Paint paint = new Paint(); - paint.setColorFilter(new ColorMatrixColorFilter(hueColorMatrix)); - // 创建一个新的图片并创建画布 - Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); - Canvas canvas = new Canvas(newBitmap); - // 将原图使用给定的画笔画到画布上 - canvas.drawBitmap(bitmap, 0, 0, paint); - return newBitmap; - } - - /** - * 亮度、色相、饱和度处理 - * @param bitmap 原图 - * @param lumValue 亮度值 - * @param hueValue 色相值 - * @param saturationValue 饱和度值 - * @return 亮度、色相、饱和度处理后的图片 - */ - public static Bitmap lumAndHueAndSaturation(Bitmap bitmap, int lumValue, int hueValue, int saturationValue) { - // 计算出符合要求的饱和度值 - float newSaturationValue = saturationValue * 1.0F / 127; - // 计算出符合要求的亮度值 - float newlumValue = lumValue * 1.0F / 127; - // 计算出符合要求的色相值 - float newHueValue = (hueValue - 127) * 1.0F / 127 * 180; - // 创建一个颜色矩阵并设置其饱和度 - ColorMatrix colorMatrix = new ColorMatrix(); - // 设置饱和度值 - colorMatrix.setSaturation(newSaturationValue); - // 设置亮度值 - colorMatrix.setScale(newlumValue, newlumValue, newlumValue, 1); - // 控制让红色区在色轮上旋转的角度 - colorMatrix.setRotate(0, newHueValue); - // 控制让绿红色区在色轮上旋转的角度 - colorMatrix.setRotate(1, newHueValue); - // 控制让蓝色区在色轮上旋转的角度 - colorMatrix.setRotate(2, newHueValue); - // 创建一个画笔并设置其颜色过滤器 - Paint paint = new Paint(); - paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); - // 创建一个新的图片并创建画布 - Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); - Canvas canvas = new Canvas(newBitmap); - // 将原图使用给定的画笔画到画布上 - canvas.drawBitmap(bitmap, 0, 0, paint); - return newBitmap; - } - - /** - * 怀旧效果处理 - * @param bitmap 原图 - * @return 怀旧效果处理后的图片 - */ - public static Bitmap nostalgic(Bitmap bitmap) { - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Config.RGB_565); - int pixColor = 0; - int pixR = 0; - int pixG = 0; - int pixB = 0; - int newR = 0; - int newG = 0; - int newB = 0; - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - for (int i = 0; i < height; i++) { - for (int k = 0; k < width; k++) { - pixColor = pixels[width * i + k]; - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - newR = (int) (0.393 * pixR + 0.769 * pixG + 0.189 * pixB); - newG = (int) (0.349 * pixR + 0.686 * pixG + 0.168 * pixB); - newB = (int) (0.272 * pixR + 0.534 * pixG + 0.131 * pixB); - int newColor = Color.argb(255, newR > 255 ? 255 : newR, - newG > 255 ? 255 : newG, newB > 255 ? 255 : newB); - pixels[width * i + k] = newColor; - } - } - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 柔化效果处理 - * @param bitmap 原图 - * @return 柔化效果处理后的图片 - */ - public static Bitmap soften(Bitmap bitmap) { - // 高斯矩阵 - int[] gauss = new int[]{1, 2, 1, 2, 4, 2, 1, 2, 1}; - - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - - int delta = 16; // 值越小图片会越亮,越大则越暗 - - int idx = 0; - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - idx = 0; - for (int m = -1; m <= 1; m++) { - for (int n = -1; n <= 1; n++) { - pixColor = pixels[(i + m) * width + k + n]; - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - newR = newR + (int) (pixR * gauss[idx]); - newG = newG + (int) (pixG * gauss[idx]); - newB = newB + (int) (pixB * gauss[idx]); - idx++; - } - } - newR /= delta; - newG /= delta; - newB /= delta; - - newR = Math.min(255, Math.max(0, newR)); - newG = Math.min(255, Math.max(0, newG)); - newB = Math.min(255, Math.max(0, newB)); - - pixels[i * width + k] = Color.argb(255, newR, newG, newB); - - newR = 0; - newG = 0; - newB = 0; - } - } - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 光照效果处理 - * @param bitmap 原图 - * @param centerX 光源在X轴的位置 - * @param centerY 光源在Y轴的位置 - * @return 光照效果处理后的图片 - */ - public static Bitmap sunshine(Bitmap bitmap, int centerX, int centerY) { - final int width = bitmap.getWidth(); - final int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - int radius = Math.min(centerX, centerY); - - final float strength = 150F; // 光照强度 100~150 - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - int pos = 0; - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - pos = i * width + k; - pixColor = pixels[pos]; - - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - newR = pixR; - newG = pixG; - newB = pixB; - - // 计算当前点到光照中心的距离,平面座标系中求两点之间的距离 - int distance = (int) (Math.pow((centerY - i), 2) + Math.pow(centerX - k, 2)); - if (distance < radius * radius) { - // 按照距离大小计算增加的光照值 - int result = (int) (strength * (1.0 - Math.sqrt(distance) / radius)); - newR = pixR + result; - newG = pixG + result; - newB = pixB + result; - } - - newR = Math.min(255, Math.max(0, newR)); - newG = Math.min(255, Math.max(0, newG)); - newB = Math.min(255, Math.max(0, newB)); - - pixels[pos] = Color.argb(255, newR, newG, newB); - } - } - - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 底片效果处理 - * @param bitmap 原图 - * @return 底片效果处理后的图片 - */ - public static Bitmap film(Bitmap bitmap) { - // RGBA的最大值 - final int MAX_VALUE = 255; - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - int pos = 0; - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - pos = i * width + k; - pixColor = pixels[pos]; - - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - newR = MAX_VALUE - pixR; - newG = MAX_VALUE - pixG; - newB = MAX_VALUE - pixB; - - newR = Math.min(MAX_VALUE, Math.max(0, newR)); - newG = Math.min(MAX_VALUE, Math.max(0, newG)); - newB = Math.min(MAX_VALUE, Math.max(0, newB)); - - pixels[pos] = Color.argb(MAX_VALUE, newR, newG, newB); - } - } - - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 锐化效果处理 - * @param bitmap 原图 - * @return 锐化效果处理后的图片 - */ - public static Bitmap sharpen(Bitmap bitmap) { - // 拉普拉斯矩阵 - int[] laplacian = new int[]{-1, -1, -1, -1, 9, -1, -1, -1, -1}; - - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - - int idx = 0; - float alpha = 0.3F; - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - idx = 0; - for (int m = -1; m <= 1; m++) { - for (int n = -1; n <= 1; n++) { - pixColor = pixels[(i + n) * width + k + m]; - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - newR = newR + (int) (pixR * laplacian[idx] * alpha); - newG = newG + (int) (pixG * laplacian[idx] * alpha); - newB = newB + (int) (pixB * laplacian[idx] * alpha); - idx++; - } - } - - newR = Math.min(255, Math.max(0, newR)); - newG = Math.min(255, Math.max(0, newG)); - newB = Math.min(255, Math.max(0, newB)); - - pixels[i * width + k] = Color.argb(255, newR, newG, newB); - newR = 0; - newG = 0; - newB = 0; - } - } - - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 浮雕效果处理 - * @param bitmap 原图 - * @return 浮雕效果处理后的图片 - */ - public static Bitmap emboss(Bitmap bitmap) { - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - int pos = 0; - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - pos = i * width + k; - pixColor = pixels[pos]; - - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - pixColor = pixels[pos + 1]; - newR = Color.red(pixColor) - pixR + 127; - newG = Color.green(pixColor) - pixG + 127; - newB = Color.blue(pixColor) - pixB + 127; - - newR = Math.min(255, Math.max(0, newR)); - newG = Math.min(255, Math.max(0, newG)); - newB = Math.min(255, Math.max(0, newB)); - - pixels[pos] = Color.argb(255, newR, newG, newB); - } - } - - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 将YUV格式的图片的源数据从横屏模式转为竖屏模式,注意:将源图片的宽高互换一下就是新图片的宽高 - * @param sourceData YUV格式的图片的源数据 - * @param width 源图片的宽 - * @param height 源图片的高 - * @return - */ - public static final byte[] yuvLandscapeToPortrait(byte[] sourceData, int width, int height) { - byte[] rotatedData = new byte[sourceData.length]; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) - rotatedData[x * height + height - y - 1] = sourceData[x + y * width]; - } - return rotatedData; - } - - /** - * 比较安全的 解码(decodeStream) 方法 - * rather than the one of {@link BitmapFactory} - * which will be easy to get OutOfMemory Exception - * while loading a big image file. - * @param uri 压缩相册返回的照片 - * @return - * @throws FileNotFoundException - */ - public static Bitmap safeDecodeStream(Uri uri, int targetWidth, int targetHeight) throws FileNotFoundException { - BitmapFactory.Options options = new BitmapFactory.Options(); - android.content.ContentResolver resolver = DevUtils.getContext().getContentResolver(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(new BufferedInputStream(resolver.openInputStream(uri), 16 * 1024), null, options); - // 获取图片的宽度、高度; - float imgWidth = options.outWidth; - float imgHeight = options.outHeight; - // 分别计算图片宽度、高度与目标宽度、高度的比例;取大于等于该比例的最小整数; - int widthRatio = (int) Math.ceil(imgWidth / (float) targetWidth); - int heightRatio = (int) Math.ceil(imgHeight / (float) targetHeight); - options.inSampleSize = 1; - if (widthRatio > 1 || widthRatio > 1) { - if (widthRatio > heightRatio) { - options.inSampleSize = widthRatio; - } else { - options.inSampleSize = heightRatio; - } - } - // 设置好缩放比例后,加载图片进内容; - options.inJustDecodeBounds = false; - return BitmapFactory.decodeStream(new BufferedInputStream(resolver.openInputStream(uri), 16 * 1024), null, options); - } -} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/BitmapUtils.java deleted file mode 100644 index 9cd1069885..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapUtils.java +++ /dev/null @@ -1,540 +0,0 @@ -package dev.utils.app.image; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.util.DisplayMetrics; -import android.view.View; -import android.view.ViewGroup.LayoutParams; -import android.widget.ImageView; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Field; - -import dev.utils.LogPrintUtils; -import dev.utils.app.AppUtils; -import dev.utils.app.ScreenUtils; - -/** - * detail: Bitmap工具类 - * Created by Ttt - */ -public final class BitmapUtils { - - private BitmapUtils() { - } - - // 日志Tag - private static final String TAG = BitmapUtils.class.getSimpleName(); - - /** - * 通过Resources获取Bitmap - * @param context - * @param resId - * @return - */ - public static Bitmap getBitmapFromResources(Context context, int resId) { - Resources res = context.getResources(); - return BitmapFactory.decodeResource(res, resId); - } - - /** - * 通过Resources获取Drawable - * @param context - * @param resId - * @return - */ - public static Drawable getDrawableFromResources(Context context, int resId) { - Resources res = context.getResources(); - return res.getDrawable(resId); - } - - /** - * 获取本地SDCard 图片 - * @param fPath 图片地址 - * @return - */ - public static Bitmap getSDCardBitmapStream(String fPath) { - try { - FileInputStream fis = new FileInputStream(new File(fPath));//文件输入流 - Bitmap bmp = BitmapFactory.decodeStream(fis); - return bmp; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getSDCardBitmapStream"); - } - return null; - } - - /** - * 获取本地SDCard 图片 - * @param fPath 图片地址 - * @return - */ - public static Bitmap getSDCardBitmapFile(String fPath) { - try { - Bitmap bmp = BitmapFactory.decodeFile(fPath); - return bmp; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getSDCardBitmapFile"); - } - return null; - } - - /** - * 获取Bitmap - * @param is - * @return - */ - public static Bitmap getBitmap(final InputStream is) { - if (is == null) return null; - return BitmapFactory.decodeStream(is); - } - - // ===== - - /** - * Bitmay 转换成byte数组 - * @param bitmap - * @return - */ - public static byte[] bitmapToByte(Bitmap bitmap) { - return bitmapToByte(bitmap, 100, Bitmap.CompressFormat.PNG); - } - - /** - * Bitmay 转换成byte数组 - * @param bitmap - * @param format - * @return - */ - public static byte[] bitmapToByte(Bitmap bitmap, Bitmap.CompressFormat format) { - return bitmapToByte(bitmap, 100, format); - } - - /** - * Bitmay 转换成byte数组 - * @param bitmap - * @param quality - * @param format - * @return - */ - public static byte[] bitmapToByte(Bitmap bitmap, int quality, Bitmap.CompressFormat format) { - if (bitmap == null) { - return null; - } - ByteArrayOutputStream o = new ByteArrayOutputStream(); - bitmap.compress(format, quality, o); - return o.toByteArray(); - } - - // = - - /** - * Drawable 转换成 byte数组 - * @param drawable - * @return - */ - public static byte[] drawableToByte(Drawable drawable) { - return bitmapToByte(drawableToBitmap(drawable)); - } - - /** - * Drawable 转换成 byte数组 - * @param drawable - * @param format - * @return - */ - public static byte[] drawableToByte(Drawable drawable, Bitmap.CompressFormat format) { - return bitmapToByte(drawableToBitmap(drawable), format); - } - - /** - * Drawable 转换成 byte数组 - * @param drawable - * @return - */ - public static byte[] drawableToByte2(Drawable drawable) { - return drawable == null ? null : bitmapToByte(drawable2Bitmap(drawable)); - } - - /** - * Drawable 转换成 byte数组 - * @param drawable - * @param format - * @return - */ - public static byte[] drawableToByte2(Drawable drawable, Bitmap.CompressFormat format) { - return drawable == null ? null : bitmapToByte(drawable2Bitmap(drawable), format); - } - - // == - - /** - * byte 数组转换为Bitmap - * @param bytes - * @return - */ - public static Bitmap byteToBitmap(byte[] bytes) { - return (bytes == null || bytes.length == 0) ? null : BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - } - - /** - * Drawable 转换成 Bitmap - * @param drawable - * @return - */ - public static Bitmap drawableToBitmap(Drawable drawable) { - return drawable == null ? null : ((BitmapDrawable) drawable).getBitmap(); - } - - /** - * Bitmap 转换成 Drawable - * @param bitmap - * @return - */ - public static Drawable bitmapToDrawable(Bitmap bitmap) { - return bitmap == null ? null : new BitmapDrawable(AppUtils.getResources(), bitmap); - } - - /** - * byte数组转换成Drawable - * @param bytes - * @return - */ - public static Drawable byteToDrawable(byte[] bytes) { - return bitmapToDrawable(byteToBitmap(bytes)); - } - - /** - * Drawable 转换 Bitmap - * @param drawable The drawable. - * @return bitmap - */ - public static Bitmap drawable2Bitmap(final Drawable drawable) { - if (drawable instanceof BitmapDrawable) { - BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; - if (bitmapDrawable.getBitmap() != null) { - return bitmapDrawable.getBitmap(); - } - } - Bitmap bitmap; - if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { - bitmap = Bitmap.createBitmap(1, 1, - drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); - } else { - bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), - drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); - } - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - return bitmap; - } - - // === - - /** - * 保存图片到SD卡 - JPEG - * @param bitmap 需要保存的数据 - * @param path 保存路径 - * @param quality 压缩比例 - */ - public static boolean saveBitmapToSDCardJPEG(Bitmap bitmap, String path, int quality) { - return saveBitmapToSDCard(bitmap, path, Bitmap.CompressFormat.JPEG, quality); - } - - /** - * 保存图片到SD卡 - PNG - * @param bitmap 需要保存的数据 - * @param path 保存路径 - */ - public static boolean saveBitmapToSDCardPNG(Bitmap bitmap, String path) { - return saveBitmapToSDCard(bitmap, path, Bitmap.CompressFormat.PNG, 80); - } - - /** - * 保存图片到SD卡 - PNG - * @param bitmap 需要保存的数据 - * @param path 保存路径 - * @param quality 压缩比例 - */ - public static boolean saveBitmapToSDCardPNG(Bitmap bitmap, String path, int quality) { - return saveBitmapToSDCard(bitmap, path, Bitmap.CompressFormat.PNG, quality); - } - - /** - * 保存图片到SD卡 - PNG - * @param bitmap 需要保存的数据 - * @param path 保存路径 - * @param quality 压缩比例 - * @return - */ - public static boolean saveBitmapToSDCard(Bitmap bitmap, String path, int quality) { - return saveBitmapToSDCard(bitmap, path, Bitmap.CompressFormat.PNG, quality); - } - - /** - * 保存图片到SD卡 - * @param bitmap 图片资源 - * @param path 保存路径 - * @param format 如 Bitmap.CompressFormat.PNG - * @param quality 保存的图片质量, 100 则完整质量不压缩保存 - * @return 保存结果 - */ - public static boolean saveBitmapToSDCard(Bitmap bitmap, String path, Bitmap.CompressFormat format, int quality) { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(path); - if (fos != null) { - bitmap.compress(format, quality, fos); - fos.close(); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "saveBitmapToSDCard"); - return false; - } finally { - if(fos != null) { - try { - fos.close(); - } catch (IOException e) { - } - } - } - return true; - } - - // == - - /** - * 将Drawable转化为Bitmap - * @param drawable Drawable - * @return Bitmap - */ - public static Bitmap getBitmapFromDrawable(Drawable drawable) { - try { - int width = drawable.getIntrinsicWidth(); - int height = drawable.getIntrinsicHeight(); - Bitmap bitmap = Bitmap.createBitmap(width, height, drawable .getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, width, height); - drawable.draw(canvas); - return bitmap; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getBitmapFromDrawable"); - } - return null; - } - - /** - * 通过View, 获取背景转换Bitmap - * @param view The view. - * @return bitmap - */ - public static Bitmap bitmapToViewBackGround(View view) { - if (view == null) return null; - try { - Bitmap ret = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(ret); - Drawable bgDrawable = view.getBackground(); - if (bgDrawable != null) { - bgDrawable.draw(canvas); - } else { - canvas.drawColor(Color.WHITE); - } - view.draw(canvas); - return ret; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "bitmapToViewBackGround"); - } - return null; - } - - /** - * 通过View, 获取Bitmap -> 绘制整个View - * @param view View - * @return Bitmap - */ - public static Bitmap getBitmapFromView(View view) { - if (view == null) return null; - try { - Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - view.layout(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); - view.draw(canvas); - return bitmap; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getBitmapFromView"); - } - return null; - } - - /** - * 把一个View的对象转换成bitmap - * @param view View - * @return Bitmap - */ - public static Bitmap getBitmapFromView2(View view) { - if (view == null) return null; - try { - view.clearFocus(); - view.setPressed(false); - // 能画缓存就返回false - boolean willNotCache = view.willNotCacheDrawing(); - view.setWillNotCacheDrawing(false); - int color = view.getDrawingCacheBackgroundColor(); - view.setDrawingCacheBackgroundColor(0); - if (color != 0) { - view.destroyDrawingCache(); - } - view.buildDrawingCache(); - Bitmap cacheBitmap = view.getDrawingCache(); - if (cacheBitmap == null) { - return null; - } - Bitmap bitmap = Bitmap.createBitmap(cacheBitmap); - // Restore the view - view.destroyDrawingCache(); - view.setWillNotCacheDrawing(willNotCache); - view.setDrawingCacheBackgroundColor(color); - return bitmap; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "getBitmapFromView2"); - } - return null; - } - - // === - - /** - * 计算视频宽高大小,视频比例xxx*xxx按屏幕比例放大或者缩小 - * @param width 高度比例 - * @param height 宽度比例 - * @return 返回宽高 0 = 宽,1 = 高 - */ - public static int[] reckonVideoWidthHeight(float width, float height) { - try { - // 获取屏幕宽度 - int sWidth = ScreenUtils.getScreenWidth(); - // 判断宽度比例 - float wRatio = 0.0f; - // 计算比例 - wRatio = (sWidth - width) / width; - // 等比缩放 - int nWidth = sWidth; - int nHeight = (int) (height * (wRatio + 1)); - return new int []{ nWidth, nHeight }; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "reckonVideoWidthHeight"); - } - return null; - } - - /** - * 根据需求的宽和高以及图片实际的宽和高计算SampleSize - * @param options - * @param reqWidth - * @param reqHeight - * @return - */ - public static int caculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { - int width = options.outWidth; - int height = options.outHeight; - - int inSampleSize = 1; - if (width > reqWidth || height > reqHeight) { - int widthRadio = Math.round(width * 1.0f / reqWidth); - int heightRadio = Math.round(height * 1.0f / reqHeight); - inSampleSize = Math.max(widthRadio, heightRadio); - } - return inSampleSize; - } - - // ================ ImageView 相关 ================== - - /** - * 根据ImageView获适当的压缩的宽和高 - * @param imageView - * @return - */ - public static int[] getImageViewSize(ImageView imageView) { - int[] imgSize = new int[] {0, 0}; - // -- - DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics(); - LayoutParams lp = imageView.getLayoutParams(); - // 获取imageview的实际宽度 - int width = imageView.getWidth(); - if (width <= 0) { - width = lp.width; // 获取imageview在layout中声明的宽度 - } - if (width <= 0) { - // width = imageView.getMaxWidth(); // 检查最大值 - width = getImageViewFieldValue(imageView, "mMaxWidth"); - } - if (width <= 0) { - width = displayMetrics.widthPixels; - } - // -- - // 获取imageview的实际高度 - int height = imageView.getHeight(); - if (height <= 0) { - height = lp.height; // 获取imageview在layout中声明的宽度 - } - if (height <= 0) { - height = getImageViewFieldValue(imageView, "mMaxHeight"); // 检查最大值 - } - if (height <= 0) { - height = displayMetrics.heightPixels; - } - // -- - imgSize[0] = width; - imgSize[1] = height; - return imgSize; - } - - /** - * 通过反射获取imageview的某个属性值 - * @param object - * @param fieldName - * @return - */ - private static int getImageViewFieldValue(Object object, String fieldName) { - int value = 0; - try { - Field field = ImageView.class.getDeclaredField(fieldName); - field.setAccessible(true); - int fieldValue = field.getInt(object); - if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) { - value = fieldValue; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getImageViewFieldValue"); - } - return value; - } - - /** - * 获取图片宽度高度(不加载解析图片) - * @param path - * @return - */ - public static int[] getImageWidthHeight(String path){ - BitmapFactory.Options options = new BitmapFactory.Options(); - // 不解析图片信息 - options.inJustDecodeBounds = true; - // 此时返回的bitmap为null - Bitmap bitmap = BitmapFactory.decodeFile(path, options); - // options.outHeight为原始图片的高 - return new int[]{options.outWidth,options.outHeight}; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/FastBlurUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/FastBlurUtils.java deleted file mode 100644 index 3925a69905..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/image/FastBlurUtils.java +++ /dev/null @@ -1,259 +0,0 @@ -package dev.utils.app.image; - -import android.graphics.Bitmap; -import android.media.ThumbnailUtils; - -/** - * detail: 毛玻璃效果 - * Created by Ttt - */ -public final class FastBlurUtils { - - private FastBlurUtils() { - } - - /** - * 对图片进行毛玻璃化 - * @param sentBitmap 位图 - * @param radius 虚化程度 - * @param canReuseInBitmap 是否重用 - * @return 位图 - */ - public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { - Bitmap bitmap; - if (canReuseInBitmap) { - bitmap = sentBitmap; - } else { - bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); - } - - if (radius < 1) { - return (null); - } - - int w = bitmap.getWidth(); - int h = bitmap.getHeight(); - - int[] pix = new int[w * h]; - bitmap.getPixels(pix, 0, w, 0, 0, w, h); - - int wm = w - 1; - int hm = h - 1; - int wh = w * h; - int div = radius + radius + 1; - - int r[] = new int[wh]; - int g[] = new int[wh]; - int b[] = new int[wh]; - int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; - int vmin[] = new int[Math.max(w, h)]; - - int divsum = (div + 1) >> 1; - divsum *= divsum; - int dv[] = new int[256 * divsum]; - for (i = 0; i < 256 * divsum; i++) { - dv[i] = (i / divsum); - } - - yw = yi = 0; - - int[][] stack = new int[div][3]; - int stackpointer; - int stackstart; - int[] sir; - int rbs; - int r1 = radius + 1; - int routsum, goutsum, boutsum; - int rinsum, ginsum, binsum; - - for (y = 0; y < h; y++) { - rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; - for (i = -radius; i <= radius; i++) { - p = pix[yi + Math.min(wm, Math.max(i, 0))]; - sir = stack[i + radius]; - sir[0] = (p & 0xff0000) >> 16; - sir[1] = (p & 0x00ff00) >> 8; - sir[2] = (p & 0x0000ff); - rbs = r1 - Math.abs(i); - rsum += sir[0] * rbs; - gsum += sir[1] * rbs; - bsum += sir[2] * rbs; - if (i > 0) { - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - } else { - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - } - } - stackpointer = radius; - - for (x = 0; x < w; x++) { - - r[yi] = dv[rsum]; - g[yi] = dv[gsum]; - b[yi] = dv[bsum]; - - rsum -= routsum; - gsum -= goutsum; - bsum -= boutsum; - - stackstart = stackpointer - radius + div; - sir = stack[stackstart % div]; - - routsum -= sir[0]; - goutsum -= sir[1]; - boutsum -= sir[2]; - - if (y == 0) { - vmin[x] = Math.min(x + radius + 1, wm); - } - p = pix[yw + vmin[x]]; - - sir[0] = (p & 0xff0000) >> 16; - sir[1] = (p & 0x00ff00) >> 8; - sir[2] = (p & 0x0000ff); - - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - - rsum += rinsum; - gsum += ginsum; - bsum += binsum; - - stackpointer = (stackpointer + 1) % div; - sir = stack[(stackpointer) % div]; - - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - - rinsum -= sir[0]; - ginsum -= sir[1]; - binsum -= sir[2]; - - yi++; - } - yw += w; - } - for (x = 0; x < w; x++) { - rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; - yp = -radius * w; - for (i = -radius; i <= radius; i++) { - yi = Math.max(0, yp) + x; - - sir = stack[i + radius]; - - sir[0] = r[yi]; - sir[1] = g[yi]; - sir[2] = b[yi]; - - rbs = r1 - Math.abs(i); - - rsum += r[yi] * rbs; - gsum += g[yi] * rbs; - bsum += b[yi] * rbs; - - if (i > 0) { - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - } else { - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - } - - if (i < hm) { - yp += w; - } - } - yi = x; - stackpointer = radius; - for (y = 0; y < h; y++) { - // Preserve alpha channel: ( 0xff000000 & pix[yi] ) - pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; - - rsum -= routsum; - gsum -= goutsum; - bsum -= boutsum; - - stackstart = stackpointer - radius + div; - sir = stack[stackstart % div]; - - routsum -= sir[0]; - goutsum -= sir[1]; - boutsum -= sir[2]; - - if (x == 0) { - vmin[y] = Math.min(y + r1, hm) * w; - } - p = x + vmin[y]; - - sir[0] = r[p]; - sir[1] = g[p]; - sir[2] = b[p]; - - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - - rsum += rinsum; - gsum += ginsum; - bsum += binsum; - - stackpointer = (stackpointer + 1) % div; - sir = stack[stackpointer]; - - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - - rinsum -= sir[0]; - ginsum -= sir[1]; - binsum -= sir[2]; - - yi += w; - } - } - - bitmap.setPixels(pix, 0, w, 0, 0, w, h); - return (bitmap); - } - - /** - * 对图片进行毛玻璃化 数值越大效果越明显 - * @param originBitmap 位图 - * @param scaleRatio 缩放比率 - * @param blurRadius 毛玻璃化比率,虚化程度 - * @return 位图 - */ - public static Bitmap doBlur(Bitmap originBitmap, int scaleRatio, int blurRadius){ - Bitmap scaledBitmap = Bitmap.createScaledBitmap(originBitmap, - originBitmap.getWidth() / scaleRatio, - originBitmap.getHeight() / scaleRatio, - false); - Bitmap blurBitmap = doBlur(scaledBitmap, blurRadius, false); - scaledBitmap.recycle(); - return blurBitmap; - } - - - /** - * 对图片进行 毛玻璃化,虚化 数值越大效果越明显 - * @param originBitmap 位图 - * @param width 缩放后的期望宽度 - * @param height 缩放后的期望高度 - * @param blurRadius 虚化程度 - * @return 位图 - */ - public static Bitmap doBlur(Bitmap originBitmap, int width, int height, int blurRadius){ - Bitmap thumbnail = ThumbnailUtils.extractThumbnail(originBitmap, width, height); - Bitmap blurBitmap = doBlur(thumbnail, blurRadius, true); - thumbnail.recycle(); - return blurBitmap; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/ImageBmpUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/ImageBmpUtils.java deleted file mode 100644 index f499c3effb..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/image/ImageBmpUtils.java +++ /dev/null @@ -1,205 +0,0 @@ -package dev.utils.app.image; - -import android.graphics.Bitmap; - -import java.io.File; -import java.io.FileOutputStream; - -import dev.utils.LogPrintUtils; - -/** - * detail: 图片Buf转换 - 转换BMP图片 - * Created by Ttt - */ -public final class ImageBmpUtils { - - private ImageBmpUtils(){ - } - - // 日志Tag - private static final String TAG = ImageBmpUtils.class.getSimpleName(); - - // ===================== 转换BMP图片 ======================= - - /** - * bmp位图,头结构 - * @param size 文件总大小(字节数) - * @return - */ - private static byte[] addBMPImageHeader(int size) { - byte[] buffer = new byte[14]; - // -- 文件标识 BM -- - buffer[0] = 0x42; - buffer[1] = 0x4D; - // -- 位图文件大小 -- - buffer[2] = (byte) (size >> 0); - buffer[3] = (byte) (size >> 8); - buffer[4] = (byte) (size >> 16); - buffer[5] = (byte) (size >> 24); - // -- 位图文件保留字,必须为0 -- - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - // -- 位图数据开始之间的偏移量 -- - buffer[10] = 0x36; - buffer[11] = 0x00; - buffer[12] = 0x00; - buffer[13] = 0x00; - return buffer; - } - - /** - * bmp位图,头信息 - * @param w 宽度 - * @param h 高度 - * @return - */ - private static byte[] addBMPImageInfosHeader(int w, int h) { - byte[] buffer = new byte[40]; - // -- - buffer[0] = 0x28; - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - // -- - buffer[4] = (byte) (w >> 0); - buffer[5] = (byte) (w >> 8); - buffer[6] = (byte) (w >> 16); - buffer[7] = (byte) (w >> 24); - // -- - buffer[8] = (byte) (h >> 0); - buffer[9] = (byte) (h >> 8); - buffer[10] = (byte) (h >> 16); - buffer[11] = (byte) (h >> 24); - // -- - buffer[12] = 0x01; - buffer[13] = 0x00; - // -- - buffer[14] = 0x20; // 位数 0x20 - 32位 - buffer[15] = 0x00; - // -- - buffer[16] = 0x00; - buffer[17] = 0x00; - buffer[18] = 0x00; - buffer[19] = 0x00; - // -- - buffer[20] = 0x00; - buffer[21] = 0x00; - buffer[22] = 0x00; - buffer[23] = 0x00; - // -- - buffer[24] = (byte) 0xE0; - buffer[25] = 0x01; - buffer[26] = 0x00; - buffer[27] = 0x00; - // -- - buffer[28] = 0x02; - buffer[29] = 0x03; - buffer[30] = 0x00; - buffer[31] = 0x00; - // -- - buffer[32] = 0x00; - buffer[33] = 0x00; - buffer[34] = 0x00; - buffer[35] = 0x00; - // -- - buffer[36] = 0x00; - buffer[37] = 0x00; - buffer[38] = 0x00; - buffer[39] = 0x00; - return buffer; - } - - /** - * 增加位图 ARGB值 - * @param b 数据 - * @param w 宽度 - * @param h 高度 - * @return - */ - private static byte[] addBMP_ARGB_8888(int[] b, int w, int h) { - int len = b.length; - byte[] buffer = new byte[w * h * 4]; // A + R + G + B = 4 - int offset = 0; // 计算偏移量 - for (int i = len - 1; i >= 0; i -= w) { - // DIB文件格式最后一行为第一行,每行按从左到右顺序 - int end = i, start = i - w + 1; - for (int j = start; j <= end; j++) { - buffer[offset] = (byte) (b[j] >> 0); - buffer[offset + 1] = (byte) (b[j] >> 8); - buffer[offset + 2] = (byte) (b[j] >> 16); - buffer[offset + 3] = (byte) (b[j] >> 24); - offset += 4; - } - } - return buffer; - } - - /** - * 获取bmp位图 byte数据 - * @param bitmap - * @return - */ - private static byte[] getBmpBytes(Bitmap bitmap){ - try { - if(bitmap != null){ - int w = bitmap.getWidth(), h = bitmap.getHeight(); - int[] pixels = new int[w * h]; - bitmap.getPixels(pixels, 0, w, 0, 0, w, h); - // -- - byte[] rgb = addBMP_ARGB_8888(pixels, w, h); - byte[] header = addBMPImageHeader(rgb.length); - byte[] infos = addBMPImageInfosHeader(w, h); - // -- - byte[] buffer = new byte[54 + rgb.length]; - // -- - System.arraycopy(header, 0, buffer, 0, header.length); - System.arraycopy(infos, 0, buffer, 14, infos.length); - System.arraycopy(rgb, 0, buffer, 54, rgb.length); - // -- - return buffer; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getBmpBytes"); - } - return null; - } - - // ================================================================= - - /** - * 保存Bmp图片 - * @param path 保存路径 - * @param bitmap 图片信息 - * @return - */ - public static boolean saveBmpImg(String path, Bitmap bitmap){ - FileOutputStream fos = null; - try { - // 转换Bmp byte数据 - byte[] buffer = getBmpBytes(bitmap); - // 创建写入流 - fos = new FileOutputStream(new File(path)); - // 写入数据 - fos.write(buffer); - return true; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "saveBmpImg"); - } finally { // 最终关闭写入流 - try { - if(fos != null){ - fos.flush(); - } - } catch (Exception e) { - } - try { - if(fos != null){ - fos.close(); - } - } catch (Exception e) { - } - } - return false; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/ImageProcessor.java b/DevLibUtils/src/main/java/dev/utils/app/image/ImageProcessor.java deleted file mode 100644 index 4f747e46df..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/image/ImageProcessor.java +++ /dev/null @@ -1,704 +0,0 @@ -package dev.utils.app.image; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.LinearGradient; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Shader; - -import java.io.InputStream; - -/** - * detail: 图片处理器 - * Created by Ttt - */ -public class ImageProcessor { - - // 需要处理的Bitmap - private Bitmap bitmap; - - /** - * 构造方法 - * @param bitmap 需要处理的bitmap - */ - public ImageProcessor(Bitmap bitmap) { - this.bitmap = bitmap; - } - - /** - * 缩放处理 - * @param scaling 缩放比例 - * @return 缩放后的图片 - */ - public Bitmap scale(float scaling) { - Matrix matrix = new Matrix(); - matrix.postScale(scaling, scaling); - return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); - } - - /** - * 缩放处理 - * @param newWidth 新的宽度 - * @return Bitmap - */ - public Bitmap scaleByWidth(int newWidth) { - return scale((float) newWidth / bitmap.getWidth()); - } - - /** - * 缩放处理 - * @param newHeight 新的高度 - * @return Bitmap - */ - public Bitmap scaleByHeight(int newHeight) { - return scale((float) newHeight / bitmap.getHeight()); - } - - /** - * 水平翻转处理 - * @param bitmap 原图 - * @return 水平翻转后的图片 - */ - public Bitmap reverseByHorizontal(Bitmap bitmap){ - Matrix matrix = new Matrix(); - matrix.preScale(-1, 1); - return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); - } - - /** - * 垂直翻转处理 - * @param bitmap 原图 - * @return 垂直翻转后的图片 - */ - public Bitmap reverseByVertical(Bitmap bitmap){ - Matrix matrix = new Matrix(); - matrix.preScale(1, -1); - return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); - } - - /** - * 将给定资源ID的Drawable转换成Bitmap - * @param context 上下文 - * @param resId Drawable资源文件的ID - * @return 新的Bitmap - */ - public Bitmap drawableToBitmap(Context context, int resId) { - BitmapFactory.Options opt = new BitmapFactory.Options(); - opt.inPreferredConfig = Bitmap.Config.RGB_565; - opt.inPurgeable = true; - opt.inInputShareable = true; - InputStream is = context.getResources().openRawResource(resId); - return BitmapFactory.decodeStream(is, null, opt); - } - - /** - * 圆角处理 - * @param pixels 角度,度数越大圆角越大 - * @return 转换成圆角后的图片 - */ - public Bitmap roundCorner(float pixels) { - Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(output); - Paint paint = new Paint(); - Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); // 创建一个同原图一样大小的矩形,用于把原图绘制到这个矩形上 - RectF rectF = new RectF(rect); // 创建一个精度更高的矩形,用于画出圆角效果 - paint.setAntiAlias(true); - canvas.drawARGB(0, 0, 0, 0); // 涂上黑色全透明的底色 - paint.setColor(0xff424242); // 设置画笔的颜色为不透明的灰色 - canvas.drawRoundRect(rectF, pixels, pixels, paint); // 用给给定的画笔把给定的矩形以给定的圆角的度数画到画布 - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); - canvas.drawBitmap(bitmap, rect, rect, paint); // 用画笔paint将原图bitmap根据新的矩形重新绘制 - return output; - } - - /** - * 倒影处理 - * @param reflectionSpacing 原图与倒影之间的间距 - * @return 加上倒影后的图片 - */ - public Bitmap reflection(int reflectionSpacing, int reflectionHeight) { - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - - /* 获取倒影图片,并创建一张宽度与原图相同,但高度等于原图的高度加上间距加上倒影的高度的图片,并创建画布。画布分为上中下三部分,上:是原图;中:是原图与倒影的间距;下:是倒影 */ - Bitmap reflectionImage = reverseByVertical(bitmap);// - Bitmap bitmapWithReflection = Bitmap.createBitmap(width, height + reflectionSpacing + reflectionHeight, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmapWithReflection); - - /* 将原图画到画布的上半部分,将倒影画到画布的下半部分,倒影与画布顶部的间距是原图的高度加上原图与倒影之间的间距 */ - canvas.drawBitmap(bitmap, 0, 0, null); - canvas.drawBitmap(reflectionImage, 0, height + reflectionSpacing, null); - reflectionImage.recycle(); - - /* 将倒影改成半透明,创建画笔,并设置画笔的渐变从半透明的白色到全透明的白色,然后再倒影上面画半透明效果 */ - Paint paint = new Paint(); - paint.setShader(new LinearGradient(0, bitmap.getHeight(), 0, bitmapWithReflection.getHeight() + reflectionSpacing, 0x70ffffff, 0x00ffffff, Shader.TileMode.CLAMP)); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); - canvas.drawRect(0, height+reflectionSpacing, width, bitmapWithReflection.getHeight() + reflectionSpacing, paint); - - return bitmapWithReflection; - } - - /** - * 倒影处理 - * @return 加上倒影后的图片 - */ - public Bitmap reflection() { - return reflection(4, bitmap.getHeight() / 2); - } - - /** - * 旋转处理 - * @param angle 旋转角度 - * @param px 旋转中心点在X轴的坐标 - * @param py 旋转中心点在Y轴的坐标 - * @return 旋转后的图片 - */ - public Bitmap rotate(float angle, float px, float py){ - Matrix matrix = new Matrix(); - matrix.postRotate(angle, px, py); - return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); - } - - /** - * 旋转后处理 - * @param angle 旋转角度 - * @return 旋转后的图片 - */ - public Bitmap rotate(float angle){ - Matrix matrix = new Matrix(); - matrix.postRotate(angle); - return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); - } - - /** - * 饱和度处理 - * @param saturationValue 新的饱和度值 - * @return 改变了饱和度值之后的图片 - */ - public Bitmap saturation(int saturationValue){ - //计算出符合要求的饱和度值 - float newSaturationValue = saturationValue * 1.0F / 127; - //创建一个颜色矩阵 - ColorMatrix saturationColorMatrix = new ColorMatrix(); - //设置饱和度值 - saturationColorMatrix.setSaturation(newSaturationValue); - //创建一个画笔并设置其颜色过滤器 - Paint paint = new Paint(); - paint.setColorFilter(new ColorMatrixColorFilter(saturationColorMatrix)); - //创建一个新的图片并创建画布 - Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(newBitmap); - //将原图使用给定的画笔画到画布上 - canvas.drawBitmap(bitmap, 0, 0, paint); - return newBitmap; - } - - /** - * 亮度处理 - * @param lumValue 新的亮度值 - * @return 改变了亮度值之后的图片 - */ - public Bitmap lum(int lumValue){ - //计算出符合要求的亮度值 - float newlumValue = lumValue * 1.0F / 127; - //创建一个颜色矩阵 - ColorMatrix lumColorMatrix = new ColorMatrix(); - //设置亮度值 - lumColorMatrix.setScale(newlumValue, newlumValue, newlumValue, 1); - //创建一个画笔并设置其颜色过滤器 - Paint paint = new Paint(); - paint.setColorFilter(new ColorMatrixColorFilter(lumColorMatrix)); - //创建一个新的图片并创建画布 - Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(newBitmap); - //将原图使用给定的画笔画到画布上 - canvas.drawBitmap(bitmap, 0, 0, paint); - return newBitmap; - } - - /** - * 色相处理 - * @param hueValue 新的色相值 - * @return 改变了色相值之后的图片 - */ - public Bitmap hue(int hueValue){ - //计算出符合要求的色相值 - float newHueValue = (hueValue - 127) * 1.0F / 127 * 180; - //创建一个颜色矩阵 - ColorMatrix hueColorMatrix = new ColorMatrix(); - // 控制让红色区在色轮上旋转的角度 - hueColorMatrix.setRotate(0, newHueValue); - // 控制让绿红色区在色轮上旋转的角度 - hueColorMatrix.setRotate(1, newHueValue); - // 控制让蓝色区在色轮上旋转的角度 - hueColorMatrix.setRotate(2, newHueValue); - //创建一个画笔并设置其颜色过滤器 - Paint paint = new Paint(); - paint.setColorFilter(new ColorMatrixColorFilter(hueColorMatrix)); - //创建一个新的图片并创建画布 - Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(newBitmap); - //将原图使用给定的画笔画到画布上 - canvas.drawBitmap(bitmap, 0, 0, paint); - return newBitmap; - } - - /** - * 亮度、色相、饱和度处理 - * @param lumValue 亮度值 - * @param hueValue 色相值 - * @param saturationValue 饱和度值 - * @return 亮度、色相、饱和度处理后的图片 - */ - public Bitmap lumAndHueAndSaturation(int lumValue, int hueValue, int saturationValue){ - //计算出符合要求的饱和度值 - float newSaturationValue = saturationValue * 1.0F / 127; - //计算出符合要求的亮度值 - float newlumValue = lumValue * 1.0F / 127; - //计算出符合要求的色相值 - float newHueValue = (hueValue - 127) * 1.0F / 127 * 180; - - //创建一个颜色矩阵并设置其饱和度 - ColorMatrix colorMatrix = new ColorMatrix(); - - //设置饱和度值 - colorMatrix.setSaturation(newSaturationValue); - //设置亮度值 - colorMatrix.setScale(newlumValue, newlumValue, newlumValue, 1); - // 控制让红色区在色轮上旋转的角度 - colorMatrix.setRotate(0, newHueValue); - // 控制让绿红色区在色轮上旋转的角度 - colorMatrix.setRotate(1, newHueValue); - // 控制让蓝色区在色轮上旋转的角度 - colorMatrix.setRotate(2, newHueValue); - - //创建一个画笔并设置其颜色过滤器 - Paint paint = new Paint(); - paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); - //创建一个新的图片并创建画布 - Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(newBitmap); - //将原图使用给定的画笔画到画布上 - canvas.drawBitmap(bitmap, 0, 0, paint); - return newBitmap; - } - - /** - * 怀旧效果处理 - * @param bitmap 原图 - * @return 怀旧效果处理后的图片 - */ - public Bitmap nostalgic(Bitmap bitmap) { - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - int pixColor = 0; - int pixR = 0; - int pixG = 0; - int pixB = 0; - int newR = 0; - int newG = 0; - int newB = 0; - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - for (int i = 0; i < height; i++) { - for (int k = 0; k < width; k++) { - pixColor = pixels[width * i + k]; - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - newR = (int) (0.393 * pixR + 0.769 * pixG + 0.189 * pixB); - newG = (int) (0.349 * pixR + 0.686 * pixG + 0.168 * pixB); - newB = (int) (0.272 * pixR + 0.534 * pixG + 0.131 * pixB); - int newColor = Color.argb(255, newR > 255 ? 255 : newR, newG > 255 ? 255 : newG, newB > 255 ? 255 - : newB); - pixels[width * i + k] = newColor; - } - } - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 模糊效果处理 - * @return 模糊效果处理后的图片 - */ - public Bitmap blur() { - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - - int newColor = 0; - - int[][] colors = new int[9][3]; - for (int i = 1, length = width - 1; i < length; i++) { - for (int k = 1, len = height - 1; k < len; k++) { - for (int m = 0; m < 9; m++) { - int s = 0; - int p = 0; - switch (m) { - case 0: - s = i - 1; - p = k - 1; - break; - case 1: - s = i; - p = k - 1; - break; - case 2: - s = i + 1; - p = k - 1; - break; - case 3: - s = i + 1; - p = k; - break; - case 4: - s = i + 1; - p = k + 1; - break; - case 5: - s = i; - p = k + 1; - break; - case 6: - s = i - 1; - p = k + 1; - break; - case 7: - s = i - 1; - p = k; - break; - case 8: - s = i; - p = k; - } - pixColor = bitmap.getPixel(s, p); - colors[m][0] = Color.red(pixColor); - colors[m][1] = Color.green(pixColor); - colors[m][2] = Color.blue(pixColor); - } - - for (int m = 0; m < 9; m++) { - newR += colors[m][0]; - newG += colors[m][1]; - newB += colors[m][2]; - } - - newR = (int) (newR / 9F); - newG = (int) (newG / 9F); - newB = (int) (newB / 9F); - - newR = Math.min(255, Math.max(0, newR)); - newG = Math.min(255, Math.max(0, newG)); - newB = Math.min(255, Math.max(0, newB)); - - newColor = Color.argb(255, newR, newG, newB); - newBitmap.setPixel(i, k, newColor); - - newR = 0; - newG = 0; - newB = 0; - } - } - return newBitmap; - } - - /** - * 柔化效果处理 - * @return 柔化效果处理后的图片 - */ - public Bitmap soften() { - // 高斯矩阵 - int[] gauss = new int[] { 1, 2, 1, 2, 4, 2, 1, 2, 1 }; - - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - - int delta = 16; // 值越小图片会越亮,越大则越暗 - - int idx = 0; - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - idx = 0; - for (int m = -1; m <= 1; m++) { - for (int n = -1; n <= 1; n++) { - pixColor = pixels[(i + m) * width + k + n]; - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - newR = newR + (int) (pixR * gauss[idx]); - newG = newG + (int) (pixG * gauss[idx]); - newB = newB + (int) (pixB * gauss[idx]); - idx++; - } - } - - newR /= delta; - newG /= delta; - newB /= delta; - - newR = Math.min(255, Math.max(0, newR)); - newG = Math.min(255, Math.max(0, newG)); - newB = Math.min(255, Math.max(0, newB)); - - pixels[i * width + k] = Color.argb(255, newR, newG, newB); - - newR = 0; - newG = 0; - newB = 0; - } - } - - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 光照效果处理 - * @param centerX 光源在X轴的位置 - * @param centerY 光源在Y轴的位置 - * @return 光照效果处理后的图片 - */ - public Bitmap sunshine(int centerX, int centerY) { - final int width = bitmap.getWidth(); - final int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - int radius = Math.min(centerX, centerY); - - final float strength = 150F; // 光照强度 100~150 - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - int pos = 0; - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - pos = i * width + k; - pixColor = pixels[pos]; - - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - newR = pixR; - newG = pixG; - newB = pixB; - - // 计算当前点到光照中心的距离,平面座标系中求两点之间的距离 - int distance = (int) ( - Math.pow((centerY - i), 2) + Math.pow(centerX - k, 2)); - if (distance < radius * radius) { - // 按照距离大小计算增加的光照值 - int result = (int) (strength * (1.0 - Math.sqrt(distance) / radius)); - newR = pixR + result; - newG = pixG + result; - newB = pixB + result; - } - - newR = Math.min(255, Math.max(0, newR)); - newG = Math.min(255, Math.max(0, newG)); - newB = Math.min(255, Math.max(0, newB)); - - pixels[pos] = Color.argb(255, newR, newG, newB); - } - } - - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 底片效果处理 - * @return 底片效果处理后的图片 - */ - public Bitmap film() { - // RGBA的最大值 - final int MAX_VALUE = 255; - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - int pos = 0; - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - pos = i * width + k; - pixColor = pixels[pos]; - - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - newR = MAX_VALUE - pixR; - newG = MAX_VALUE - pixG; - newB = MAX_VALUE - pixB; - - newR = Math.min(MAX_VALUE, Math.max(0, newR)); - newG = Math.min(MAX_VALUE, Math.max(0, newG)); - newB = Math.min(MAX_VALUE, Math.max(0, newB)); - - pixels[pos] = Color.argb(MAX_VALUE, newR, newG, newB); - } - } - - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 锐化效果处理 - * @return 锐化效果处理后的图片 - */ - public Bitmap sharpen() { - // 拉普拉斯矩阵 - int[] laplacian = new int[] { -1, -1, -1, -1, 9, -1, -1, -1, -1 }; - - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - - int idx = 0; - float alpha = 0.3F; - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - idx = 0; - for (int m = -1; m <= 1; m++) { - for (int n = -1; n <= 1; n++) { - pixColor = pixels[(i + n) * width + k + m]; - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - newR = newR + (int) (pixR * laplacian[idx] * alpha); - newG = newG + (int) (pixG * laplacian[idx] * alpha); - newB = newB + (int) (pixB * laplacian[idx] * alpha); - idx++; - } - } - - newR = Math.min(255, Math.max(0, newR)); - newG = Math.min(255, Math.max(0, newG)); - newB = Math.min(255, Math.max(0, newB)); - - pixels[i * width + k] = Color.argb(255, newR, newG, newB); - newR = 0; - newG = 0; - newB = 0; - } - } - - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } - - /** - * 浮雕效果处理 - * @return 浮雕效果处理后的图片 - */ - public Bitmap emboss() { - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - - int pixR = 0; - int pixG = 0; - int pixB = 0; - - int pixColor = 0; - - int newR = 0; - int newG = 0; - int newB = 0; - - int[] pixels = new int[width * height]; - bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - int pos = 0; - for (int i = 1, length = height - 1; i < length; i++) { - for (int k = 1, len = width - 1; k < len; k++) { - pos = i * width + k; - pixColor = pixels[pos]; - - pixR = Color.red(pixColor); - pixG = Color.green(pixColor); - pixB = Color.blue(pixColor); - - pixColor = pixels[pos + 1]; - newR = Color.red(pixColor) - pixR + 127; - newG = Color.green(pixColor) - pixG + 127; - newB = Color.blue(pixColor) - pixB + 127; - - newR = Math.min(255, Math.max(0, newR)); - newG = Math.min(255, Math.max(0, newG)); - newB = Math.min(255, Math.max(0, newB)); - - pixels[pos] = Color.argb(255, newR, newG, newB); - } - } - - newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - return newBitmap; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/ImageUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/ImageUtils.java deleted file mode 100644 index b4afb59e0b..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/image/ImageUtils.java +++ /dev/null @@ -1,1517 +0,0 @@ -package dev.utils.app.image; - -import android.annotation.TargetApi; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Bitmap.CompressFormat; -import android.graphics.BitmapFactory; -import android.graphics.BitmapShader; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.LinearGradient; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Shader; -import android.graphics.drawable.Drawable; -import android.media.ExifInterface; -import android.os.Build; -import android.renderscript.Allocation; -import android.renderscript.Element; -import android.renderscript.RenderScript; -import android.renderscript.ScriptIntrinsicBlur; -import android.support.annotation.ColorInt; -import android.support.annotation.DrawableRes; -import android.support.annotation.FloatRange; -import android.support.annotation.IntRange; -import android.support.v4.content.ContextCompat; - -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; -import dev.utils.common.CloseUtils; - -/** - * detail: 图片相关工具类 - * Created by Ttt - */ -public final class ImageUtils { - - private ImageUtils() { - } - - // 日志Tag - private static final String TAG = ImageUtils.class.getSimpleName(); - - /** - * 获取 bitmap - * @param file The file. - * @return bitmap - */ - public static Bitmap getBitmap(final File file) { - if (file == null) return null; - return BitmapFactory.decodeFile(file.getAbsolutePath()); - } - - /** - * 获取 bitmap - * @param file The file. - * @param maxWidth 最大宽度 - * @param maxHeight 最大高度 - * @return bitmap - */ - public static Bitmap getBitmap(final File file, final int maxWidth, final int maxHeight) { - if (file == null) return null; - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(file.getAbsolutePath(), options); - options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); - options.inJustDecodeBounds = false; - return BitmapFactory.decodeFile(file.getAbsolutePath(), options); - } - - /** - * 获取 bitmap - * @param filePath The path of file. - * @return bitmap - */ - public static Bitmap getBitmap(final String filePath) { - if (isSpace(filePath)) return null; - return BitmapFactory.decodeFile(filePath); - } - - /** - * 获取 bitmap - * @param filePath The path of file. - * @param maxWidth 最大宽度 - * @param maxHeight 最大高度 - * @return bitmap - */ - public static Bitmap getBitmap(final String filePath, final int maxWidth, final int maxHeight) { - if (isSpace(filePath)) return null; - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(filePath, options); - options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); - options.inJustDecodeBounds = false; - return BitmapFactory.decodeFile(filePath, options); - } - - /** - * 获取 bitmap - * @param is 输入流 - * @return bitmap - */ - public static Bitmap getBitmap(final InputStream is) { - if (is == null) return null; - return BitmapFactory.decodeStream(is); - } - - /** - * 获取 bitmap - * @param resId 资源 id - * @return bitmap - */ - public static Bitmap getBitmap(@DrawableRes final int resId) { - Drawable drawable = ContextCompat.getDrawable(DevUtils.getContext(), resId); - Canvas canvas = new Canvas(); - Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - canvas.setBitmap(bitmap); - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - drawable.draw(canvas); - return bitmap; - } - - /** - * 获取 bitmap - * @param resId 资源 id - * @param maxWidth 最大宽度 - * @param maxHeight 最大高度 - * @return bitmap - */ - public static Bitmap getBitmap(@DrawableRes final int resId, final int maxWidth, final int maxHeight) { - BitmapFactory.Options options = new BitmapFactory.Options(); - final Resources resources = DevUtils.getContext().getResources(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeResource(resources, resId, options); - options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); - options.inJustDecodeBounds = false; - return BitmapFactory.decodeResource(resources, resId, options); - } - - /** - * 获取 bitmap - * @param fd 文件描述 - * @return bitmap - */ - public static Bitmap getBitmap(final FileDescriptor fd) { - if (fd == null) return null; - return BitmapFactory.decodeFileDescriptor(fd); - } - - /** - * 获取 bitmap - * @param fd 文件描述 - * @param maxWidth 最大宽度 - * @param maxHeight 最大高度 - * @return bitmap - */ - public static Bitmap getBitmap(final FileDescriptor fd, final int maxWidth, final int maxHeight) { - if (fd == null) return null; - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFileDescriptor(fd, null, options); - options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); - options.inJustDecodeBounds = false; - return BitmapFactory.decodeFileDescriptor(fd, null, options); - } - - /** - * 缩放图片 - * @param src 源图片 - * @param newWidth 新宽度 - * @param newHeight 新高度 - * @return 缩放后的图片 - */ - public static Bitmap scale(final Bitmap src, final int newWidth, final int newHeight) { - return scale(src, newWidth, newHeight, false); - } - - /** - * 缩放图片 - * @param src 源图片 - * @param newWidth 新宽度 - * @param newHeight 新高度 - * @param recycle 是否回收 - * @return 缩放后的图片 - */ - public static Bitmap scale(final Bitmap src, final int newWidth, final int newHeight, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - Bitmap ret = Bitmap.createScaledBitmap(src, newWidth, newHeight, true); - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 缩放图片 - * @param src 源图片 - * @param scaleWidth 缩放宽度倍数 - * @param scaleHeight 缩放高度倍数 - * @return 缩放后的图片 - */ - public static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight) { - return scale(src, scaleWidth, scaleHeight, false); - } - - /** - * 缩放图片 - * @param src 源图片 - * @param scaleWidth 缩放宽度倍数 - * @param scaleHeight 缩放高度倍数 - * @param recycle 是否回收 - * @return 缩放后的图片 - */ - public static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - Matrix matrix = new Matrix(); - matrix.setScale(scaleWidth, scaleHeight); - Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 裁剪图片 - * @param src 源图片 - * @param x 开始坐标 x - * @param y 开始坐标 y - * @param width 裁剪宽度 - * @param height 裁剪高度 - * @return 裁剪后的图片 - */ - public static Bitmap clip(final Bitmap src, final int x, final int y, final int width, final int height) { - return clip(src, x, y, width, height, false); - } - - /** - * 裁剪图片 - * @param src 源图片 - * @param x 开始坐标 x - * @param y 开始坐标 y - * @param width 裁剪宽度 - * @param height 裁剪高度 - * @param recycle 是否回收 - * @return 裁剪后的图片 - */ - public static Bitmap clip(final Bitmap src, final int x, final int y, final int width, final int height, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - Bitmap ret = Bitmap.createBitmap(src, x, y, width, height); - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 倾斜图片 - * @param src 源图片 - * @param kx 倾斜因子 x - * @param ky 倾斜因子 y - * @return 倾斜后的图片 - */ - public static Bitmap skew(final Bitmap src, final float kx, final float ky) { - return skew(src, kx, ky, 0, 0, false); - } - - /** - * 倾斜图片 - * @param src 源图片 - * @param kx 倾斜因子 x - * @param ky 倾斜因子 y - * @param recycle 是否回收 - * @return 倾斜后的图片 - */ - public static Bitmap skew(final Bitmap src, final float kx, final float ky, final boolean recycle) { - return skew(src, kx, ky, 0, 0, recycle); - } - - /** - * 倾斜图片 - * @param src 源图片 - * @param kx 倾斜因子 x - * @param ky 倾斜因子 y - * @param px 平移因子 x - * @param py 平移因子 y - * @return 倾斜后的图片 - */ - public static Bitmap skew(final Bitmap src, final float kx, final float ky, final float px, final float py) { - return skew(src, kx, ky, px, py, false); - } - - /** - * 倾斜图片 - * @param src 源图片 - * @param kx 倾斜因子 x - * @param ky 倾斜因子 y - * @param px 平移因子 x - * @param py 平移因子 y - * @param recycle 是否回收 - * @return 倾斜后的图片 - */ - public static Bitmap skew(final Bitmap src, final float kx, final float ky, final float px, final float py, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - Matrix matrix = new Matrix(); - matrix.setSkew(kx, ky, px, py); - Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 旋转图片 - * @param src 源图片 - * @param degrees 旋转角度 - * @param px 旋转点横坐标 - * @param py 旋转点纵坐标 - * @return 旋转后的图片 - */ - public static Bitmap rotate(final Bitmap src, final int degrees, final float px, final float py) { - return rotate(src, degrees, px, py, false); - } - - /** - * 旋转图片 - * @param src 源图片 - * @param degrees 旋转角度 - * @param px 旋转点横坐标 - * @param py 旋转点纵坐标 - * @param recycle 是否回收 - * @return 旋转后的图片 - */ - public static Bitmap rotate(final Bitmap src, final int degrees, final float px, final float py, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - if (degrees == 0) return src; - Matrix matrix = new Matrix(); - matrix.setRotate(degrees, px, py); - Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 获取图片旋转角度 - 返回 -1 表示异常 - * @param filePath The path of file. - * @return 旋转角度 - */ - public static int getRotateDegree(final String filePath) { - try { - ExifInterface exifInterface = new ExifInterface(filePath); - int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); - switch (orientation) { - case ExifInterface.ORIENTATION_ROTATE_90: - return 90; - case ExifInterface.ORIENTATION_ROTATE_180: - return 180; - case ExifInterface.ORIENTATION_ROTATE_270: - return 270; - default: - return 0; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getRotateDegree"); - return -1; - } - } - - /** - * 转为圆形图片 - * @param src 源图片 - * @return 圆形图片 - */ - public static Bitmap toRound(final Bitmap src) { - return toRound(src, 0, 0, false); - } - - /** - * 转为圆形图片 - * @param src 源图片 - * @param recycle 是否回收 - * @return 圆形图片 - */ - public static Bitmap toRound(final Bitmap src, final boolean recycle) { - return toRound(src, 0, 0, recycle); - } - - /** - * 转为圆形图片 - * @param src 源图片 - * @param borderSize 边框尺寸 - * @param borderColor 边框颜色 - * @return 圆形图片 - */ - public static Bitmap toRound(final Bitmap src, @IntRange(from = 0) int borderSize, @ColorInt int borderColor) { - return toRound(src, borderSize, borderColor, false); - } - - /** - * 转为圆形图片 - * @param src 源图片 - * @param recycle 是否回收 - * @param borderSize 边框尺寸 - * @param borderColor 边框颜色 - * @return 圆形图片 - */ - public static Bitmap toRound(final Bitmap src, @IntRange(from = 0) int borderSize, @ColorInt int borderColor, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - int width = src.getWidth(); - int height = src.getHeight(); - int size = Math.min(width, height); - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig()); - float center = size / 2f; - RectF rectF = new RectF(0, 0, width, height); - rectF.inset((width - size) / 2f, (height - size) / 2f); - Matrix matrix = new Matrix(); - matrix.setTranslate(rectF.left, rectF.top); - matrix.preScale((float) size / width, (float) size / height); - BitmapShader shader = new BitmapShader(src, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); - shader.setLocalMatrix(matrix); - paint.setShader(shader); - Canvas canvas = new Canvas(ret); - canvas.drawRoundRect(rectF, center, center, paint); - if (borderSize > 0) { - paint.setShader(null); - paint.setColor(borderColor); - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(borderSize); - float radius = center - borderSize / 2f; - canvas.drawCircle(width / 2f, height / 2f, radius, paint); - } - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 转为圆角图片 - * @param src 源图片 - * @param radius 圆角的度数 - * @return 圆角图片 - */ - public static Bitmap toRoundCorner(final Bitmap src, final float radius) { - return toRoundCorner(src, radius, 0, 0, false); - } - - /** - * 转为圆角图片 - * @param src 源图片 - * @param radius 圆角的度数 - * @param recycle 是否回收 - * @return 圆角图片 - */ - public static Bitmap toRoundCorner(final Bitmap src, final float radius, final boolean recycle) { - return toRoundCorner(src, radius, 0, 0, recycle); - } - - /** - * 转为圆角图片 - * @param src 源图片 - * @param radius 圆角的度数 - * @param borderSize 边框尺寸 - * @param borderColor 边框颜色 - * @return 圆角图片 - */ - public static Bitmap toRoundCorner(final Bitmap src, final float radius, @IntRange(from = 0) int borderSize, @ColorInt int borderColor) { - return toRoundCorner(src, radius, borderSize, borderColor, false); - } - - /** - * 转为圆角图片 - * @param src 源图片 - * @param radius 圆角的度数 - * @param borderSize 边框尺寸 - * @param borderColor 边框颜色 - * @param recycle 是否回收 - * @return 圆角图片 - */ - public static Bitmap toRoundCorner(final Bitmap src, final float radius, @IntRange(from = 0) int borderSize, @ColorInt int borderColor, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - int width = src.getWidth(); - int height = src.getHeight(); - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig()); - BitmapShader shader = new BitmapShader(src, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); - paint.setShader(shader); - Canvas canvas = new Canvas(ret); - RectF rectF = new RectF(0, 0, width, height); - float halfBorderSize = borderSize / 2f; - rectF.inset(halfBorderSize, halfBorderSize); - canvas.drawRoundRect(rectF, radius, radius, paint); - if (borderSize > 0) { - paint.setShader(null); - paint.setColor(borderColor); - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(borderSize); - paint.setStrokeCap(Paint.Cap.ROUND); - canvas.drawRoundRect(rectF, radius, radius, paint); - } - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 添加圆角边框 - * @param src 源图片 - * @param borderSize 边框尺寸 - * @param color 边框颜色 - * @param cornerRadius 圆角半径 - * @return 圆角边框图 - */ - public static Bitmap addCornerBorder(final Bitmap src, @IntRange(from = 1) final int borderSize, @ColorInt final int color, @FloatRange(from = 0) final float cornerRadius) { - return addBorder(src, borderSize, color, false, cornerRadius, false); - } - - /** - * 添加圆角边框 - * @param src 源图片 - * @param borderSize 边框尺寸 - * @param color 边框颜色 - * @param cornerRadius 圆角半径 - * @param recycle 是否回收 - * @return 圆角边框图 - */ - public static Bitmap addCornerBorder(final Bitmap src, @IntRange(from = 1) final int borderSize, @ColorInt final int color, @FloatRange(from = 0) final float cornerRadius, final boolean recycle) { - return addBorder(src, borderSize, color, false, cornerRadius, recycle); - } - - /** - * 添加圆形边框 - * @param src 源图片 - * @param borderSize 边框尺寸 - * @param color 边框颜色 - * @return 圆形边框图 - */ - public static Bitmap addCircleBorder(final Bitmap src, @IntRange(from = 1) final int borderSize, @ColorInt final int color) { - return addBorder(src, borderSize, color, true, 0, false); - } - - /** - * 添加圆形边框 - * @param src 源图片 - * @param borderSize 边框尺寸 - * @param color 边框颜色 - * @param recycle 是否回收 - * @return 圆形边框图 - */ - public static Bitmap addCircleBorder(final Bitmap src, @IntRange(from = 1) final int borderSize, @ColorInt final int color, final boolean recycle) { - return addBorder(src, borderSize, color, true, 0, recycle); - } - - /** - * 添加边框 - * @param src 源图片 - * @param borderSize 边框尺寸 - * @param color 边框颜色 - * @param isCircle 是否画圆 - * @param cornerRadius 圆角半径 - * @param recycle 是否回收 - * @return 边框图 - */ - private static Bitmap addBorder(final Bitmap src, @IntRange(from = 1) final int borderSize, @ColorInt final int color, - final boolean isCircle, final float cornerRadius, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - Bitmap ret = recycle ? src : src.copy(src.getConfig(), true); - int width = ret.getWidth(); - int height = ret.getHeight(); - Canvas canvas = new Canvas(ret); - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - paint.setColor(color); - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(borderSize); - if (isCircle) { - float radius = Math.min(width, height) / 2f - borderSize / 2f; - canvas.drawCircle(width / 2f, height / 2f, radius, paint); - } else { - int halfBorderSize = borderSize >> 1; - RectF rectF = new RectF(halfBorderSize, halfBorderSize,width - halfBorderSize, height - halfBorderSize); - canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint); - } - return ret; - } - - /** - * 添加倒影 - * @param src 源图片的 - * @param reflectionHeight 倒影高度 - * @return 带倒影图片 - */ - public static Bitmap addReflection(final Bitmap src, final int reflectionHeight) { - return addReflection(src, reflectionHeight, false); - } - - /** - * 添加倒影 - * @param src 源图片的 - * @param reflectionHeight 倒影高度 - * @param recycle 是否回收 - * @return 带倒影图片 - */ - public static Bitmap addReflection(final Bitmap src, final int reflectionHeight, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - // 原图与倒影之间的间距 - final int REFLECTION_GAP = 0; - int srcWidth = src.getWidth(); - int srcHeight = src.getHeight(); - Matrix matrix = new Matrix(); - matrix.preScale(1, -1); - Bitmap reflectionBitmap = Bitmap.createBitmap(src, 0, srcHeight - reflectionHeight, srcWidth, reflectionHeight, matrix, false); - Bitmap ret = Bitmap.createBitmap(srcWidth, srcHeight + reflectionHeight, src.getConfig()); - Canvas canvas = new Canvas(ret); - canvas.drawBitmap(src, 0, 0, null); - canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null); - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - LinearGradient shader = new LinearGradient(0, srcHeight,0, ret.getHeight() + REFLECTION_GAP,0x70FFFFFF,0x00FFFFFF, Shader.TileMode.MIRROR); - paint.setShader(shader); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); - canvas.drawRect(0, srcHeight + REFLECTION_GAP, srcWidth, ret.getHeight(), paint); - if (!reflectionBitmap.isRecycled()) reflectionBitmap.recycle(); - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 添加文字水印 - * @param src 源图片 - * @param content 水印文本 - * @param textSize 水印字体大小 - * @param color 水印字体颜色 - * @param x 起始坐标 x - * @param y 起始坐标 y - * @return 带有文字水印的图片 - */ - public static Bitmap addTextWatermark(final Bitmap src, final String content, final int textSize, - @ColorInt final int color, final float x, final float y) { - return addTextWatermark(src, content, textSize, color, x, y, false); - } - - /** - * 添加文字水印 - * @param src 源图片 - * @param content 水印文本 - * @param textSize 水印字体大小 - * @param color 水印字体颜色 - * @param x 起始坐标 x - * @param y 起始坐标 y - * @param recycle 是否回收 - * @return 带有文字水印的图片 - */ - public static Bitmap addTextWatermark(final Bitmap src, final String content, final float textSize, - @ColorInt final int color, final float x, final float y, final boolean recycle) { - if (isEmptyBitmap(src) || content == null) return null; - Bitmap ret = src.copy(src.getConfig(), true); - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - Canvas canvas = new Canvas(ret); - paint.setColor(color); - paint.setTextSize(textSize); - Rect bounds = new Rect(); - paint.getTextBounds(content, 0, content.length(), bounds); - canvas.drawText(content, x, y + textSize, paint); - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 添加图片水印 - * @param src 源图片 - * @param watermark 图片水印 - * @param x 起始坐标 x - * @param y 起始坐标 y - * @param alpha 透明度 - * @return 带有图片水印的图片 - */ - public static Bitmap addImageWatermark(final Bitmap src, final Bitmap watermark, final int x, final int y, final int alpha) { - return addImageWatermark(src, watermark, x, y, alpha, false); - } - - /** - * 添加图片水印 - * @param src 源图片 - * @param watermark 图片水印 - * @param x 起始坐标 x - * @param y 起始坐标 y - * @param alpha 透明度 - * @param recycle 是否回收 - * @return 带有图片水印的图片 - */ - public static Bitmap addImageWatermark(final Bitmap src, final Bitmap watermark, final int x, final int y, final int alpha, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - Bitmap ret = src.copy(src.getConfig(), true); - if (!isEmptyBitmap(watermark)) { - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - Canvas canvas = new Canvas(ret); - paint.setAlpha(alpha); - canvas.drawBitmap(watermark, x, y, paint); - } - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 转为 alpha 位图 - * @param src 源图片 - * @return alpha 位图 - */ - public static Bitmap toAlpha(final Bitmap src) { - return toAlpha(src, false); - } - - /** - * 转为 alpha 位图 - * @param src 源图片 - * @param recycle 是否回收 - * @return alpha 位图 - */ - public static Bitmap toAlpha(final Bitmap src, final Boolean recycle) { - if (isEmptyBitmap(src)) return null; - Bitmap ret = src.extractAlpha(); - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 转为灰度图片 - * @param src 源图片 - * @return 灰度图 - */ - public static Bitmap toGray(final Bitmap src) { - return toGray(src, false); - } - - /** - * 转为灰度图片 - * @param src 源图片 - * @param recycle 是否回收 - * @return 灰度图 - */ - public static Bitmap toGray(final Bitmap src, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - Bitmap ret = Bitmap.createBitmap(src.getWidth(), src.getHeight(), src.getConfig()); - Canvas canvas = new Canvas(ret); - Paint paint = new Paint(); - ColorMatrix colorMatrix = new ColorMatrix(); - colorMatrix.setSaturation(0); - ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix); - paint.setColorFilter(colorMatrixColorFilter); - canvas.drawBitmap(src, 0, 0, paint); - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * 快速模糊 - 先缩小原图,对小图进行模糊,再放大回原先尺寸 - * @param src 源图片 - * @param scale 缩放比例(0...1) - * @param radius 模糊半径 - * @return 模糊后的图片 - */ - public static Bitmap fastBlur(final Bitmap src, - @FloatRange(from = 0, to = 1, fromInclusive = false) final float scale, - @FloatRange(from = 0, to = 25, fromInclusive = false) final float radius) { - return fastBlur(src, scale, radius, false); - } - - /** - * 快速模糊图片 - 先缩小原图,对小图进行模糊,再放大回原先尺寸 - * @param src 源图片 - * @param scale 缩放比例(0...1) - * @param radius 模糊半径(0...25) - * @param recycle 是否回收 - * @return 模糊后的图片 - */ - public static Bitmap fastBlur(final Bitmap src, - @FloatRange(from = 0, to = 1, fromInclusive = false) final float scale, - @FloatRange(from = 0, to = 25, fromInclusive = false) final float radius, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - int width = src.getWidth(); - int height = src.getHeight(); - Matrix matrix = new Matrix(); - matrix.setScale(scale, scale); - Bitmap scaleBitmap = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); - Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); - Canvas canvas = new Canvas(); - PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.TRANSPARENT, PorterDuff.Mode.SRC_ATOP); - paint.setColorFilter(filter); - canvas.scale(scale, scale); - canvas.drawBitmap(scaleBitmap, 0, 0, paint); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - scaleBitmap = renderScriptBlur(scaleBitmap, radius, recycle); - } else { - scaleBitmap = stackBlur(scaleBitmap, (int) radius, recycle); - } - if (scale == 1) { - if (recycle && !src.isRecycled()) src.recycle(); - return scaleBitmap; - } - Bitmap ret = Bitmap.createScaledBitmap(scaleBitmap, width, height, true); - if (!scaleBitmap.isRecycled()) scaleBitmap.recycle(); - if (recycle && !src.isRecycled()) src.recycle(); - return ret; - } - - /** - * renderScript 模糊图片 - API 大于 17 - * @param src 源图片 - * @param radius 模糊半径(0...25) - * @return 模糊后的图片 - */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - public static Bitmap renderScriptBlur(final Bitmap src, @FloatRange(from = 0, to = 25, fromInclusive = false) final float radius) { - return renderScriptBlur(src, radius, false); - } - - /** - * renderScript 模糊图片 - API 大于 17 - * @param src 源图片 - * @param radius 模糊半径(0...25) - * @param recycle 是否回收 - * @return 模糊后的图片 - */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - public static Bitmap renderScriptBlur(final Bitmap src, @FloatRange(from = 0, to = 25, fromInclusive = false) final float radius, final boolean recycle) { - RenderScript rs = null; - Bitmap ret = recycle ? src : src.copy(src.getConfig(), true); - try { - rs = RenderScript.create(DevUtils.getContext()); - rs.setMessageHandler(new RenderScript.RSMessageHandler()); - Allocation input = Allocation.createFromBitmap(rs, ret, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); - Allocation output = Allocation.createTyped(rs, input.getType()); - ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); - blurScript.setInput(input); - blurScript.setRadius(radius); - blurScript.forEach(output); - output.copyTo(ret); - } finally { - if (rs != null) { - rs.destroy(); - } - } - return ret; - } - - /** - * stack 模糊图片 - * @param src 源图片 - * @param radius 模糊半径 - * @return stack 模糊后的图片 - */ - public static Bitmap stackBlur(final Bitmap src, final int radius) { - return stackBlur(src, radius, false); - } - - /** - * stack 模糊图片 - * @param src 源图片 - * @param radius 模糊半径 - * @param recycle 是否回收 - * @return stack 模糊后的图片 - */ - public static Bitmap stackBlur(final Bitmap src, int radius, final boolean recycle) { - Bitmap ret = recycle ? src : src.copy(src.getConfig(), true); - if (radius < 1) { - radius = 1; - } - int w = ret.getWidth(); - int h = ret.getHeight(); - - int[] pix = new int[w * h]; - ret.getPixels(pix, 0, w, 0, 0, w, h); - - int wm = w - 1; - int hm = h - 1; - int wh = w * h; - int div = radius + radius + 1; - - int r[] = new int[wh]; - int g[] = new int[wh]; - int b[] = new int[wh]; - int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; - int vmin[] = new int[Math.max(w, h)]; - - int divsum = (div + 1) >> 1; - divsum *= divsum; - int dv[] = new int[256 * divsum]; - for (i = 0; i < 256 * divsum; i++) { - dv[i] = (i / divsum); - } - - yw = yi = 0; - - int[][] stack = new int[div][3]; - int stackpointer; - int stackstart; - int[] sir; - int rbs; - int r1 = radius + 1; - int routsum, goutsum, boutsum; - int rinsum, ginsum, binsum; - - for (y = 0; y < h; y++) { - rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; - for (i = -radius; i <= radius; i++) { - p = pix[yi + Math.min(wm, Math.max(i, 0))]; - sir = stack[i + radius]; - sir[0] = (p & 0xff0000) >> 16; - sir[1] = (p & 0x00ff00) >> 8; - sir[2] = (p & 0x0000ff); - rbs = r1 - Math.abs(i); - rsum += sir[0] * rbs; - gsum += sir[1] * rbs; - bsum += sir[2] * rbs; - if (i > 0) { - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - } else { - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - } - } - stackpointer = radius; - - for (x = 0; x < w; x++) { - - r[yi] = dv[rsum]; - g[yi] = dv[gsum]; - b[yi] = dv[bsum]; - - rsum -= routsum; - gsum -= goutsum; - bsum -= boutsum; - - stackstart = stackpointer - radius + div; - sir = stack[stackstart % div]; - - routsum -= sir[0]; - goutsum -= sir[1]; - boutsum -= sir[2]; - - if (y == 0) { - vmin[x] = Math.min(x + radius + 1, wm); - } - p = pix[yw + vmin[x]]; - - sir[0] = (p & 0xff0000) >> 16; - sir[1] = (p & 0x00ff00) >> 8; - sir[2] = (p & 0x0000ff); - - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - - rsum += rinsum; - gsum += ginsum; - bsum += binsum; - - stackpointer = (stackpointer + 1) % div; - sir = stack[(stackpointer) % div]; - - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - - rinsum -= sir[0]; - ginsum -= sir[1]; - binsum -= sir[2]; - - yi++; - } - yw += w; - } - for (x = 0; x < w; x++) { - rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; - yp = -radius * w; - for (i = -radius; i <= radius; i++) { - yi = Math.max(0, yp) + x; - - sir = stack[i + radius]; - - sir[0] = r[yi]; - sir[1] = g[yi]; - sir[2] = b[yi]; - - rbs = r1 - Math.abs(i); - - rsum += r[yi] * rbs; - gsum += g[yi] * rbs; - bsum += b[yi] * rbs; - - if (i > 0) { - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - } else { - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - } - - if (i < hm) { - yp += w; - } - } - yi = x; - stackpointer = radius; - for (y = 0; y < h; y++) { - // Preserve alpha channel: ( 0xff000000 & pix[yi] ) - pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; - - rsum -= routsum; - gsum -= goutsum; - bsum -= boutsum; - - stackstart = stackpointer - radius + div; - sir = stack[stackstart % div]; - - routsum -= sir[0]; - goutsum -= sir[1]; - boutsum -= sir[2]; - - if (x == 0) { - vmin[y] = Math.min(y + r1, hm) * w; - } - p = x + vmin[y]; - - sir[0] = r[p]; - sir[1] = g[p]; - sir[2] = b[p]; - - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - - rsum += rinsum; - gsum += ginsum; - bsum += binsum; - - stackpointer = (stackpointer + 1) % div; - sir = stack[stackpointer]; - - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - - rinsum -= sir[0]; - ginsum -= sir[1]; - binsum -= sir[2]; - - yi += w; - } - } - ret.setPixels(pix, 0, w, 0, 0, w, h); - return ret; - } - - /** - * 保存图片 - * @param src 源图片 - * @param filePath The path of file. - * @param format 格式 - * @return true : 成功, false : 失败 - */ - public static boolean save(final Bitmap src, final String filePath, final CompressFormat format) { - return save(src, getFileByPath(filePath), format, false); - } - - /** - * 保存图片 - * @param src 源图片 - * @param file The file. - * @param format 格式 - * @return true : 成功, false : 失败 - */ - public static boolean save(final Bitmap src, final File file, final CompressFormat format) { - return save(src, file, format, false); - } - - /** - * 保存图片 - * @param src 源图片 - * @param filePath The path of file. - * @param format 格式 - * @param recycle 是否回收 - * @return true : 成功, false : 失败 - */ - public static boolean save(final Bitmap src, final String filePath, final CompressFormat format, final boolean recycle) { - return save(src, getFileByPath(filePath), format, recycle); - } - - /** - * 保存图片 - * @param src 源图片 - * @param file The file. - * @param format 格式 - * @param recycle 是否回收 - * @return true : 成功, false : 失败 - */ - public static boolean save(final Bitmap src, final File file, final CompressFormat format, final boolean recycle) { - if (isEmptyBitmap(src) || !createFileByDeleteOldFile(file)) return false; - OutputStream os = null; - boolean ret = false; - try { - os = new BufferedOutputStream(new FileOutputStream(file)); - ret = src.compress(format, 100, os); - if (recycle && !src.isRecycled()) src.recycle(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "save"); - } finally { - CloseUtils.closeIO(os); - } - return ret; - } - - /** - * 根据文件名判断文件是否为图片 - * @param file The file. - * @return true : 是, false : 否 - */ - public static boolean isImage(final File file) { - return file != null && isImage(file.getPath()); - } - - /** - * 根据文件名判断文件是否为图片 - * @param filePath The path of file. - * @return true : 是, false : 否 - */ - public static boolean isImage(final String filePath) { - String path = filePath.toUpperCase(); - return path.endsWith(".PNG") || path.endsWith(".JPG") || path.endsWith(".JPEG") || path.endsWith(".BMP") || path.endsWith(".GIF"); - } - - /** - * 获取图片类型 - * @param filePath The path of file. - * @return 图片类型 - */ - public static String getImageType(final String filePath) { - return getImageType(getFileByPath(filePath)); - } - - /** - * 获取图片类型 - * @param file The file. - * @return 图片类型 - */ - public static String getImageType(final File file) { - if (file == null) return null; - InputStream is = null; - try { - is = new FileInputStream(file); - return getImageType(is); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getImageType"); - return null; - } finally { - CloseUtils.closeIO(is); - } - } - - /** - * 流获取图片类型 - * @param is 图片输入流 - * @return 图片类型 - */ - public static String getImageType(final InputStream is) { - if (is == null) return null; - try { - byte[] bytes = new byte[8]; - return is.read(bytes, 0, 8) != -1 ? getImageType(bytes) : null; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getImageType"); - return null; - } - } - - /** - * 获取图片类型 - * @param bytes bitmap 的前 8 字节 - * @return 图片类型 - */ - public static String getImageType(final byte[] bytes) { - if (isJPEG(bytes)) return "JPEG"; - if (isGIF(bytes)) return "GIF"; - if (isPNG(bytes)) return "PNG"; - if (isBMP(bytes)) return "BMP"; - return null; - } - - /** - * 判断是否jpg图片 - * @param b - * @return - */ - private static boolean isJPEG(final byte[] b) { - return b.length >= 2 && (b[0] == (byte) 0xFF) && (b[1] == (byte) 0xD8); - } - - /** - * 判断是否gif图片 - * @param b - * @return - */ - private static boolean isGIF(final byte[] b) { - return b.length >= 6 && b[0] == 'G' && b[1] == 'I' && b[2] == 'F' && b[3] == '8' && (b[4] == '7' || b[4] == '9') && b[5] == 'a'; - } - - /** - * 判断是否png图片 - * @param b - * @return - */ - private static boolean isPNG(final byte[] b) { - return b.length >= 8 - && (b[0] == (byte) 137 && b[1] == (byte) 80 - && b[2] == (byte) 78 && b[3] == (byte) 71 - && b[4] == (byte) 13 && b[5] == (byte) 10 - && b[6] == (byte) 26 && b[7] == (byte) 10); - } - - /** - * 判断是否bmp图片 - * @param b - * @return - */ - private static boolean isBMP(final byte[] b) { - return b.length >= 2 && (b[0] == 0x42) && (b[1] == 0x4d); - } - - /** - * 判断 bitmap 对象是否为空 - * @param src 源图片 - * @return true : 是, false : 否 - */ - private static boolean isEmptyBitmap(final Bitmap src) { - return src == null || src.getWidth() == 0 || src.getHeight() == 0; - } - - // ============== - // 下方和压缩有关 - // ============== - - /** - * 按缩放压缩 - * @param src 源图片 - * @param newWidth 新宽度 - * @param newHeight 新高度 - * @return 缩放压缩后的图片 - */ - public static Bitmap compressByScale(final Bitmap src, final int newWidth, final int newHeight) { - return scale(src, newWidth, newHeight, false); - } - - /** - * 按缩放压缩 - * @param src 源图片 - * @param newWidth 新宽度 - * @param newHeight 新高度 - * @param recycle 是否回收 - * @return 缩放压缩后的图片 - */ - public static Bitmap compressByScale(final Bitmap src, final int newWidth, final int newHeight, final boolean recycle) { - return scale(src, newWidth, newHeight, recycle); - } - - /** - * 按缩放压缩 - * @param src 源图片 - * @param scaleWidth 缩放宽度倍数 - * @param scaleHeight 缩放高度倍数 - * @return 缩放压缩后的图片 - */ - public static Bitmap compressByScale(final Bitmap src, final float scaleWidth, final float scaleHeight) { - return scale(src, scaleWidth, scaleHeight, false); - } - - /** - * 按缩放压缩 - * @param src 源图片 - * @param scaleWidth 缩放宽度倍数 - * @param scaleHeight 缩放高度倍数 - * @param recycle 是否回收 - * @return 缩放压缩后的图片 - */ - public static Bitmap compressByScale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) { - return scale(src, scaleWidth, scaleHeight, recycle); - } - - /** - * 按质量压缩 - * @param src 源图片 - * @param quality 质量 - * @return 质量压缩后的图片 - */ - public static Bitmap compressByQuality(final Bitmap src, @IntRange(from = 0, to = 100) final int quality) { - return compressByQuality(src, quality, false); - } - - /** - * 按质量压缩 - * @param src 源图片 - * @param quality 质量 - * @param recycle 是否回收 - * @return 质量压缩后的图片 - */ - public static Bitmap compressByQuality(final Bitmap src, @IntRange(from = 0, to = 100) final int quality, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - src.compress(CompressFormat.JPEG, quality, baos); - byte[] bytes = baos.toByteArray(); - if (recycle && !src.isRecycled()) src.recycle(); - return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - } - - /** - * 按质量压缩 - * @param src 源图片 - * @param maxByteSize 允许最大值字节数 - * @return 质量压缩压缩过的图片 - */ - public static Bitmap compressByQuality(final Bitmap src, final long maxByteSize) { - return compressByQuality(src, maxByteSize, false); - } - - /** - * 按质量压缩 - * @param src 源图片 - * @param maxByteSize 允许最大值字节数 - * @param recycle 是否回收 - * @return 质量压缩压缩过的图片 - */ - public static Bitmap compressByQuality(final Bitmap src, final long maxByteSize, final boolean recycle) { - if (isEmptyBitmap(src) || maxByteSize <= 0) return null; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - src.compress(CompressFormat.JPEG, 100, baos); - byte[] bytes; - if (baos.size() <= maxByteSize) {// 最好质量的不大于最大字节,则返回最佳质量 - bytes = baos.toByteArray(); - } else { - baos.reset(); - src.compress(CompressFormat.JPEG, 0, baos); - if (baos.size() >= maxByteSize) { // 最差质量不小于最大字节,则返回最差质量 - bytes = baos.toByteArray(); - } else { - // 二分法寻找最佳质量 - int st = 0; - int end = 100; - int mid = 0; - while (st < end) { - mid = (st + end) / 2; - baos.reset(); - src.compress(CompressFormat.JPEG, mid, baos); - int len = baos.size(); - if (len == maxByteSize) { - break; - } else if (len > maxByteSize) { - end = mid - 1; - } else { - st = mid + 1; - } - } - if (end == mid - 1) { - baos.reset(); - src.compress(CompressFormat.JPEG, st, baos); - } - bytes = baos.toByteArray(); - } - } - if (recycle && !src.isRecycled()) src.recycle(); - return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - } - - /** - * 按采样大小压缩 - * @param src 源图片 - * @param sampleSize 采样率大小 - * @return 按采样率压缩后的图片 - */ - - public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize) { - return compressBySampleSize(src, sampleSize, false); - } - - /** - * 按采样大小压缩 - * @param src 源图片 - * @param sampleSize 采样率大小 - * @param recycle 是否回收 - * @return 按采样率压缩后的图片 - */ - public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = sampleSize; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - src.compress(CompressFormat.JPEG, 100, baos); - byte[] bytes = baos.toByteArray(); - if (recycle && !src.isRecycled()) src.recycle(); - return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); - } - - /** - * 按采样大小压缩 - * @param src 源图片 - * @param maxWidth 最大宽度 - * @param maxHeight 最大高度 - * @return 按采样率压缩后的图片 - */ - public static Bitmap compressBySampleSize(final Bitmap src, final int maxWidth, final int maxHeight) { - return compressBySampleSize(src, maxWidth, maxHeight, false); - } - - /** - * 按采样大小压缩 - * @param src 源图片 - * @param maxWidth 最大宽度 - * @param maxHeight 最大高度 - * @param recycle 是否回收 - * @return 按采样率压缩后的图片 - */ - public static Bitmap compressBySampleSize(final Bitmap src, final int maxWidth, final int maxHeight, final boolean recycle) { - if (isEmptyBitmap(src)) return null; - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - src.compress(CompressFormat.JPEG, 100, baos); - byte[] bytes = baos.toByteArray(); - BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); - options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); - options.inJustDecodeBounds = false; - if (recycle && !src.isRecycled()) src.recycle(); - return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); - } - - /** - * 计算采样大小 - * @param options 选项 - * @param maxWidth 最大宽度 - * @param maxHeight 最大高度 - * @return 采样大小 - */ - private static int calculateInSampleSize(final BitmapFactory.Options options, final int maxWidth, final int maxHeight) { - int height = options.outHeight; - int width = options.outWidth; - int inSampleSize = 1; - while ((width >>= 1) >= maxWidth && (height >>= 1) >= maxHeight) { - inSampleSize <<= 1; - } - return inSampleSize; - } - - // = - - /** - * 获取文件 - * @param filePath - * @return - */ - private static File getFileByPath(final String filePath){ - return filePath != null ? new File(filePath) : null; - } - - /** - * 判断文件是否存在,存在则在创建之前删除 - * @param file - * @return true : 创建成功, false : 创建失败 - */ - private static boolean createFileByDeleteOldFile(final File file) { - if (file == null) return false; - // 文件存在并且删除失败返回 false - if (file.exists() && !file.delete()) return false; - // 创建目录失败返回 false - if (!createOrExistsDir(file.getParentFile())) return false; - try { - return file.createNewFile(); - } catch (IOException e) { - LogPrintUtils.eTag(TAG, e, "createFileByDeleteOldFile"); - return false; - } - } - - /** - * 判断目录是否存在,不存在则判断是否创建成功 - * @param file 文件 - * @return true : 存在或创建成功, false : 不存在或创建失败 - */ - private static boolean createOrExistsDir(final File file) { - // 如果存在,是目录则返回 true,是文件则返回 false,不存在则返回是否创建成功 - return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); - } - - /** - * 判断字符串是否为 null 或全为空白字符 - * @param str 待校验字符串 - * @return - */ - private static boolean isSpace(final String str) { - if (str == null) return true; - for (int i = 0, len = str.length(); i < len; ++i) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/info/ApkInfoItem.java b/DevLibUtils/src/main/java/dev/utils/app/info/ApkInfoItem.java deleted file mode 100644 index 5707bbd116..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/info/ApkInfoItem.java +++ /dev/null @@ -1,213 +0,0 @@ -package dev.utils.app.info; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.Signature; -import android.text.TextUtils; -import android.text.format.Formatter; - -import java.io.File; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -import java.security.cert.X509Certificate; -import java.text.SimpleDateFormat; -import java.util.ArrayList; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; -import dev.utils.R; -import dev.utils.app.AppCommonUtils; -import dev.utils.app.SignaturesUtils; -import dev.utils.common.FileUtils; - -/** - * detail: Apk 信息Item - * Created by Ttt - */ -public final class ApkInfoItem { - - // 日志Tag - private static final String TAG = ApkInfoItem.class.getSimpleName(); - // apk文件地址 - private String apkUri; - // App 信息实体类 - private AppInfoBean appInfoBean; - // App 参数集 - private ArrayList listKeyValues = new ArrayList<>(); - - private ApkInfoItem(){ - } - - public static ApkInfoItem obtain(String apkUri) throws Exception { - // 如果地址为null, 则直接不处理 - if (TextUtils.isEmpty(apkUri)){ - return null; - } - // 文件信息 - File apkFile = new File(apkUri); - // 如果文件不存在, 则不处理 - if (!apkFile.exists()){ - return null; - } - - // https://blog.csdn.net/sljjyy/article/details/17370665 - - // 获取上下文 - Context context = DevUtils.getContext(); - // 初始化包管理类 - PackageManager pManager = context.getPackageManager(); - // 获取对应的PackageInfo(原始的PackageInfo 获取 signatures 等于null,需要这样获取) - PackageInfo pInfo = pManager.getPackageArchiveInfo(apkUri, PackageManager.GET_ACTIVITIES); - // = 设置 apk 位置信息 = - ApplicationInfo appInfo = pInfo.applicationInfo; - /* 必须加这两句,不然下面icon获取是default icon而不是应用包的icon */ - appInfo.sourceDir = apkUri; - appInfo.publicSourceDir = apkUri; - // 初始化实体类 - ApkInfoItem appInfoItem = new ApkInfoItem(); - // 保存apk文件地址 - appInfoItem.apkUri = apkUri; - // 获取app 信息 - appInfoItem.appInfoBean = new AppInfoBean(pInfo, pManager); - // == 获取 == - // 格式化日期 - SimpleDateFormat dFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - // 获取签名信息 - Signature[] signatures = SignaturesUtils.getSignaturesFromApk(apkFile); - // =========== - // app 签名MD5 - String md5 = SignaturesUtils.signatureMD5(signatures); - // app SHA1 - String sha1 = SignaturesUtils.signatureSHA1(signatures); - // app SHA256 - String sha256 = SignaturesUtils.signatureSHA256(signatures); - // app 最低支持版本 - int minSdkVersion = -1; - // 属于7.0以上才有的方法 - if (AppCommonUtils.isN()){ - minSdkVersion = pInfo.applicationInfo.minSdkVersion; - } - // app 兼容sdk版本 - int targetSdkVersion = pInfo.applicationInfo.targetSdkVersion; - // 获取 app 安装包大小 - String apkLength = Formatter.formatFileSize(DevUtils.getContext(), FileUtils.getFileLength(apkFile)); - - // = 临时数据存储 = - // 是否保存 - boolean isError = false; - // 临时签名信息 - ArrayList listTemps = new ArrayList<>(); - - // Android的APK应用签名机制以及读取签名的方法 - // http://www.jb51.net/article/79894.htm - try { - // 获取证书对象 - X509Certificate cert = SignaturesUtils.getX509Certificate(signatures); - // 设置有效期 - StringBuilder sbEffective = new StringBuilder(); - sbEffective.append(dFormat.format(cert.getNotBefore())); - sbEffective.append(" " + context.getString(R.string.dev_str_to) + " "); // 至 - sbEffective.append(dFormat.format(cert.getNotAfter())); - sbEffective.append("\n\n"); - sbEffective.append(cert.getNotBefore()); - sbEffective.append(" " + context.getString(R.string.dev_str_to) + " "); - sbEffective.append(cert.getNotAfter()); - // 获取有效期 - String effective = sbEffective.toString(); - // 证书是否过期 true = 过期,false = 未过期 - boolean isEffective = false; - try { - cert.checkValidity(); - // CertificateExpiredException - 如果证书已过期。 - // CertificateNotYetValidException - 如果证书不再有效。 - } catch (CertificateExpiredException ce) { - isEffective = true; - } catch (CertificateNotYetValidException ce) { - isEffective = true; - } - // 判断是否过期 - String isEffectiveState = isEffective ? context.getString(R.string.dev_str_overdue) : context.getString(R.string.dev_str_notoverdue); - // 证书发布方 - String principal = cert.getIssuerX500Principal().toString(); - // 证书版本号 - String version = cert.getVersion() + ""; - // 证书算法名称 - String sigalgname = cert.getSigAlgName(); - // 证书算法OID - String sigalgoid = cert.getSigAlgOID(); - // 证书机器码 - String serialnumber = cert.getSerialNumber().toString(); - // 证书 DER编码 - String dercode = SignaturesUtils.toHexString(cert.getTBSCertificate()); - // 获取有效期 - listTemps.add(KeyValueBean.get(R.string.dev_str_effective, effective)); - // 判断是否过期 - listTemps.add(KeyValueBean.get(R.string.dev_str_iseffective, isEffectiveState)); - // 证书发布方 - listTemps.add(KeyValueBean.get(R.string.dev_str_principal, principal)); - // 证书版本号 - listTemps.add(KeyValueBean.get(R.string.dev_str_version, version)); - // 证书算法名称 - listTemps.add(KeyValueBean.get(R.string.dev_str_sigalgname, sigalgname)); - // 证书算法OID - listTemps.add(KeyValueBean.get(R.string.dev_str_sigalgoid, sigalgoid)); - // 证书机器码 - listTemps.add(KeyValueBean.get(R.string.dev_str_dercode, serialnumber)); - // 证书 DER编码 - listTemps.add(KeyValueBean.get(R.string.dev_str_serialnumber, dercode)); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "obtain"); - isError = true; - } - - // ================ - // === 保存集合 === - // ================ - // app 包名 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_packname, appInfoItem.appInfoBean.getAppPackName())); - // 没报错才存储 MD5 信息 - if (!isError) { - // app 签名MD5 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_md5, md5)); - } - // app 版本号 - 主要用于app内部版本判断 int 类型 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_version_code, appInfoItem.appInfoBean.getVersionCode() + "")); - // app 版本名 - 主要用于对用户显示版本信息 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_version_name, appInfoItem.appInfoBean.getVersionName())); - // 安装包地址 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_apk_uri, apkUri)); - // 没报错才存储 SHA 信息 - if (!isError) { - // app SHA1 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_sha1, sha1)); - // app SHA256. - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_sha256, sha256)); - } - // app 最低支持版本 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_minsdkversion, minSdkVersion + " ( " + AppCommonUtils.convertSDKVersion(minSdkVersion) + "+ )")); - // app 兼容sdk版本 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_targetsdkversion, targetSdkVersion + " ( " + AppCommonUtils.convertSDKVersion(targetSdkVersion) + "+ )")); - // 获取 apk 大小 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_apk_length, apkLength)); - // 没报错才存储 其他签名信息 - if (!isError) { - appInfoItem.listKeyValues.addAll(listTemps); - } - // 返回实体类 - return appInfoItem; - } - - public String getApkUri() { - return apkUri; - } - - public AppInfoBean getAppInfoBean() { - return appInfoBean; - } - - public ArrayList getListKeyValues() { - return listKeyValues; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoBean.java b/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoBean.java deleted file mode 100644 index a520efaa7f..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoBean.java +++ /dev/null @@ -1,236 +0,0 @@ -package dev.utils.app.info; - -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.graphics.drawable.Drawable; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; -import dev.utils.common.FileUtils; - -/** - * detail: app 信息实体类 - * Created by Ttt - */ -public class AppInfoBean { - - // https://my.oschina.net/orgsky/blog/368768 - - // 日志Tag - private static final String TAG = AppInfoBean.class.getSimpleName(); - // app 包名 - private String appPackName; - // app 名 - private String appName; - // app 图标 - private transient Drawable appIcon; - // App 类型 - private AppType appType; - // 获取版本号 - private int versionCode; - // 获取版本名 - private String versionName; - // app 首次安装时间 - private long firstInstallTime; - // 获取最后一次更新时间 - private long lastUpdateTime; - // 获取 app 地址 - private String sourceDir; - // APK 大小 - private long apkSize; - // 申请的权限 - private String [] apkPermissionsArys; - - /** - * 通过 apk路径 初始化 App 信息实体类 - * @param apkUri apk路径 - */ - public static AppInfoBean obtainUri(String apkUri){ - try { - // https://blog.csdn.net/sljjyy/article/details/17370665 - PackageManager pManager = DevUtils.getApplication().getPackageManager(); - PackageInfo pInfo = pManager.getPackageArchiveInfo(apkUri, PackageManager.GET_ACTIVITIES); - // = 设置 apk 位置信息 = - ApplicationInfo appInfo = pInfo.applicationInfo; - /* 必须加这两句,不然下面icon获取是default icon而不是应用包的icon */ - appInfo.sourceDir = apkUri; - appInfo.publicSourceDir = apkUri; - return new AppInfoBean(pInfo, pManager); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "obtainUri"); - } - return null; - } - - /** - * 通过包名 初始化 App 信息实体类 - * @param pckName 包名 - */ - public static AppInfoBean obtainPck(String pckName){ - try { - // https://blog.csdn.net/sljjyy/article/details/17370665 - PackageManager pManager = DevUtils.getApplication().getPackageManager(); - // 获取对应的PackageInfo(原始的PackageInfo 获取 signatures 等于null,需要这样获取) - PackageInfo pInfo = pManager.getPackageInfo(pckName, PackageManager.GET_SIGNATURES); // 64 - // 返回app信息 - return new AppInfoBean(pInfo, pManager); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "obtainPck"); - } - return null; - } - - /** - * 初始化当前 App 信息实体类 - */ - public static AppInfoBean obtain(){ - try { - // https://blog.csdn.net/sljjyy/article/details/17370665 - PackageManager pManager = DevUtils.getApplication().getPackageManager(); - // 获取对应的PackageInfo(原始的PackageInfo 获取 signatures 等于null,需要这样获取) - PackageInfo pInfo = pManager.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_SIGNATURES); // 64 - // 返回app信息 - return new AppInfoBean(pInfo, pManager); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "obtain"); - } - return null; - } - - /** - * 初始化 App 信息实体类 - * @param pInfo - * @param pManager - */ - public AppInfoBean(PackageInfo pInfo, PackageManager pManager){ - // app 包名 - appPackName = pInfo.applicationInfo.packageName; - // app 名 - appName = pManager.getApplicationLabel(pInfo.applicationInfo).toString(); - // app 图标 - appIcon = pManager.getApplicationIcon(pInfo.applicationInfo); - // 获取App 类型 - appType = AppInfoBean.getAppType(pInfo); - // 获取版本号 - versionCode = pInfo.versionCode; - // 获取版本名 - versionName = pInfo.versionName; - // app 首次安装时间 - firstInstallTime = pInfo.firstInstallTime; - // 获取最后一次更新时间 - lastUpdateTime = pInfo.lastUpdateTime; - // 获取 app 地址 - sourceDir = pInfo.applicationInfo.sourceDir; - // 获取 APK 大小 - apkSize = FileUtils.getFileLength(sourceDir); - try { - // 获取权限 - apkPermissionsArys = pInfo.requestedPermissions; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "AppInfoBean"); - } - } - - /** - * 获取App 包名 - * @return - */ - public String getAppPackName() { - return appPackName; - } - - /** - * 获取App 名 - * @return - */ - public String getAppName() { - return appName; - } - - /** - * 获取App 图标 - * @return - */ - public Drawable getAppIcon() { - return appIcon; - } - - /** - * 获取 App 类型 - * @return - */ - public AppType getAppType() { - return appType; - } - - // = - - /** App 类型 */ - public enum AppType { - - USER, // 用户 App - - SYSTEM, // 系统 App - - ALL, // 全部 App - } - - /** - * 获取App 类型 - * @param pInfo - * @return - */ - public static AppType getAppType(PackageInfo pInfo){ - if (!isSystemApp(pInfo) && !isSystemUpdateApp(pInfo)){ - return AppType.USER; - } - return AppType.SYSTEM; - } - - /** - * 表示系统程序 - * @param pInfo - * @return - */ - public static boolean isSystemApp(PackageInfo pInfo) { - return ((pInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); - } - - /** - * 表示系统程序被手动更新后,也成为第三方应用程序 - * @param pInfo - * @return - */ - public static boolean isSystemUpdateApp(PackageInfo pInfo) { - return ((pInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); - } - - public int getVersionCode() { - return versionCode; - } - - public String getVersionName() { - return versionName; - } - - public long getFirstInstallTime() { - return firstInstallTime; - } - - public long getLastUpdateTime() { - return lastUpdateTime; - } - - public String getSourceDir() { - return sourceDir; - } - - public long getApkSize() { - return apkSize; - } - - public String[] getApkPermissionsArys() { - return apkPermissionsArys; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoItem.java b/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoItem.java deleted file mode 100644 index 1c9b566b21..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoItem.java +++ /dev/null @@ -1,166 +0,0 @@ -package dev.utils.app.info; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.text.TextUtils; -import android.text.format.Formatter; - -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -import java.security.cert.X509Certificate; -import java.text.SimpleDateFormat; -import java.util.ArrayList; - -import dev.DevUtils; -import dev.utils.R; -import dev.utils.app.AppCommonUtils; -import dev.utils.app.SignaturesUtils; -import dev.utils.common.FileUtils; - -/** - * detail: App 信息Item - * Created by Ttt - */ -public final class AppInfoItem { - - // App 信息实体类 - private AppInfoBean appInfoBean; - // App 参数集 - private ArrayList listKeyValues = new ArrayList<>(); - - private AppInfoItem(){ - } - - public static AppInfoItem obtain(String packName) throws Exception { - // 如果包名为null, 则直接不处理 - if (TextUtils.isEmpty(packName)){ - return null; - } - // 获取上下文 - Context context = DevUtils.getContext(); - // 初始化包管理类 - PackageManager pManager = context.getPackageManager(); - // 获取对应的PackageInfo(原始的PackageInfo 获取 signatures 等于null,需要这样获取) - PackageInfo pInfo = pManager.getPackageInfo(packName, PackageManager.GET_SIGNATURES); // 64 - // 初始化实体类 - AppInfoItem appInfoItem = new AppInfoItem(); - // 获取app 信息 - appInfoItem.appInfoBean = new AppInfoBean(pInfo, pManager); - // == 获取 == - // 格式化日期 - SimpleDateFormat dFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - // =========== - // app 签名MD5 - String md5 = SignaturesUtils.signatureMD5(pInfo.signatures); - // app SHA1 - String sha1 = SignaturesUtils.signatureSHA1(pInfo.signatures); - // app SHA256 - String sha256 = SignaturesUtils.signatureSHA256(pInfo.signatures); - // app 首次安装时间 - String firstInstallTime = dFormat.format(pInfo.firstInstallTime); - // 获取最后一次更新时间 - String lastUpdateTime = dFormat.format(pInfo.lastUpdateTime); - // app 最低支持版本 - int minSdkVersion = -1; - // 属于7.0以上才有的方法 - if (AppCommonUtils.isN()){ - minSdkVersion = pInfo.applicationInfo.minSdkVersion; - } - // app 兼容sdk版本 - int targetSdkVersion = pInfo.applicationInfo.targetSdkVersion; - // 获取 app 安装包大小 - String apkLength = Formatter.formatFileSize(DevUtils.getContext(), FileUtils.getFileLength(appInfoItem.appInfoBean.getSourceDir())); - // 获取证书对象 - X509Certificate cert = SignaturesUtils.getX509Certificate(pInfo.signatures); - // 设置有效期 - StringBuilder sbEffective = new StringBuilder(); - sbEffective.append(dFormat.format(cert.getNotBefore())); - sbEffective.append(" " + context.getString(R.string.dev_str_to) + " "); // 至 - sbEffective.append(dFormat.format(cert.getNotAfter())); - sbEffective.append("\n\n"); - sbEffective.append(cert.getNotBefore()); - sbEffective.append(" " + context.getString(R.string.dev_str_to) + " "); - sbEffective.append(cert.getNotAfter()); - // 获取有效期 - String effective = sbEffective.toString(); - // 证书是否过期 true = 过期,false = 未过期 - boolean isEffective = false; - try { - cert.checkValidity(); - // CertificateExpiredException - 如果证书已过期。 - // CertificateNotYetValidException - 如果证书不再有效。 - } catch (CertificateExpiredException ce) { - isEffective = true; - } catch (CertificateNotYetValidException ce) { - isEffective = true; - } - // 判断是否过期 - String isEffectiveState = isEffective ? context.getString(R.string.dev_str_overdue) : context.getString(R.string.dev_str_notoverdue); - // 证书发布方 - String principal = cert.getIssuerX500Principal().toString(); - // 证书版本号 - String version = cert.getVersion() + ""; - // 证书算法名称 - String sigalgname = cert.getSigAlgName(); - // 证书算法OID - String sigalgoid = cert.getSigAlgOID(); - // 证书机器码 - String serialnumber = cert.getSerialNumber().toString(); - // 证书 DER编码 - String dercode = SignaturesUtils.toHexString(cert.getTBSCertificate()); - - // ================ - // === 保存集合 === - // ================ - // app 包名 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_packname, appInfoItem.appInfoBean.getAppPackName())); - // app 签名MD5 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_md5, md5)); - // app 版本号 - 主要用于app内部版本判断 int 类型 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_version_code, appInfoItem.appInfoBean.getVersionCode() + "")); - // app 版本名 - 主要用于对用户显示版本信息 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_version_name, appInfoItem.appInfoBean.getVersionName())); - // app SHA1 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_sha1, sha1)); - // app SHA256. - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_sha256, sha256)); - // app 首次安装时间 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_first_install_time, firstInstallTime)); - // 获取最后一次更新时间 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_last_update_time, lastUpdateTime)); - // app 最低支持版本 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_minsdkversion, minSdkVersion + " ( " + AppCommonUtils.convertSDKVersion(minSdkVersion) + "+ )")); - // app 兼容sdk版本 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_targetsdkversion, targetSdkVersion + " ( " + AppCommonUtils.convertSDKVersion(targetSdkVersion) + "+ )")); - // 获取 apk 大小 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_apk_length, apkLength)); - // 获取有效期 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_effective, effective)); - // 判断是否过期 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_iseffective, isEffectiveState)); - // 证书发布方 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_principal, principal)); - // 证书版本号 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_version, version)); - // 证书算法名称 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_sigalgname, sigalgname)); - // 证书算法OID - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_sigalgoid, sigalgoid)); - // 证书机器码 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_dercode, serialnumber)); - // 证书 DER编码 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_serialnumber, dercode)); - - // 返回实体类 - return appInfoItem; - } - - public AppInfoBean getAppInfoBean() { - return appInfoBean; - } - - public ArrayList getListKeyValues() { - return listKeyValues; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoUtils.java b/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoUtils.java deleted file mode 100644 index dcf08fc0e5..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoUtils.java +++ /dev/null @@ -1,200 +0,0 @@ -package dev.utils.app.info; - -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PermissionGroupInfo; -import android.content.pm.PermissionInfo; - -import java.util.ArrayList; -import java.util.List; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: App 信息获取工具类 - * Created by Ttt - */ -public final class AppInfoUtils { - - private AppInfoUtils(){ - } - - // 日志Tag - private static final String TAG = AppInfoUtils.class.getSimpleName(); - - /** - * 通过 apk路径 初始化 App 信息实体类 - * @param apkUri apk路径 - */ - public static AppInfoBean obtainUri(String apkUri){ - return AppInfoBean.obtainUri(apkUri); - } - - /** - * 通过包名 初始化 App 信息实体类 - * @param pckName 包名 - */ - public static AppInfoBean obtainPck(String pckName){ - return AppInfoBean.obtainPck(pckName); - } - - /** - * 初始化当前 App 信息实体类 - */ - public static AppInfoBean obtain(){ - return AppInfoBean.obtain(); - } - - // ================== - // == 获取详细信息 == - // ================== - - /** - * 获取 apk 详细信息 - * @param apkUri - * @return - */ - public static ApkInfoItem getApkInfoItem(String apkUri){ - try { - return ApkInfoItem.obtain(apkUri); - } catch (Exception e){ - return null; - } - } - - /** - * 获取 app 详细信息 - * @param pckName - * @return - */ - public static AppInfoItem getAppInfoItem(String pckName){ - try { - return AppInfoItem.obtain(pckName); - } catch (Exception e){ - return null; - } - } - - /** - * 获取 app 详细信息 - * @return - */ - public static AppInfoItem getAppInfoItem(){ - return getAppInfoItem(DevUtils.getContext().getPackageName()); - } - - // = - - /** - * 获取全部App 列表 - * @return - */ - public static ArrayList getAppLists() { - return getAppLists(AppInfoBean.AppType.ALL); - } - - /** - * 获取 App 列表 - * @param appType app类型 - * @return - */ - public static ArrayList getAppLists(AppInfoBean.AppType appType) { - // App信息 - ArrayList listApps = new ArrayList<>(); - // 防止为null - if (appType != null) { - // 管理应用程序包 - PackageManager pManager = DevUtils.getContext().getPackageManager(); - // 获取手机内所有应用 - List packlist = pManager.getInstalledPackages(0); - // 判断是否属于添加全部 - if (appType == AppInfoBean.AppType.ALL){ - // 遍历 app 列表 - for (int i = 0, len = packlist.size(); i < len; i++) { - PackageInfo pInfo = packlist.get(i); - // 添加符合条件的 App 应用信息 - listApps.add(new AppInfoBean(pInfo, pManager)); - } - } else { - // 遍历 app 列表 - for (int i = 0, len = packlist.size(); i < len; i++) { - PackageInfo pInfo = packlist.get(i); - // 获取app 类型 - AppInfoBean.AppType cAppType = AppInfoBean.getAppType(pInfo); - // 判断类型 - if (appType == cAppType){ - // 添加符合条件的 App 应用信息 - listApps.add(new AppInfoBean(pInfo, pManager)); - } -// // 判断类型 -// switch (cAppType) { -// case USER: -// // 添加符合条件的 App 应用信息 -// listApps.add(new AppInfoBean(pInfo, pManager)); -// break; -// case SYSTEM: -// // 添加符合条件的 App 应用信息 -// listApps.add(new AppInfoBean(pInfo, pManager)); -// break; -// } - } - } - } - return listApps; - } - - // = - - /** - * 获取 APK 权限 - * @param pckName - * https://www.cnblogs.com/leaven/p/5485864.html - */ - public static String [] getApkPermission(String pckName){ - try { - PackageManager packageManager = DevUtils.getApplication().getPackageManager(); - PackageInfo packageInfo = packageManager.getPackageInfo(pckName, PackageManager.GET_PERMISSIONS); - return packageInfo.requestedPermissions; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getUsesPermission"); - } - return null; - } - - /** - * 打印 APK 权限 - * @param pckName - * https://www.cnblogs.com/leaven/p/5485864.html - */ - public static void printApkPermission(String pckName){ - try { - PackageManager packageManager = DevUtils.getApplication().getPackageManager(); - PackageInfo packageInfo = packageManager.getPackageInfo(pckName, PackageManager.GET_PERMISSIONS); - String [] usesPermissionsArray = packageInfo.requestedPermissions; - for (int i = 0; i < usesPermissionsArray.length; i++) { - // 获取每个权限的名字,如:android.permission.INTERNET - String usesPermissionName = usesPermissionsArray[i]; - LogPrintUtils.dTag(TAG, "usesPermissionName = " + usesPermissionName); - - // 通过usesPermissionName获取该权限的详细信息 - PermissionInfo permissionInfo = packageManager.getPermissionInfo(usesPermissionName, 0); - - // 获得该权限属于哪个权限组,如:网络通信 - PermissionGroupInfo permissionGroupInfo = packageManager.getPermissionGroupInfo(permissionInfo.group, 0); - LogPrintUtils.dTag(TAG, "permissionGroup = " + permissionGroupInfo.loadLabel(packageManager).toString()); - - // 获取该权限的标签信息,比如:完全的网络访问权限 - String permissionLabel = permissionInfo.loadLabel(packageManager).toString(); - LogPrintUtils.dTag(TAG, "permissionLabel = " + permissionLabel); - - // 获取该权限的详细描述信息,比如:允许该应用创建网络套接字和使用自定义网络协议 - // 浏览器和其他某些应用提供了向互联网发送数据的途径,因此应用无需该权限即可向互联网发送数据. - String permissionDescription = permissionInfo.loadDescription(packageManager).toString(); - LogPrintUtils.dTag(TAG, "permissionDescription = " + permissionDescription); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "printApkPermission"); - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/info/KeyValueBean.java b/DevLibUtils/src/main/java/dev/utils/app/info/KeyValueBean.java deleted file mode 100644 index b02baae48a..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/info/KeyValueBean.java +++ /dev/null @@ -1,44 +0,0 @@ -package dev.utils.app.info; - -import android.support.annotation.StringRes; - -import dev.DevUtils; - -/** - * detail: 键对值 实体类 - * Created by Ttt - */ -public class KeyValueBean { - - // 键 - 提示 - protected String key = ""; - // 值 - 参数值 - protected String value = ""; - - /** - * 构造函数 - * @param key - * @param value - */ - public KeyValueBean(String key, String value) { - this.key = key; - this.value = value; - } - - public String getKey() { - return key; - } - - public String getValue() { - return value; - } - - @Override - public String toString() { - return key + ": " + value; - } - - public static KeyValueBean get(@StringRes int resId, String value) { - return new KeyValueBean(DevUtils.getContext().getString(resId), value); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/DevLogger.java b/DevLibUtils/src/main/java/dev/utils/app/logger/DevLogger.java deleted file mode 100644 index 7138344a7d..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/DevLogger.java +++ /dev/null @@ -1,246 +0,0 @@ -package dev.utils.app.logger; - -/** - * detail: 日志操作类(对外公开直接调用) - * Created by Ttt - */ -public final class DevLogger { - - private DevLogger() { - } - - /** 包下LoggerPrinter类持有对象 */ - private static final IPrinter printer = new LoggerPrinter(); - - // -- 配置方法 -- - - /** - * 使用单次其他日志配置 - * @param lConfig - * @return - */ - public static IPrinter other(LogConfig lConfig) { - return printer.other(lConfig); - } - - /** - * 获取日志配置信息 - * @return - */ - public static LogConfig getLogConfig() { - return printer.getLogConfig(); - } - - /** - * 初始化日志配置信息(可以不调用,使用了App默认配置) - * @return - */ - public static LogConfig init() { - return printer.init(); - } - - /** - * 手动改变日志配置信息(非单次,一直持续) - * @param lConfig - */ - public static void init(LogConfig lConfig) { - printer.init(lConfig); - } - - // ========= 使用默认TAG ========= - - // -- 日志打印方法 -- - /** - * Log.DEBUG - * @param message - * @param args - */ - public static void d(String message, Object... args) { - printer.d(message, args); - } - - /** - * Log.ERROR - * @param message - * @param args - */ - public static void e(String message, Object... args) { - printer.e(message, args); - } - - /** - * Log.ERROR - * @param throwable - */ - public static void e(Throwable throwable) { - printer.e(throwable, null); - } - - /** - * Log.ERROR - * @param throwable - * @param message - * @param args - */ - public static void e(Throwable throwable, String message, Object... args) { - printer.e(throwable, message, args); - } - - /** - * Log.WARN - * @param message - * @param args - */ - public static void w(String message, Object... args) { - printer.w(message, args); - } - - /** - * Log.INFO - * @param message - * @param args - */ - public static void i(String message, Object... args) { - printer.i(message, args); - } - - /** - * Log.VERBOSE - * @param message - * @param args - */ - public static void v(String message, Object... args) { - printer.v(message, args); - } - - /** - * Log.ASSERT - * @param message - * @param args - */ - public static void wtf(String message, Object... args) { - printer.wtf(message, args); - } - - // -- 其他特殊方法 -- - - /** - * 格式化Json格式数据,并打印 - * @param json - */ - public static void json(String json) { - printer.json(json); - } - - /** - * 格式化XML格式数据,并打印 - * @param xml - */ - public static void xml(String xml) { - printer.xml(xml); - } - - - // ========= 使用自定义TAG ========= - - // -- 日志打印方法 -- - /** - * Log.DEBUG - * @param tag - * @param message - * @param args - */ - public static void dTag(String tag, String message, Object... args) { - printer.dTag(tag, message, args); - } - - /** - * Log.ERROR - * @param tag - * @param message - * @param args - */ - public static void eTag(String tag, String message, Object... args) { - printer.eTag(tag, message, args); - } - - /** - * Log.ERROR - * @param tag - * @param throwable - * @param message - * @param args - */ - public static void eTag(String tag, Throwable throwable, String message, Object... args) { - printer.eTag(tag, throwable, message, args); - } - - /** - * Log.ERROR - * @param tag - * @param throwable - */ - public static void eTag(String tag, Throwable throwable) { - printer.eTag(tag, throwable, null); - } - - /** - * Log.WARN - * @param tag - * @param message - * @param args - */ - public static void wTag(String tag, String message, Object... args) { - printer.wTag(tag, message, args); - } - - /** - * Log.INFO - * @param tag - * @param message - * @param args - */ - public static void iTag(String tag, String message, Object... args) { - printer.iTag(tag, message, args); - } - - /** - * Log.VERBOSE - * @param tag - * @param message - * @param args - */ - public static void vTag(String tag, String message, Object... args) { - printer.vTag(tag, message, args); - } - - /** - * Log.ASSERT - * @param tag - * @param message - * @param args - */ - public static void wtfTag(String tag, String message, Object... args) { - printer.wtfTag(tag, message, args); - } - - // -- 其他特殊方法 -- - - /** - * 格式化Json格式数据,并打印 - * @param tag - * @param json - */ - public static void jsonTag(String tag, String json) { - printer.jsonTag(tag, json); - } - - /** - * 格式化XML格式数据,并打印 - * @param tag - * @param xml - */ - public static void xmlTag(String tag, String xml) { - printer.xmlTag(tag, xml); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java b/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java deleted file mode 100644 index 1e484bcc81..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java +++ /dev/null @@ -1,224 +0,0 @@ -package dev.utils.app.logger; - -import android.content.Context; -import android.text.TextUtils; - -import java.io.File; - -import dev.utils.LogPrintUtils; - -/** - * detail: 日志操作工具类 - * Created by Ttt - */ -public final class DevLoggerUtils { - - private DevLoggerUtils(){ - } - - // 日志TAG - private static final String TAG = DevLoggerUtils.class.getSimpleName(); - - /** - * App初始化调用方法(获取版本号) - * @param context 上下文 - */ - public static void appInit(Context context) { - // 保存APP版本信息 - Utils.appInit(context); - } - - // ================= 内部快速初始化LogConfig ================== - - /** - * 获取发布Log配置(打印线程信息,显示方法总数3,从0开始,不进行排序, 默认属于ERROR级别日志) - * @param tag - * @return - */ - public static LogConfig getReleaseLogConfig(String tag) { - return getLogConfig(tag, 3, 0, false, true, false, LogLevel.ERROR); - } - - /** - * 获取发布Log配置(打印线程信息,显示方法总数3,从0开始,不进行排序) - * @param tag - * @param lLevel 日志级别(分四种) - LogLevel - * @return - */ - public static LogConfig getReleaseLogConfig(String tag, LogLevel lLevel) { - return getLogConfig(tag, 3, 0, false, true, false, lLevel); - } - - // -- - - /** - * 获取调试Log配置(打印线程信息,显示方法总数3,从0开始,不进行排序, 默认属于ERROR级别日志) - * @param tag - * @return - */ - public static LogConfig getDebugLogConfig(String tag) { - return getLogConfig(tag, 3, 0, false, true, false, LogLevel.DEBUG); - } - - /** - * 获取调试Log配置(打印线程信息,显示方法总数3,从0开始,进行排序) - * @param tag - * @param lLevel 日志级别(分四种) - LogLevel - * @return - */ - public static LogConfig getDebugLogConfig(String tag, LogLevel lLevel) { - return getLogConfig(tag, 3, 0, false, true, false, lLevel); - } - - // -- - - /** - * 获取Log配置(打印线程信息,显示方法总数3,从0开始,并且美化日志信息, 默认属于DEBUG级别日志) - * @param tag 日志Tag - * @return - */ - public static LogConfig getSortLogConfig(String tag) { - return getLogConfig(tag, 3, 0, false, true, true, LogLevel.DEBUG); - } - - /** - * 获取Log配置(打印线程信息,显示方法总数3,从0开始,并且美化日志信息) - * @param tag 日志Tag - * @param lLevel 日志级别(分四种) - LogLevel - * @return - */ - public static LogConfig getSortLogConfig(String tag, LogLevel lLevel) { - return getLogConfig(tag, 3, 0, false, true, true, lLevel); - } - - // -- - - /** - * 获取Log配置 - * @param tag 日志Tag - * @param mCount 显示的方法总数(推荐3) - * @param mOffset 方法偏移索引(从第几个方法开始打印,默认推荐0) - * @param mAll 是否打印全部方法 - * @param tInfo 是否显示线程信息 - * @param isSortLog 是否排序日志(美化) - * @param lLevel 日志级别(分四种) - LogLevel - * @return - */ - public static LogConfig getLogConfig(String tag, int mCount, int mOffset, boolean mAll, boolean tInfo, boolean isSortLog, LogLevel lLevel) { - // 生成默认配置信息 - LogConfig logConfig = new LogConfig(); - // 堆栈方法总数(显示经过的方法) - logConfig.methodCount = mCount; - // 堆栈方法索引偏移(0 = 最新经过调用的方法信息,偏移则往上推,如 1 = 倒数第二条经过调用的方法信息) - logConfig.methodOffset = mOffset; - // 是否输出全部方法(在特殊情况下,如想要打印全部经过的方法,但是不知道经过的总数) - logConfig.isOutputMethodAll = mAll; - // 显示日志线程信息(特殊情况,显示经过的线程信息,具体情况如上) - logConfig.isDisplayThreadInfo = tInfo; - // 是否排序日志(格式化) - logConfig.isSortLog = isSortLog; - // 日志级别 - logConfig.logLevel = lLevel; - // 设置Tag(特殊情况使用,不使用全部的Tag时,如单独输出在某个Tag下) - logConfig.tag = tag; - // 返回日志配置 - return logConfig; - } - - // =============== 错误日志处理 ================== - - /** - * 保存app错误日志 - * @param ex 错误信息 - * @param fPath 保存路径 + 文件名(含后缀) - * @param isNewLines 是否换行 - * @param eHint 错误提示(无设备信息、失败信息获取失败) - */ - public static boolean saveErrorLog(Throwable ex, String fPath, boolean isNewLines, String... eHint) { - return saveErrorLog(ex,null, null, fPath, isNewLines, eHint); - } - - /** - * 保存app错误日志 - * @param ex 错误信息 - * @param head 顶部标题 - * @param bottom 底部内容 - * @param fPath 保存路径 + 文件名(含后缀) - * @param isNewLines 是否换行 - * @param eHint 错误提示(无设备信息、失败信息获取失败) - */ - public static boolean saveErrorLog(Throwable ex, String head, String bottom, String fPath, boolean isNewLines, String... eHint) { - if(TextUtils.isEmpty(fPath)) { - return false; - } - try { - File file = new File(fPath); - // 获取文件名 - String fName = file.getName(); - // 判断是否这个文件名结尾 - if(fPath.endsWith(fName)) { - // 重新裁剪 - fPath = fPath.substring(0, fPath.length() - fName.length()); - // 进行保存 - return saveErrorLog(ex, head, bottom, fPath, fName, isNewLines, eHint); - } else { - // 进行保存 - return saveErrorLog(ex, head, bottom, fPath, fPath, isNewLines, eHint); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "saveErrorLog"); - } - return false; - } - - /** - * 保存app错误日志 - * @param ex 错误信息 - * @param head 顶部标题 - * @param bottom 底部内容 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param isNewLines 是否换行 - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveErrorLog(Throwable ex, String head, String bottom, String fPath, String fName, boolean isNewLines, String... eHint) { - if(TextUtils.isEmpty(fPath)) { - return false; - } else if (TextUtils.isEmpty(fName)) { - return false; - } - return Utils.saveErrorLog(ex, head, bottom, fPath, fName, isNewLines, eHint); - } - - /** - * 保存app日志 - * @param log 日志信息 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveLog(String log, String fPath, String fName, String... eHint){ - return saveLog(log, null, null, fPath, fName, eHint); - } - - /** - * 保存app日志 - * @param log 日志信息 - * @param head 顶部标题 - * @param bottom 底部内容 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveLog(String log, String head, String bottom, String fPath, String fName, String... eHint) { - if(TextUtils.isEmpty(fPath)) { - return false; - } else if (TextUtils.isEmpty(fName)) { - return false; - } - return Utils.saveLog(log, head, bottom, fPath, fName, eHint); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/IPrinter.java b/DevLibUtils/src/main/java/dev/utils/app/logger/IPrinter.java deleted file mode 100644 index 13ef2528d6..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/IPrinter.java +++ /dev/null @@ -1,109 +0,0 @@ -package dev.utils.app.logger; - -/** - * detail: 日志接口 - * Created by Ttt - */ -public interface IPrinter { - - // ==================== - // ----- 配置方法 ----- - // ==================== - - /** - * 使用单次其他日志配置 - * @param lConfig 日志配置 - * @return - */ - IPrinter other(LogConfig lConfig); - - /** - * 获取日志配置信息 - * @return 日志配置 - */ - LogConfig getLogConfig(); - - /** - * 初始化日志配置信息(可以不调用,使用了App默认配置) - * @return 日志配置 - */ - LogConfig init(); - - /** - * 手动改变日志配置信息(非单次,一直持续) - * @param lConfig 日志配置 - */ - void init(LogConfig lConfig); - - // ========= 使用默认TAG ========= - - // -- 日志打印方法 -- - - /** Log.DEBUG */ - void d(String message, Object... args); - - /** Log.ERROR */ - void e(String message, Object... args); - - /** Log.ERROR,并且输出错误信息Throwable */ - void e(Throwable throwable); - - /** Log.ERROR,并且输出错误信息Throwable */ - void e(Throwable throwable, String message, Object... args); - - /** Log.WARN */ - void w(String message, Object... args); - - /** Log.INFO */ - void i(String message, Object... args); - - /** Log.VERBOSE */ - void v(String message, Object... args); - - /** Log.ASSERT */ - void wtf(String message, Object... args); - - // -- 其他特殊方法 -- - - /** 格式化Json格式数据,并打印 */ - void json(String json); - - /** 格式化xml格式数据,并打印 */ - void xml(String xml); - - // ========= 使用自定义TAG ========= - - // -- 日志打印方法 -- - - /** Log.DEBUG */ - void dTag(String tag, String message, Object... args); - - /** Log.ERROR */ - void eTag(String tag, String message, Object... args); - - /** Log.ERROR,并且输出错误信息Throwable */ - void eTag(String tag, Throwable throwable); - - /** Log.ERROR,并且输出错误信息Throwable */ - void eTag(String tag, Throwable throwable, String message, Object... args); - - /** Log.WARN */ - void wTag(String tag, String message, Object... args); - - /** Log.INFO */ - void iTag(String tag, String message, Object... args); - - /** Log.VERBOSE */ - void vTag(String tag, String message, Object... args); - - /** Log.ASSERT */ - void wtfTag(String tag, String message, Object... args); - - // -- 其他特殊方法 -- - - /** 格式化Json格式数据,并打印 */ - void jsonTag(String tag, String json); - - /** 格式化xml格式数据,并打印 */ - void xmlTag(String tag, String xml); -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/LogConfig.java b/DevLibUtils/src/main/java/dev/utils/app/logger/LogConfig.java deleted file mode 100644 index 609ab48fac..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/LogConfig.java +++ /dev/null @@ -1,29 +0,0 @@ -package dev.utils.app.logger; - -/** - * detail: 日志设置类 - * Created by Ttt - */ -public class LogConfig { - - /** 堆栈方法总数(显示经过的方法) = 默认 3 */ - public int methodCount = LogConstants.DEFAULT_LOG_METHOD_COUNT; - - /** 堆栈方法索引偏移(0 = 最新经过调用的方法信息,偏移则往上推,如 1 = 倒数第二条经过调用的方法信息) = 默认 0 */ - public int methodOffset = LogConstants.DEFAULT_LOG_METHOD_OFFSET; - - /** 是否输出全部方法(在特殊情况下,如想要打印全部经过的方法,但是不知道经过的总数) = 默认 false */ - public boolean isOutputMethodAll = LogConstants.JUDGE_OUTPUT_METHOD_ALL; - - /** 显示日志线程信息(特殊情况,显示经过的线程信息,具体情况如上) = 默认 false */ - public boolean isDisplayThreadInfo = LogConstants.JUDGE_DISPLAY_THREAD_LOG; - - /** 是否排序日志(格式化) = 默认 false */ - public boolean isSortLog = LogConstants.JUDGE_SORT_LOG; - - /** 日志级别 - 默认异常级别(只有 e,wtf 才进行显示) */ - public LogLevel logLevel = LogConstants.DEFAULT_LOG_LEVEL; - - /** 设置Tag特殊情况使用,不使用全部的Tag时,如单独输出在某个Tag下) */ - public String tag = LogConstants.DEFAULT_LOG_TAG; -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/LogConstants.java b/DevLibUtils/src/main/java/dev/utils/app/logger/LogConstants.java deleted file mode 100644 index ebddbc41d0..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/LogConstants.java +++ /dev/null @@ -1,62 +0,0 @@ -package dev.utils.app.logger; - -/** - * detail: 日志常量类 - * Created by Ttt - */ -final class LogConstants { - - // ======================= 日志配置常量 ============================ - - /** 判断是否排序日志 */ - public static final boolean JUDGE_SORT_LOG = false; - - /** 判断是否输出全部方法 - 异常的全部方法 */ - public static final boolean JUDGE_OUTPUT_METHOD_ALL = false; - - /** 判断是否显示日志线程信息 */ - public static final boolean JUDGE_DISPLAY_THREAD_LOG = false; - - /** 默认的日志Tag */ - public static final String DEFAULT_LOG_TAG = "DevLogger"; - - /** 默认输出方法数量 */ - public static final int DEFAULT_LOG_METHOD_COUNT = 3; - - /** 默认方法索引偏移 */ - public static final int DEFAULT_LOG_METHOD_OFFSET = 0; - - /** 默认日志级别 - 异常级别(只有 e,wtf 才进行显示) */ - public static final LogLevel DEFAULT_LOG_LEVEL = LogLevel.ERROR; - - // =================== 日志配置信息 ======================= - - /** - * Android's max limit for a log entry is ~4076 bytes, - * so 4000 bytes is used as chunk size since default charset is UTF-8 - * ---------------------------------------- - * Android的最大限制为一个日志条目 4076字节,4000字节作为块的大小从默认字符集是UTF-8 - */ - public static final int CHUNK_SIZE = 4000; - - /** JSON格式内容缩进 */ - public static final int JSON_INDENT = 4; - - /** 最小堆栈跟踪索引,在本类中启动2次本地调用。 */ - public static final int MIN_STACK_OFFSET = 3; - - // ========================= - // == 绘制日志工具 - 字符 == - // ========================= - - public static final char TOP_LEFT_CORNER = '╔'; - public static final char BOTTOM_LEFT_CORNER = '╚'; - public static final char MIDDLE_CORNER = '╟'; - public static final char HORIZONTAL_DOUBLE_LINE = '║'; - public static final String DOUBLE_DIVIDER = "═══════"; - public static final String SINGLE_DIVIDER = "───────"; - // -- - public static final String TOP_BORDER = TOP_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER; - public static final String BOTTOM_BORDER = BOTTOM_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER; - public static final String MIDDLE_BORDER = MIDDLE_CORNER + SINGLE_DIVIDER + SINGLE_DIVIDER; -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/LogLevel.java b/DevLibUtils/src/main/java/dev/utils/app/logger/LogLevel.java deleted file mode 100644 index 788a21369f..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/LogLevel.java +++ /dev/null @@ -1,31 +0,0 @@ -package dev.utils.app.logger; - -/** - * detail: 日志级别 - * Created by Ttt - */ -public enum LogLevel { - - /** ================================================================== */ - // Log.v 输出颜色为黑色,任何消息都会输出,这里的v代表verbose啰嗦的意思,平时使用就是Log.v(,); - // Log.d 输出颜色为蓝色,仅输出debug调试的意思,但他会输出上层的信息,过滤起来可以通过DDMS的Logcat标签来选择 - // Log.i 输出颜色为绿色,一般提示性的消息information,它不会输出Log.v和Log.d的信息,但会显示i、w和e的信息 - // Log.w 输出颜色为橙色,可以看作为warning警告,一般需要我们注意优化Android代码,同时选择它后还会输出Log.e的信息。 - // Log.e 输出颜色为红色,可以想到error错误,这里仅显示红色的错误信息,这些错误就需要我们认真的分析,查看栈的信息了。 - /** ================================================================== */ - - /** 全部不打印 */ - NONE, - - /** 调试级别 v,d - 全部打印 */ - DEBUG, - - /** 正常级别 i */ - INFO, - - /** 警告级别 w */ - WARN, - - /** 异常级别 e,wtf */ - ERROR; -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/LoggerPrinter.java b/DevLibUtils/src/main/java/dev/utils/app/logger/LoggerPrinter.java deleted file mode 100644 index c1b3fa57aa..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/LoggerPrinter.java +++ /dev/null @@ -1,735 +0,0 @@ -package dev.utils.app.logger; - -import android.text.TextUtils; -import android.util.Log; - -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.StringReader; -import java.io.StringWriter; - -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -/** - * detail: 日志输出类(处理方法) - * Created by Ttt - */ -final class LoggerPrinter implements IPrinter { - - /** 日志配置 */ - private static LogConfig LOG_CONFIG = null; - - /** 每个线程的日志配置信息 */ - private static final ThreadLocal LOCAL_LOG_CONFIGS = new ThreadLocal(); - - // ================== 实现IPrinter接口,对外公开方法 =================== - - /** - * 其他(特殊情况,如不使用默认配置,单使用一次自定义配置) - * @param lConfig 对应的配置信息 - */ - @Override - public IPrinter other(LogConfig lConfig) { - if(lConfig != null) { - LOCAL_LOG_CONFIGS.set(lConfig); - } - return this; - } - - /** - * 返回日志配置 - * @return - */ - @Override - public LogConfig getLogConfig() { - return LOG_CONFIG; - } - - /** - * 默认参数初始化,防止设置信息为null - * @return 日志配置 - */ - @Override - public LogConfig init() { - // 判断日志配置信息是否等于null - if(LOG_CONFIG == null) { - // 生成默认配置信息 - LOG_CONFIG = new LogConfig(); - } - // 返回配置信息 - return LOG_CONFIG; - } - - /** - * 自定义日志配置 - * @param lConfig 日志配置 - */ - @Override - public void init(LogConfig lConfig) { - LOG_CONFIG = lConfig; - // 防止日志配置参数为null - init(); - } - - // ========= 使用默认TAG ========= - - // -- 日志打印方法 -- - - @Override - public void d(String message, Object... args) { - logHandle(Log.DEBUG, message, args); - } - - @Override - public void e(String message, Object... args) { - e(null, message, args); - } - - @Override - public void e(Throwable throwable) { - e(throwable, null); - } - - @Override - public void e(Throwable throwable, String message, Object... args) { - if (throwable != null && message != null) { - message += " : " + throwable.toString(); - } else if (throwable != null && message == null) { - message = throwable.toString(); - } else if (message == null) { - // 没有日志信息,也没有异常信息传入 - message = "No message/exception is set"; - } - logHandle(Log.ERROR, message, args); - } - - @Override - public void w(String message, Object... args) { - logHandle(Log.WARN, message, args); - } - - @Override - public void i(String message, Object... args) { - logHandle(Log.INFO, message, args); - } - - @Override - public void v(String message, Object... args) { - logHandle(Log.VERBOSE, message, args); - } - - @Override - public void wtf(String message, Object... args) { - logHandle(Log.ASSERT, message, args); - } - - // -- 其他特殊方法 -- - - @Override - public void json(String json) { - // 获取当前线程日志配置信息 - LogConfig lConfig = getThreadLogConfig(); - // 判断是否打印日志(日志级别) - if(!isPrintLog(lConfig, Log.DEBUG)) { - return; - } - // 日志Tag - String tag = lConfig.tag; - // 判断传入JSON格式信息是否为null - if (TextUtils.isEmpty(json)) { - logHandle(lConfig, tag, Log.DEBUG, "Empty/Null json content"); - return; - } - try { - // 属于对象的JSON格式信息 - if (json.startsWith("{")) { - JSONObject jsonObject = new JSONObject(json); - // 进行缩进 - String message = jsonObject.toString(LogConstants.JSON_INDENT); - // 打印信息 - logHandle(lConfig, tag, Log.DEBUG, message); - } else if (json.startsWith("[")) { - // 属于数据的JSON格式信息 - JSONArray jsonArray = new JSONArray(json); - // 进行缩进 - String message = jsonArray.toString(LogConstants.JSON_INDENT); - // 打印信息 - logHandle(lConfig, tag, Log.DEBUG, message); - } - } catch (Exception e) { - String eHint = "null"; - if (e != null){ - Throwable throwable = e.getCause(); - if (throwable != null){ - eHint = throwable.getMessage(); - } else { - try { - eHint = e.getMessage(); - } catch (Exception e1){ - eHint = e1.getMessage(); - } - } - } - logHandle(lConfig, tag, Log.ERROR, eHint + "\n" + json); - - } - } - - @Override - public void xml(String xml) { - // 获取当前线程日志配置信息 - LogConfig lConfig = getThreadLogConfig(); - // 判断是否打印日志(日志级别) - if(!isPrintLog(lConfig, Log.DEBUG)) { - return; - } - // 日志Tag - String tag = lConfig.tag; - // 判断传入XML格式信息是否为null - if (TextUtils.isEmpty(xml)) { - logHandle(lConfig, tag, Log.DEBUG, "Empty/Null xml content"); - return; - } - try { - Source xmlInput = new StreamSource(new StringReader(xml)); - StreamResult xmlOutput = new StreamResult(new StringWriter()); - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - transformer.transform(xmlInput, xmlOutput); - // 获取打印消息 - String message = xmlOutput.getWriter().toString().replaceFirst(">", ">\n"); - // 打印信息 - logHandle(lConfig, tag, Log.DEBUG, message); - } catch (Exception e) { - String eHint = "null"; - if (e != null){ - Throwable throwable = e.getCause(); - if (throwable != null){ - eHint = throwable.getMessage(); - } else { - try { - eHint = e.getMessage(); - } catch (Exception e1){ - eHint = e1.getMessage(); - } - } - } - logHandle(lConfig, tag, Log.ERROR, eHint + "\n" + xml); - } - } - - // ========= 使用自定义TAG ========= - - // -- 日志打印方法 -- - - @Override - public void dTag(String tag, String message, Object... args) { - logHandle(tag, Log.DEBUG, message, args); - } - - @Override - public void eTag(String tag, String message, Object... args) { - eTag(tag, null, message, args); - } - - @Override - public void eTag(String tag, Throwable throwable) { - eTag(tag, throwable, null); - } - - @Override - public void eTag(String tag, Throwable throwable, String message, Object... args) { - if (throwable != null && message != null) { - message += " : " + throwable.toString(); - } else if (throwable != null && message == null) { - message = throwable.toString(); - } else if (message == null) { - // 没有日志信息,也没有异常信息传入 - message = "No message/exception is set"; - } - logHandle(tag, Log.ERROR, message, args); - } - - @Override - public void wTag(String tag, String message, Object... args) { - logHandle(tag, Log.WARN, message, args); - } - - @Override - public void iTag(String tag, String message, Object... args) { - logHandle(tag, Log.INFO, message, args); - } - - @Override - public void vTag(String tag, String message, Object... args) { - logHandle(tag, Log.VERBOSE, message, args); - } - - @Override - public void wtfTag(String tag, String message, Object... args) { - logHandle(tag, Log.ASSERT, message, args); - } - - // -- 其他特殊方法 -- - - @Override - public void jsonTag(String tag, String json) { - // 获取当前线程日志配置信息 - LogConfig lConfig = getThreadLogConfig(); - // 判断是否打印日志(日志级别) - if(!isPrintLog(lConfig, Log.DEBUG)) { - return; - } - // 判断传入JSON格式信息是否为null - if (TextUtils.isEmpty(json)) { - logHandle(lConfig, tag, Log.DEBUG, "Empty/Null json content"); - return; - } - try { - // 属于对象的JSON格式信息 - if (json.startsWith("{")) { - JSONObject jsonObject = new JSONObject(json); - // 进行缩进 - String message = jsonObject.toString(LogConstants.JSON_INDENT); - // 打印信息 - logHandle(lConfig, tag, Log.DEBUG, message); - } else if (json.startsWith("[")) { - // 属于数据的JSON格式信息 - JSONArray jsonArray = new JSONArray(json); - // 进行缩进 - String message = jsonArray.toString(LogConstants.JSON_INDENT); - // 打印信息 - logHandle(lConfig, tag, Log.DEBUG, message); - } - } catch (Exception e) { - String eHint = "null"; - if (e != null){ - Throwable throwable = e.getCause(); - if (throwable != null){ - eHint = throwable.getMessage(); - } else { - try { - eHint = e.getMessage(); - } catch (Exception e1){ - eHint = e1.getMessage(); - } - } - } - logHandle(lConfig, tag, Log.ERROR, eHint + "\n" + json); - } - } - - @Override - public void xmlTag(String tag, String xml) { - // 获取当前线程日志配置信息 - LogConfig lConfig = getThreadLogConfig(); - // 判断是否打印日志(日志级别) - if(!isPrintLog(lConfig, Log.DEBUG)) { - return; - } - // 判断传入XML格式信息是否为null - if (TextUtils.isEmpty(xml)) { - logHandle(lConfig, tag, Log.DEBUG, "Empty/Null xml content"); - return; - } - try { - Source xmlInput = new StreamSource(new StringReader(xml)); - StreamResult xmlOutput = new StreamResult(new StringWriter()); - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - transformer.transform(xmlInput, xmlOutput); - // 获取打印消息 - String message = xmlOutput.getWriter().toString().replaceFirst(">", ">\n"); - // 打印信息 - logHandle(lConfig, tag, Log.DEBUG, message); - } catch (Exception e) { - String eHint = "null"; - if (e != null){ - Throwable throwable = e.getCause(); - if (throwable != null){ - eHint = throwable.getMessage(); - } else { - try { - eHint = e.getMessage(); - } catch (Exception e1){ - eHint = e1.getMessage(); - } - } - } - logHandle(lConfig, tag, Log.ERROR, eHint + "\n" + xml); - } - } - - // ======================= 内部判断方法 ========================== - - /** - * 是否打印日志 - * @param lConfig 日志配置 - * @param logType 日志类型 - * @return - */ - private boolean isPrintLog(LogConfig lConfig, int logType) { - // 是否打印日志 - 默认不打印 - boolean isPrint = false; - // 日志级别 - LogLevel lLevel = lConfig.logLevel; - // -- - switch(lLevel) { - /** 全部不打印 */ - case NONE: - break; - /** 调试级别 v,d - 全部打印*/ - case DEBUG: - isPrint = true; - break; - /** 正常级别 i */ - case INFO: - /** 警告级别 w */ - case WARN: - /** 异常级别 e,wtf */ - case ERROR: - isPrint = checkLogLevel(lLevel, logType); - break; - default: - break; - } - return isPrint; - } - - /** - * 判断日志级别是否允许输出 - * @param lLevel 日志级别 - * @param logType 日志类型 - * @return - */ - private boolean checkLogLevel(LogLevel lLevel, int logType) { - switch(lLevel) { - /** 正常级别 i */ - case INFO: - if(logType != Log.VERBOSE && logType != Log.DEBUG) { - return true; - } - break; - /** 警告级别 w */ - case WARN: - if(logType != Log.VERBOSE && logType != Log.DEBUG && logType != Log.INFO) { - return true; - } - break; - /** 异常级别 e,wtf */ - case ERROR: - if(logType == Log.ERROR || logType == Log.ASSERT) { - return true; - } - break; - default: - break; - } - return false; - } - - // ======================= 打印日志处理方法 ========================== - /** - * 最终打印方法 f = Final - * @param logType 日志类型 - * @param tag 日志Tag - * @param msg 打印信息 - */ - private void fLogPrinter(int logType, String tag, String msg) { - switch (logType) { - case Log.VERBOSE: - Log.v(tag, msg); - break; - case Log.DEBUG: - Log.d(tag, msg); - break; - case Log.INFO: - Log.i(tag, msg); - break; - case Log.WARN: - Log.w(tag, msg); - break; - case Log.ERROR: - Log.e(tag, msg); - break; - case Log.ASSERT: - Log.wtf(tag, msg); - break; - default: // 默认使用,自定义级别 - Log.wtf(tag, msg); - break; - } - } - - /** - * 日志处理方法(统一调用这个) - * @param logType 日志类型 - * @param msg 打印信息 - * @param args 占位符替换 - */ - private void logHandle(int logType, String msg, Object... args) { - logHandle(null, null, logType, msg, args); - } - - /** - * 日志处理方法(统一调用这个) - * @param tag 日志Tag - * @param logType 日志类型 - * @param msg 打印信息 - * @param args 占位符替换 - */ - private void logHandle(String tag, int logType, String msg, Object... args) { - logHandle(null, tag, logType, msg, args); - } - - /** - * 日志处理方法(统一调用这个) -- 此方法是同步的,以避免混乱的日志的顺序。 - * @param lConfig 配置信息 - * @param tag 日志Tag - * @param logType 日志类型 - * @param msg 打印信息 - * @param args 占位符替换 - */ - private synchronized void logHandle(LogConfig lConfig, String tag, int logType, String msg, Object... args) { - if(lConfig == null) { // 如果配置为null,才进行获取 - // 获取当前线程日志配置信息 - lConfig = getThreadLogConfig(); - } - // 判断是否打印日志(日志级别) - if(!isPrintLog(lConfig, logType)) { - return; - } - // 防止TAG为null - if(TextUtils.isEmpty(tag)) { - // 获取配置的TAG - tag = lConfig.tag; - // 防止配置的TAG也为null - if(TextUtils.isEmpty(tag)) { - // 使用默认的TAG - tag = LogConstants.DEFAULT_LOG_TAG; - } - } - // 判断是否显示排序后的日志(如果不排序,则显示默认) - if(!lConfig.isSortLog) { - fLogPrinter(logType, tag, createMessage(msg, args)); - return; - } - /** ============== 日志配置信息获取 ========== */ - // 获取方法总数 - int methodCount = lConfig.methodCount; - // 获取方法偏移索引 - int methodOffset = lConfig.methodOffset; - // 如果出现小于0的设置,则设置默认值处理 - if(methodOffset < 0) { - methodOffset = LogConstants.DEFAULT_LOG_METHOD_OFFSET; - } - // 如果出现小于0的设置,则设置默认值处理 - if(methodCount < 0) { - methodCount = LogConstants.DEFAULT_LOG_METHOD_COUNT; - } - // 获取打印的日志信息 - String message = createMessage(msg, args); - // 打印头部 - logTopBorder(logType, tag); - // 打印头部线程信息 - logHeaderContent(lConfig,logType, tag, methodCount, methodOffset); - // 获取系统的默认字符集的信息字节(UTF-8) - byte[] bytes = message.getBytes(); - // 获取字节总数 - int length = bytes.length; - // 判断是否超过总数,没有超过则一次性打印,超过则遍历打印 - if (length <= LogConstants.CHUNK_SIZE) { - if (methodCount > 0) { - logDivider(logType, tag); - } - // 打印日志内容 - logContent(logType, tag, message); - // 打印结尾 - logBottomBorder(logType, tag); - return; - } - // 打印换行符 - if (methodCount > 0) { - // 换行 - logDivider(logType, tag); - } - // 因为超过系统打印字节总数,遍历打印 - for (int i = 0; i < length; i += LogConstants.CHUNK_SIZE) { - int count = Math.min(length - i, LogConstants.CHUNK_SIZE); - // 创建系统的默认字符集的一个新的字符串(UTF-8),并打印日志内容 - logContent(logType, tag, new String(bytes, i, count)); - } - // 打印结尾 - logBottomBorder(logType, tag); - } - - // ========================== 日志格式拼接 ========================== - /** - * 日志线程信息主体部分 - * @param lConfig 日志配置 - * @param logType 日志类型 - * @param tag 日志Tag - * @param methodCount 方法总数 - * @param methodOffset 方法偏移索引 - */ - private void logHeaderContent(LogConfig lConfig, int logType, String tag, int methodCount, int methodOffset) { - StackTraceElement[] trace = Thread.currentThread().getStackTrace(); - // 判断是否显示日志线程信息 - if (lConfig.isDisplayThreadInfo) { - // 打印线程信息(线程名) - fLogPrinter(logType, tag, LogConstants.HORIZONTAL_DOUBLE_LINE + " Thread: " + Thread.currentThread().getName()); - // 进行换行 - logDivider(logType, tag); - } else { - // 不打印线程信息,都设置为0 - methodCount = methodOffset = 0; - return; - } - // 手动进行偏移 - String level = ""; - // 堆栈总数 - int traceCount = trace.length; - // 获取堆栈偏移量 - int stackOffset = getStackOffset(trace) + methodOffset; - // 对应的方法计数与当前堆栈可能超过,进行堆栈跟踪。修剪计数 - if (methodCount + stackOffset > traceCount) { - methodCount = traceCount - stackOffset - 1; - } - // 判断是否显示全部方法 - if(lConfig.isOutputMethodAll) { - // 设置方法总数 - methodCount = traceCount; - // 设置方法偏移索引为0 - stackOffset = 0; - } else if (methodCount <= 0) { - // 如果打印数小于等于0,则直接跳过 - return; - } - // 遍历打印的方法数量(类名、行数、操作的方法名) - for (int i = methodCount; i > 0; i--) { - int stackIndex = i + stackOffset; - if (stackIndex >= traceCount) { - continue; - } - // 拼接中间内容,以及操作的类名,行数,方法名等信息 - StringBuilder builder = new StringBuilder(); - builder.append("║ "); - builder.append(level); - builder.append(getSimpleClassName(trace[stackIndex].getClassName())); - builder.append(".").append(trace[stackIndex].getMethodName()); - builder.append(" ("); - builder.append(trace[stackIndex].getFileName()); - builder.append(":"); - builder.append(trace[stackIndex].getLineNumber()); - builder.append(")"); - level += " "; - // 打印日志信息 - fLogPrinter(logType, tag, builder.toString()); - } - } - - /** - * 日志顶部 - * @param logType 日志类型 - * @param tag 日志Tag - */ - private void logTopBorder(int logType, String tag) { - fLogPrinter(logType, tag, LogConstants.TOP_BORDER); - } - - /** - * 日志结尾 - * @param logType 日志类型 - * @param tag 日志Tag - */ - private void logBottomBorder(int logType, String tag) { - fLogPrinter(logType, tag, LogConstants.BOTTOM_BORDER); - } - - /** - * 日志换行 - * @param logType 日志类型 - * @param tag 日志Tag - */ - private void logDivider(int logType, String tag) { - fLogPrinter(logType, tag, LogConstants.MIDDLE_BORDER); - } - - /** - * 日志内容 - * @param logType 日志类型 - * @param tag 日志Tag - * @param msg 日志信息 - */ - private void logContent(int logType, String tag, String msg) { - String[] lines = msg.split(System.getProperty("line.separator")); - for (String line : lines) { - fLogPrinter(logType, tag, LogConstants.HORIZONTAL_DOUBLE_LINE + " " + line); - } - } - - /** - * 处理信息 - * @param message 打印信息 - * @param args 占位符替换 - * @return - */ - private String createMessage(String message, Object... args) { - try { - return args.length == 0 ? message : String.format(message, args); - } catch (Exception e) { - } - return ""; - } - - // =================== 获取堆栈信息 ============================ - /** - * 获取类名 - * @param name 类名.class - */ - private String getSimpleClassName(String name) { - int lastIndex = name.lastIndexOf("."); - return name.substring(lastIndex + 1); - } - - /** - * 确定该类的方法调用后的堆栈跟踪的起始索引。 - * @param trace 堆栈 - * @return 堆栈跟踪索引 - */ - private int getStackOffset(StackTraceElement[] trace) { - for (int i = LogConstants.MIN_STACK_OFFSET, len = trace.length; i < len; i++) { - StackTraceElement e = trace[i]; - String name = e.getClassName(); - if (!name.equals(LoggerPrinter.class.getName()) && !name.equals(DevLogger.class.getName())) { - return --i; - } - } - return -1; - } - - // ========================== 日志配置获取 ========================== - /** - * 返回对应线程的日志配置信息 - */ - private LogConfig getThreadLogConfig() { - // 获取当前线程的日志配置信息 - LogConfig lConfig = LOCAL_LOG_CONFIGS.get(); - // 如果等于null,则返回默认配置信息 - if(lConfig == null) { - return init(); - } else { - LOCAL_LOG_CONFIGS.remove(); - } - // 如果存在当前线程的配置信息,则返回 - return lConfig; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/Utils.java b/DevLibUtils/src/main/java/dev/utils/app/logger/Utils.java deleted file mode 100644 index b71e4a0afc..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/Utils.java +++ /dev/null @@ -1,488 +0,0 @@ -package dev.utils.app.logger; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.Build; -import android.text.TextUtils; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.reflect.Field; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import dev.utils.LogPrintUtils; - -/** - * detail: 内部快捷操作工具类(便于单独提取Logger,不依赖其他工具类) - * Created by Ttt - */ -final class Utils { - - private Utils(){ - } - - // 日志TAG - private static final String TAG = Utils.class.getSimpleName(); - - // =================== 配置信息 ======================= - - /** App版本(如1.0.01) 显示给用户看的 */ - static String APP_VERSION_NAME = ""; - - /** android:versionCode——整数值,代表应用程序代码的相对版本,也就是版本更新过多少次。(不显示给用户看) */ - static String APP_VERSION_CODE = ""; - - /** 设备信息 */ - static String DEVICE_INFO_STR = null; - - /** 用来存储设备信息 */ - static HashMap DEVICE_INFO_MAPS = new HashMap(); - - /** 换行字符串 */ - static final String NEW_LINE_STR = System.getProperty("line.separator"); - - /** 换行字符串 - 两行 */ - static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; - - // ================== APP、设备信息处理 =================== - - /** - * 获取app版本信息 - * @param context 上下文 - */ - static String[] getAppVersion(Context context) { - String[] aVersion = null; - try { - PackageManager pm = context.getPackageManager(); - PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES); - if (pi != null) { - String versionName = pi.versionName == null ? "null" : pi.versionName; - String versionCode = pi.versionCode + ""; - // -- - aVersion = new String[]{versionName,versionCode}; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getAppVersion"); - } - return aVersion; - } - - /** - * 获取设备信息 - * @param dInfoMaps 传入设备信息传出HashMap - */ - static void getDeviceInfo(HashMap dInfoMaps) { - // 获取设备信息类的所有申明的字段,即包括public、private和proteced, 但是不包括父类的申明字段。 - Field[] fields = Build.class.getDeclaredFields(); - // 遍历字段 - for (Field field : fields) { - try { - // 取消java的权限控制检查 - field.setAccessible(true); - // 获取类型对应字段的数据,并保存 - dInfoMaps.put(field.getName(), field.get(null).toString()); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); - } - } - } - - /** - * 处理设备信息 - * @param eHint 错误提示,如获取设备信息失败 - */ - static String handleDeviceInfo(String eHint) { - try { - // 如果不为null,则直接返回之前的信息 - if(!TextUtils.isEmpty(DEVICE_INFO_STR)) { - return DEVICE_INFO_STR; - } - // 初始化StringBuilder,拼接字符串 - StringBuilder sBuilder = new StringBuilder(); - // 获取设备信息 - Iterator> mapIter = DEVICE_INFO_MAPS.entrySet().iterator(); - // 遍历设备信息 - while (mapIter.hasNext()) { - // 获取对应的key-value - Map.Entry rnEntry = (Map.Entry) mapIter.next(); - String rnKey = (String) rnEntry.getKey(); // key - String rnValue = (String) rnEntry.getValue(); // value - // 保存设备信息 - sBuilder.append(rnKey); - sBuilder.append(" = "); - sBuilder.append(rnValue); - sBuilder.append(NEW_LINE_STR); - } - // 保存设备信息 - DEVICE_INFO_STR = sBuilder.toString(); - // 返回设备信息 - return DEVICE_INFO_STR; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleDeviceInfo"); - } - return eHint; - } - - // ================== 时间格式化 =================== - - /** 日期格式类型 */ - static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; - - /** - * 获取当前日期的字符串 - * @return 字符串 - */ - @SuppressLint("SimpleDateFormat") - static String getDateNow() { - try { - Calendar cld = Calendar.getInstance(); - DateFormat df = new SimpleDateFormat(yyyyMMddHHmmss); - return df.format(cld.getTime()); - } catch (Exception e) { - } - return null; - } - - - // ================== 文件操作 =================== - - /** - * 判断某个文件夹是否创建,未创建则创建(不能加入文件名) - * @param fPath 文件夹路径 - */ - static File createFile(String fPath) { - try { - File file = new File(fPath); - // 当这个文件夹不存在的时候则创建文件夹 - if(!file.exists()) { - // 允许创建多级目录 - file.mkdirs(); - } - return file; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "createFile"); - } - return null; - } - - /** - * 保存文件 - * @param txt 保存内容 - * @param fUrl 保存路径(包含文件名.后缀) - * @return 是否保存成功 - */ - static boolean saveFile(String txt, String fUrl) { - try { - // 保存内容到一个文件 - FileOutputStream fos = new FileOutputStream(fUrl); - fos.write(txt.getBytes()); - fos.close(); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "saveFile"); - return false; - } - return true; - } - - - // ================== 异常信息处理 =================== - - /** - * 获取错误信息(无换行) - * @param eHint 获取失败提示 - * @param ex 错误信息 - * @return - */ - static String getThrowableMsg(String eHint, Throwable ex) { - PrintWriter printWriter = null; - try { - if(ex != null) { - // 初始化Writer,PrintWriter打印流 - Writer writer = new StringWriter(); - printWriter = new PrintWriter(writer); - // 写入错误栈信息 - ex.printStackTrace(printWriter); - // 关闭流 - printWriter.close(); - return writer.toString(); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getThrowableMsg"); - } finally { - if(printWriter != null) { - printWriter.close(); - } - } - return eHint; - } - - /** - * 获取错误信息(有换行) - * @param eHint 获取失败提示 - * @param ex 错误信息 - * @return - */ - static String getThrowableNewLinesMsg(String eHint, Throwable ex) { - PrintWriter printWriter = null; - try { - if(ex != null) { - // 初始化Writer,PrintWriter打印流 - Writer writer = new StringWriter(); - printWriter = new PrintWriter(writer); - // 获取错误栈信息 - StackTraceElement[] stElement = ex.getStackTrace(); - // 标题,提示属于什么异常 - printWriter.append(ex.toString()); - printWriter.append(NEW_LINE_STR); - // 遍历错误栈信息,并且进行换行,缩进 - for(StackTraceElement st : stElement) { - printWriter.append("\tat "); - printWriter.append(st.toString()); - printWriter.append(NEW_LINE_STR); - } - // 关闭流 - printWriter.close(); - return writer.toString(); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getThrowableNewLinesMsg"); - } finally { - if(printWriter != null) { - printWriter.close(); - } - } - return eHint; - } - - // ================== 对外公开方法 =================== - - /** - * App初始化调用方法 - * @param context 上下文 - */ - public static void appInit(Context context) { - // 如果版本信息为null,才进行处理 - if (TextUtils.isEmpty(APP_VERSION_CODE) || TextUtils.isEmpty(APP_VERSION_NAME)) { - // 获取app版本信息 - String[] aVersion = getAppVersion(context); - // 保存app版本信息 - APP_VERSION_NAME = aVersion[0]; - APP_VERSION_CODE = aVersion[1]; - } - // 判断是否存在设备信息 - if (DEVICE_INFO_MAPS.size() == 0) { - // 获取设备信息 - getDeviceInfo(DEVICE_INFO_MAPS); - // 转换字符串 - handleDeviceInfo(""); - } - } - - // ========= 保存错误日志信息 ========== - - /** - * 保存app错误日志 - * @param ex 错误信息 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param isNewLines 是否换行 - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveErrorLog(Throwable ex, String fPath, String fName, boolean isNewLines, String... eHint) { - return saveErrorLog(ex, null, null, fPath, fName, isNewLines, eHint); - } - - /** - * 保存app错误日志 - * @param ex 错误信息 - * @param head 顶部标题 - * @param bottom 底部内容 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param isNewLines 是否换行 - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveErrorLog(Throwable ex, String head, String bottom, String fPath, String fName, boolean isNewLines, String... eHint) { - // 处理可变参数(错误提示) - eHint = handleVariable(2, eHint); - // 日志拼接 - StringBuilder sBuilder = new StringBuilder(); - // 防止文件夹不存在 - createFile(fPath); - // 设备信息 - String dInfo = handleDeviceInfo(eHint[0]); - // 如果存在顶部内容,则进行添加 - if(!TextUtils.isEmpty(head)) { - sBuilder.append(head); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - } - // ============ - // 保存App信息 - sBuilder.append("date: " + getDateNow()); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("versionName: " + APP_VERSION_NAME); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("versionCode: " + APP_VERSION_CODE); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append("============================"); - // 保存设备信息 - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append(dInfo); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - // ============ - // 错误信息 - String eMsg = null; - // 是否换行 - if(isNewLines) { - eMsg = getThrowableNewLinesMsg(eHint[1], ex); - } else { - eMsg = getThrowableMsg(eHint[1], ex); - } - // 保存异常信息 - sBuilder.append(eMsg); - // 如果存在顶部内容,则进行添加 - if(!TextUtils.isEmpty(bottom)) { - sBuilder.append(NEW_LINE_STR); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append(bottom); - } - // 保存日志到文件 - return saveFile(sBuilder.toString(), fPath + File.separator + fName); - } - - /** - * 保存app日志 - * @param log 日志信息 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveLog(String log, String fPath, String fName, String... eHint){ - return saveLog(log, null, null, fPath, fName, eHint); - } - - /** - * 保存app日志 - * @param log 日志信息 - * @param head 顶部标题 - * @param bottom 底部内容 - * @param fPath 保存路径 - * @param fName 文件名(含后缀) - * @param eHint 错误提示(无设备信息、失败信息获取失败) - * @return - */ - public static boolean saveLog(String log, String head, String bottom, String fPath, String fName, String... eHint){ - // 处理可变参数(错误提示) - eHint = handleVariable(2, eHint); - // 日志拼接 - StringBuilder sBuilder = new StringBuilder(); - // 防止文件夹不存在 - createFile(fPath); - // 设备信息 - String dInfo = handleDeviceInfo(eHint[0]); - // 如果存在顶部内容,则进行添加 - if(!TextUtils.isEmpty(head)) { - sBuilder.append(head); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - } - // ============ - // 保存App信息 - sBuilder.append("date: " + getDateNow()); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("versionName: " + APP_VERSION_NAME); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("versionCode: " + APP_VERSION_CODE); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append("============================"); - // 保存设备信息 - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append(dInfo); - sBuilder.append(NEW_LINE_STR); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - // ============ - // 保存日志信息 - sBuilder.append(log); - // 如果存在顶部内容,则进行添加 - if(!TextUtils.isEmpty(bottom)) { - sBuilder.append(NEW_LINE_STR); - sBuilder.append("============================"); - sBuilder.append(NEW_LINE_STR_X2); - sBuilder.append(bottom); - } - // 保存日志到文件 - return saveFile(sBuilder.toString(), fPath + File.separator + fName); - } - - // == - - /** - * 处理可变参数 - * @param length 保留长度 - * @param vArrays 可变参数数组 - * @return - */ - public static String[] handleVariable(int length, String[] vArrays) { - // 处理后的数据, - String[] hArrays = new String[length]; - // 是否统一处理 - boolean isUnifiedHandler = true; - try { - if(vArrays != null) { - // 获取可变参数数组长度 - int vLength = vArrays.length; - // 如果长度超出预留长度 - if(vLength >= length) { - for(int i = 0;i < length;i++) { - if(vArrays[i] == null) { - hArrays[i] = ""; - } else { - hArrays[i] = vArrays[i]; - } - } - // 但可变参数长度,超过预留长度时,已经处理完毕,不需要再次处理,节省遍历资源 - isUnifiedHandler = false; - } else { - for(int i = 0;i < vLength;i++) { - if(vArrays[i] == null) { - hArrays[i] = ""; - } else { - hArrays[i] = vArrays[i]; - } - } - } - } - if(isUnifiedHandler) { - // 统一处理,如果数据未null,则设置为“”,防止拼接出现 "null" - for(int i = 0;i < length;i++) { - if(hArrays[i] == null) { - hArrays[i] = ""; - } - } - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleVariable"); - } - return hArrays; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/share/IPreference.java b/DevLibUtils/src/main/java/dev/utils/app/share/IPreference.java deleted file mode 100644 index 831757d4c8..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/share/IPreference.java +++ /dev/null @@ -1,143 +0,0 @@ -package dev.utils.app.share; - -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * detail: SharedPreferences 操作接口 - * Created by Ttt - * http://www.jianshu.com/p/fcd75a324c35 - */ -public interface IPreference { - - /** 枚举: 存储或取出的数据类型 */ - enum DataType { - INTEGER, LONG, BOOLEAN, FLOAT, STRING, STRING_SET - } - - // == - - /** - * 保存一个数据 - * @param key - * @param value - */ - void put(String key, T value); - - /** - * 保存一个Map集合(只能是 Integer,Long,Boolean,Float,String,Set) - * @param map - */ - void putAll(Map map); - - /** - * 保存一个List集合 - * @param key - * @param list - */ - void putAll(String key, List list); - - /** - * 保存一个List集合,并且自定义保存顺序 - * @param key - * @param list - * @param comparator - */ - void putAll(String key, List list, Comparator comparator); - - /** - * 根据key取出一个数据 - * @param key - */ - T get(String key, DataType type); - - /** - * 取出全部数据 - * @return - */ - Map getAll(); - - /** - * 取出一个List集合 - * @param key - * @return - */ - List getAll(String key); - - /** - * 移除一个数据 - * @param key - * @return - */ - void remove(String key); - - /** - * 移除一个集合的数据 - * @param keys - * @return - */ - void removeAll(List keys); - - /** - * 移除一个数组的数据 - * @param keys - * @return - */ - void removeAll(String[] keys); - - /** - * 是否存在key - * @return - */ - boolean contains(String key); - - /** - * 清除全部数据 - */ - void clear(); - - // == - /** - * 获取int类型的数据 - * @return - */ - int getInt(String key); - - /** - * 获取Float类型的数据 - * @param key - * @return - */ - float getFloat(String key); - - /** - * 获取long类型的数据 - * @param key - * @return - */ - long getLong(String key); - - - /** - * 获取boolean类型的数据 - * @param key - * @return - */ - boolean getBoolean(String key); - - /** - * 获取String类型的数据 - * @param key - * @return - */ - String getString(String key); - - /** - * 获取Set类型的数据 - * @param key - * @return - */ - Set getSet(String key); -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/share/IPreferenceHolder.java b/DevLibUtils/src/main/java/dev/utils/app/share/IPreferenceHolder.java deleted file mode 100644 index f83b322858..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/share/IPreferenceHolder.java +++ /dev/null @@ -1,71 +0,0 @@ -package dev.utils.app.share; - -import android.content.Context; - -import java.util.HashMap; - -/** - * detail: IPreference 持有类,内部返回实现类 - * Created by Ttt - */ -class IPreferenceHolder { - - /** HashMap 保存持有对象 */ - private static final HashMap hashMap = new HashMap<>(); - - /** - * 初始化 - * @param context - */ - public static IPreference getPreference(Context context) { - // 判断是否为存在对应的持有类 - IPreference ipref = hashMap.get(null); - // 判断是否为存在 - if (ipref != null) { - return ipref; - } - // 初始化并保存 - ipref = new PreferenceImpl(context); - hashMap.put(null, ipref); - return ipref; - } - - /** - * 初始化 - * @param context - * @param fName - */ - public static IPreference getPreference(Context context, String fName) { - // 判断是否为存在对应的持有类 - IPreference ipref = hashMap.get(fName); - // 判断是否为存在 - if (ipref != null) { - return ipref; - } - // 初始化并保存 - ipref = new PreferenceImpl(context, fName); - hashMap.put(fName, ipref); - return ipref; - } - - /** - * 初始化 - * @param context - * @param fName - * @param mode - */ - public static IPreference getPreference(Context context, String fName, int mode) { - String key = fName + "_" + mode; - // 判断是否为存在对应的持有类 - IPreference ipref = hashMap.get(key); - // 判断是否为存在 - if (ipref != null) { - return ipref; - } - // 初始化并保存 - ipref = new PreferenceImpl(context, fName, mode); - hashMap.put(key, ipref); - return ipref; - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/share/PreferenceImpl.java b/DevLibUtils/src/main/java/dev/utils/app/share/PreferenceImpl.java deleted file mode 100644 index 0f7b6cd2f8..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/share/PreferenceImpl.java +++ /dev/null @@ -1,242 +0,0 @@ -package dev.utils.app.share; - -import android.content.Context; -import android.content.SharedPreferences; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -/** - * detail: SharedPreferences 操作接口具体实现类 - * Created by Ttt - * hint: - * 1.apply没有返回值而 commit返回boolean表明修改是否提交成功 - * 2.apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘 - * 3.apply方法不会提示任何失败的提示 apply的效率高一些,如果没有必要确认是否提交成功建议使用apply。 - */ -final class PreferenceImpl implements IPreference { - - /** 文件名 */ - private static final String NAME = "SPConfig"; - /** 默认SharedPreferences对象 */ - private SharedPreferences preferences = null; - - // ============== - // == 构造函数 == - // ============== - - /** - * 初始化 - * @param context - */ - public PreferenceImpl(Context context) { - preferences = context.getSharedPreferences(NAME, Context.MODE_PRIVATE); - } - - /** - * 初始化 - * @param context - * @param fName - */ - public PreferenceImpl(Context context, String fName) { - preferences = context.getSharedPreferences(fName, Context.MODE_PRIVATE); - } - - /** - * 初始化 - * @param context - * @param fName - * @param mode - */ - public PreferenceImpl(Context context, String fName, int mode) { - preferences = context.getSharedPreferences(fName, mode); - } - - // ============== - // == 内部方法 == - // ============== - - /** - * 保存数据 - * @param editor - * @param key - * @param obj - */ - @SuppressWarnings("unchecked") - private void put(SharedPreferences.Editor editor, String key, Object obj) { - // key 不为null时再存入,否则不存储 - if (key != null) { - if (obj instanceof Integer) { - editor.putInt(key, (Integer) obj); - } else if (obj instanceof Long) { - editor.putLong(key, (Long) obj); - } else if (obj instanceof Boolean) { - editor.putBoolean(key, (Boolean) obj); - } else if (obj instanceof Float) { - editor.putFloat(key, (Float) obj); - } else if (obj instanceof Set) { - editor.putStringSet(key, (Set) obj); - } else if (obj instanceof String) { - editor.putString(key, String.valueOf(obj)); - } - } - } - - /** - * 根据 Key 和 数据类型 取出数据 - * @param key - * @param type - * @return - */ - private Object getValue(String key, DataType type) { - switch (type) { - case INTEGER: - return preferences.getInt(key, -1); - case FLOAT: - return preferences.getFloat(key, -1f); - case BOOLEAN: - return preferences.getBoolean(key, false); - case LONG: - return preferences.getLong(key, -1L); - case STRING: - return preferences.getString(key, null); - case STRING_SET: - return preferences.getStringSet(key, null); - default: // 默认取出String类型的数据 - return null; - } - } - - /** - * 默认比较器,当存储List集合中的String类型数据时,没有指定比较器,就使用默认比较器 - */ - class ComparatorImpl implements Comparator { - @Override - public int compare(String lhs, String rhs) { - return lhs.compareTo(rhs); - } - } - - // ================== - // == 接口实现方法 == - // ================== - - - @Override - public void put(String key, T value) { - SharedPreferences.Editor edit = preferences.edit(); - put(edit, key, value); - edit.apply(); - } - - @Override - public void putAll(Map map) { - SharedPreferences.Editor edit = preferences.edit(); - for(Map.Entry entry : map.entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - put(edit, key, value); - } - edit.apply(); - } - - @Override - public void putAll(String key, List list) { - putAll(key, list, new ComparatorImpl()); - } - - @Override - public void putAll(String key, List list, Comparator comparator) { - Set set = new TreeSet<>(comparator); - for(String value : list) { - set.add(value); - } - preferences.edit().putStringSet(key, set).apply(); - } - - @Override - public T get(String key, DataType type) { - return (T) getValue(key, type); - } - - @Override - public Map getAll() { - return preferences.getAll(); - } - - @Override - public List getAll(String key) { - List list = new ArrayList(); - Set set = get(key, DataType.STRING_SET); - for(String value : set) { - list.add(value); - } - return list; - } - - @Override - public void remove(String key) { - preferences.edit().remove(key).apply(); - } - - @Override - public void removeAll(List keys) { - SharedPreferences.Editor edit = preferences.edit(); - for (String k : keys) { - edit.remove(k); - } - edit.apply(); - } - - @Override - public void removeAll(String[] keys) { - removeAll(Arrays.asList(keys)); - } - - @Override - public boolean contains(String key) { - return preferences.contains(key); - } - - @Override - public void clear() { - preferences.edit().clear().apply(); - } - - // == - - @Override - public int getInt(String key) { - return get(key, DataType.INTEGER); - } - - @Override - public float getFloat(String key) { - return get(key, DataType.FLOAT); - } - - @Override - public long getLong(String key) { - return get(key, DataType.LONG); - } - - @Override - public boolean getBoolean(String key) { - return get(key, DataType.BOOLEAN); - } - - @Override - public String getString(String key) { - return get(key, DataType.STRING); - } - - @Override - public Set getSet(String key) { - return get(key, DataType.STRING_SET); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/share/SharedUtils.java b/DevLibUtils/src/main/java/dev/utils/app/share/SharedUtils.java deleted file mode 100644 index ab95be05a0..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/share/SharedUtils.java +++ /dev/null @@ -1,106 +0,0 @@ -package dev.utils.app.share; - -import android.content.Context; - -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * detail: SPUtils 工具类 - 直接单独使用 - * Created by Ttt - */ -public final class SharedUtils { - - private SharedUtils(){ - } - - /** 全局上下文 */ - private static Context mContext; - - /** - * 初始化操作 - * @param context - */ - public static void init(Context context) { - if (mContext == null && context != null) { - // 初始化全局上下文 - mContext = context.getApplicationContext(); - } - } - - public static void put(String key, T value) { - SPUtils.getPreference(mContext).put(key, value); - } - - public static void putAll(Map map) { - SPUtils.getPreference(mContext).putAll(map); - } - - public static void putAll(String key, List list) { - SPUtils.getPreference(mContext).putAll(key, list); - } - - public static void putAll(String key, List list, Comparator comparator) { - SPUtils.getPreference(mContext).putAll(key, list, comparator); - } - - public static T get(String key, IPreference.DataType type) { - return SPUtils.getPreference(mContext).get(key, type); - } - - public static Map getAll() { - return SPUtils.getPreference(mContext).getAll(); - } - - public static List getAll(String key) { - return SPUtils.getPreference(mContext).getAll(key); - } - - public static void remove(String key) { - SPUtils.getPreference(mContext).remove(key); - } - - public static void removeAll(List keys) { - SPUtils.getPreference(mContext).removeAll(keys); - } - - public static void removeAll(String[] keys) { - SPUtils.getPreference(mContext).removeAll(keys); - } - - public static boolean contains(String key) { - return SPUtils.getPreference(mContext).contains(key); - } - - public static void clear() { - SPUtils.getPreference(mContext).clear(); - } - - // == - - public static int getInt(String key) { - return SPUtils.getPreference(mContext).getInt(key); - } - - public static float getFloat(String key) { - return SPUtils.getPreference(mContext).getFloat(key); - } - - public static long getLong(String key) { - return SPUtils.getPreference(mContext).getLong(key); - } - - public static boolean getBoolean(String key) { - return SPUtils.getPreference(mContext).getBoolean(key); - } - - public static String getString(String key) { - return SPUtils.getPreference(mContext).getString(key); - } - - public static Set getSet(String key) { - return SPUtils.getPreference(mContext).getSet(key); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/toast/ToastUtils.java b/DevLibUtils/src/main/java/dev/utils/app/toast/ToastUtils.java deleted file mode 100644 index 39f61355b6..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/toast/ToastUtils.java +++ /dev/null @@ -1,284 +0,0 @@ -package dev.utils.app.toast; - -import android.content.Context; -import android.widget.Toast; - -import dev.DevUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: 自定义Toast工具类 - * Created by Ttt - */ -public final class ToastUtils { - - private ToastUtils() { - } - - // 日志TAG - private static final String TAG = ToastUtils.class.getSimpleName(); - - /** 内部持有唯一 */ - private static Toast mToast = null; - - /** - * 内部处理防止Context 为null奔溃问题 - * @return - */ - private static Context getContext(Context context){ - if (context != null){ - return context; - } else { - // 设置全局Context - return DevUtils.getContext(); - } - } - - /** - * 获取内部唯一Toast对象 - * @return - */ - public static Toast getSignleToast(){ - return mToast; - } - - // ===================== - // === 统一显示Toast === - // ===================== - - // ======================== - // == Toast.LENGTH_SHORT == - // ======================== - - public static Toast showShort(Context context, String text) { - return handlerToastStr(true, context, text, Toast.LENGTH_SHORT); - } - - public static Toast showShort(Context context, String text, Object... objs) { - return handlerToastStr(true, context, text, Toast.LENGTH_SHORT, objs); - } - - public static Toast showShort(Context context, int resId) { - return handlerToastRes(true, context, resId, Toast.LENGTH_SHORT); - } - - public static Toast showShort(Context context, int resId, Object... objs) { - return handlerToastRes(true, context, resId, Toast.LENGTH_SHORT, objs); - } - - // ======================== - // == Toast.LENGTH_LONG === - // ======================== - - public static Toast showLong(Context context, String text) { - return handlerToastStr(true, context, text, Toast.LENGTH_LONG); - } - - public static Toast showLong(Context context, String text, Object... objs) { - return handlerToastStr(true, context, text, Toast.LENGTH_LONG, objs); - } - - public static Toast showLong(Context context, int resId) { - return handlerToastRes(true, context, resId, Toast.LENGTH_LONG); - } - - public static Toast showLong(Context context, int resId, Object...objs) { - return handlerToastRes(true, context, resId, Toast.LENGTH_LONG, objs); - } - - // ======================= - // ==== 最终Toast方法 ==== - // ======================= - - /** - * 显示Toast - * @param context - * @param resId - * @param duration - */ - public static Toast showToast(Context context, int resId, int duration) { - return handlerToastRes(true, context, resId, duration); - } - - /** - * 显示Toast - * @param context - * @param text - * @param duration - */ - public static Toast showToast(Context context, String text, int duration) { - return showToast(true, context, text, duration); - } - - // == - - /** - * 最终显示的Toast方法 - * @param isSingle - * @param context - * @param text - * @param duration - * @return Toast - */ - private static Toast showToast(boolean isSingle, Context context, String text, int duration) { - // 尽心设置为null, 便于提示排查 - if (text == null) { - text = "null"; - } - // 判断是否显示唯一, 单独共用一个 - if (isSingle) { - try { - if (mToast != null) { - mToast.setDuration(duration); - mToast.setText(text); - } else { - if (context != null) { - mToast = Toast.makeText(context, text, duration); - } - } - // 处理后,不为null,才进行处理 - if (mToast != null) { - mToast.show(); - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "showToast"); - } - return mToast; - } else { - Toast toast = null; - try { - toast = Toast.makeText(context, text, duration); - toast.show(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "showToast"); - } - return toast; - } - } - - // ===== - - /** - * 处理 R.string 资源Toast的格式化 - * @param isSingle 是否单独共用显示一个 - * @param context - * @param resId - * @param duration - * @param objs - */ - private static Toast handlerToastRes(boolean isSingle, Context context, int resId, int duration, Object... objs) { - if (getContext(context) != null) { - String text; - try { - // 获取字符串并且进行格式化 - if (objs != null && objs.length != 0) { - text = getContext(context).getString(resId, objs); - } else { - text = getContext(context).getString(resId); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handlerToastRes"); - text = e.getMessage(); - } - return showToast(isSingle, context, text, duration); - } - return null; - } - - /** - * 处理字符串Toast的格式化 - * @param context - * @param text - * @param duration - * @param objs - */ - private static Toast handlerToastStr(boolean isSingle, Context context, String text, int duration, Object... objs) { - // 防止上下文为null - if (context != null) { - // 表示需要格式化字符串,只是为了减少 format步骤,增加判断,为null不影响 - if (objs != null && objs.length != 0) { - if (text != null) { // String.format() 中的 objs 可以为null,但是 text不能为null - try { - return showToast(isSingle, context, String.format(text, objs), duration); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "handlerToastStr"); - return showToast(isSingle, context, e.getMessage(), duration); - } - } else { - return showToast(isSingle, context, text, duration); - } - } else { - return showToast(isSingle, context, text, duration); - } - } - return null; - } - - // ===================== - // == 非统一显示Toast == - // ===================== - - // ======================== - // == Toast.LENGTH_SHORT == - // ======================== - - public static Toast showShortNew(Context context, String text) { - return handlerToastStr(false, context, text, Toast.LENGTH_SHORT); - } - - public static Toast showShortNew(Context context, String text, Object... objs) { - return handlerToastStr(false, context, text, Toast.LENGTH_SHORT, objs); - } - - public static Toast showShortNew(Context context, int resId) { - return handlerToastRes(false, context, resId, Toast.LENGTH_SHORT); - } - - public static Toast showShortNew(Context context, int resId, Object... objs) { - return handlerToastRes(false, context, resId, Toast.LENGTH_SHORT, objs); - } - - // ======================== - // == Toast.LENGTH_LONG === - // ======================== - - public static Toast showLongNew(Context context, String text) { - return handlerToastStr(false, context, text, Toast.LENGTH_LONG); - } - - public static Toast showLongNew(Context context, String text, Object... objs) { - return handlerToastStr(false, context, text, Toast.LENGTH_LONG, objs); - } - - public static Toast showLongNew(Context context, int resId) { - return handlerToastRes(false, context, resId, Toast.LENGTH_LONG); - } - - public static Toast showLongNew(Context context, int resId, Object...objs) { - return handlerToastRes(false, context, resId, Toast.LENGTH_LONG, objs); - } - - // ======================= - // ==== 最终Toast方法 ==== - // ======================= - - /** - * 显示Toast - * @param context - * @param resId - * @param duration - */ - public static Toast showToastNew(Context context, int resId, int duration) { - return handlerToastRes(false, context, resId, duration); - } - - /** - * 显示Toast - * @param context - * @param text - * @param duration - */ - public static Toast showToastNew(Context context, String text, int duration) { - return showToast(false, context, text, duration); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/toast/ToastViewUtils.java b/DevLibUtils/src/main/java/dev/utils/app/toast/ToastViewUtils.java deleted file mode 100644 index 555857e517..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/toast/ToastViewUtils.java +++ /dev/null @@ -1,82 +0,0 @@ -package dev.utils.app.toast; - -import android.content.Context; -import android.view.View; -import android.widget.Toast; - -import dev.DevUtils; - -/** - * detail: 自定义Toast 使用View 工具类 - * Created by Ttt - */ -public final class ToastViewUtils { - - private ToastViewUtils() { - } - - /** 内部持有唯一 */ - private static Toast mToast = null; - - /** - * 内部处理防止Context 为null奔溃问题 - * @return - */ - private static Context getContext(Context context){ - if (context != null){ - return context; - } else { - // 设置全局Context - return DevUtils.getContext(); - } - } - - /** - * 获取内部唯一Toast对象 - * @return - */ - public static Toast getSignleToast(){ - return mToast; - } - - /** - * 最终显示Toast方法 - * @param context - * @param view - * @param duration - * @param isNewToast - */ - public static void showToast(Context context, View view, int duration, boolean isNewToast) { - // 防止上下文为null - if (context == null){ - return; - } else if (view == null) { // 防止显示的View 为null - return; - } - try { - // 判断是否显示新的 Toast - if (isNewToast){ - // 生成新的Toast - Toast toast = new Toast(context); - // 设置显示的View - toast.setView(view); - // 设置显示的时间 - toast.setDuration(duration); - // 显示Toast - toast.show(); - } else { - if (mToast == null){ - // 生成新的Toast - mToast = new Toast(context); - } - // 设置显示的View - mToast.setView(view); - // 设置显示的时间 - mToast.setDuration(duration); - // 显示Toast - mToast.show(); - } - } catch (Exception e){ - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/toast/cus/Toasty.java b/DevLibUtils/src/main/java/dev/utils/app/toast/cus/Toasty.java deleted file mode 100644 index 352b5a05ba..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/toast/cus/Toasty.java +++ /dev/null @@ -1,405 +0,0 @@ -package dev.utils.app.toast.cus; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Color; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.support.annotation.CheckResult; -import android.support.annotation.ColorInt; -import android.support.annotation.DrawableRes; -import android.support.annotation.NonNull; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import dev.utils.LogPrintUtils; -import dev.utils.R; - -/** - * detail: Toast 工具类(美化后,使用Layout显示) - * Created by Ttt - * https://github.com/GrenderG/Toasty - */ -@SuppressLint("InflateParams") -public final class Toasty { - - private Toasty() { - } - - // 日志TAG - private static final String TAG = Toasty.class.getSimpleName(); - - /** 内部持有唯一 */ - private static Toast mToast; - - // =================== - // == Toast配置信息 == - // =================== - - // === Toast 使用的配置信息 === - @ColorInt - static int DEFAULT_TEXT_COLOR = Color.parseColor("#FFFFFF"); // 白色 - @ColorInt - static int ERROR_COLOR = Color.parseColor("#D50000"); // 红色 - @ColorInt - static int INFO_COLOR = Color.parseColor("#3F51B5"); // 海洋蓝 - @ColorInt - static int SUCCESS_COLOR = Color.parseColor("#388E3C"); // 绿色 - @ColorInt - static int WARNING_COLOR = Color.parseColor("#FFA900"); // 橙色 - @ColorInt - static int NORMAL_COLOR = Color.parseColor("#353A3E"); // 灰色 - // 默认字体 - static final Typeface LOADED_TOAST_TYPEFACE = Typeface.create("sans-serif-condensed", Typeface.NORMAL); - // 当前字体样式 - static Typeface currentTypeface = LOADED_TOAST_TYPEFACE; - // 字体大小 - static int textSize = 16; // in SP - // 是否渲染图标 - static boolean tintIcon = true; - // 是否使用新的Toast - static boolean isNewToast = true; - - /** - * detail: Toast 配置 - * Created by Ttt - */ - public static class Config { - - @ColorInt - int DEFAULT_TEXT_COLOR = Toasty.DEFAULT_TEXT_COLOR; - @ColorInt - int ERROR_COLOR = Toasty.ERROR_COLOR; - @ColorInt - int INFO_COLOR = Toasty.INFO_COLOR; - @ColorInt - int SUCCESS_COLOR = Toasty.SUCCESS_COLOR; - @ColorInt - int WARNING_COLOR = Toasty.WARNING_COLOR; - // 当前字体样式 - Typeface typeface = Toasty.currentTypeface; - // 字体大小 - int textSize = Toasty.textSize; - // 是否渲染图标 - boolean tintIcon = Toasty.tintIcon; - // 是否使用新的Toast - boolean isNewToast = Toasty.isNewToast; - - // 私有构造函数 - Config() { - } - - @CheckResult - public static Config getInstance() { - return new Config(); - } - - // == - - @CheckResult - public Config setTextColor(@ColorInt int textColor) { - DEFAULT_TEXT_COLOR = textColor; - return this; - } - - @CheckResult - public Config setErrorColor(@ColorInt int errorColor) { - ERROR_COLOR = errorColor; - return this; - } - - @CheckResult - public Config setInfoColor(@ColorInt int infoColor) { - INFO_COLOR = infoColor; - return this; - } - - @CheckResult - public Config setSuccessColor(@ColorInt int successColor) { - SUCCESS_COLOR = successColor; - return this; - } - - @CheckResult - public Config setWarningColor(@ColorInt int warningColor) { - WARNING_COLOR = warningColor; - return this; - } - - @CheckResult - public Config setToastTypeface(@NonNull Typeface typeface) { - this.typeface = typeface; - return this; - } - - @CheckResult - public Config setTextSize(int sizeInSp) { - this.textSize = sizeInSp; - return this; - } - - @CheckResult - public Config setTintIcon(boolean tintIcon) { - this.tintIcon = tintIcon; - return this; - } - - @CheckResult - public Config setNewToast(boolean isNewToast) { - this.isNewToast = isNewToast; - return this; - } - - /** 应用配置参数生效 */ - public void apply() { - Toasty.DEFAULT_TEXT_COLOR = DEFAULT_TEXT_COLOR; - Toasty.ERROR_COLOR = ERROR_COLOR; - Toasty.INFO_COLOR = INFO_COLOR; - Toasty.SUCCESS_COLOR = SUCCESS_COLOR; - Toasty.WARNING_COLOR = WARNING_COLOR; - Toasty.currentTypeface = typeface; - Toasty.textSize = textSize; - Toasty.tintIcon = tintIcon; - Toasty.isNewToast = isNewToast; - } - - /** 重置默认参数 */ - public static void reset() { - Toasty.DEFAULT_TEXT_COLOR = Color.parseColor("#FFFFFF"); - Toasty.ERROR_COLOR = Color.parseColor("#D50000"); - Toasty.INFO_COLOR = Color.parseColor("#3F51B5"); - Toasty.SUCCESS_COLOR = Color.parseColor("#388E3C"); - Toasty.WARNING_COLOR = Color.parseColor("#FFA900"); - Toasty.currentTypeface = LOADED_TOAST_TYPEFACE; - Toasty.textSize = 16; - Toasty.tintIcon = true; - Toasty.isNewToast = false; - } - } - - // =================== - // == Toast显示操作 == - // =================== - - // === normal === - - public static void normal(@NonNull Context context, @NonNull CharSequence message) { - normal(context, message, Toast.LENGTH_SHORT, null); - } - - public static void normal(@NonNull Context context, @NonNull CharSequence message, Drawable icon) { - normal(context, message, Toast.LENGTH_SHORT, icon); - } - - public static void normal(@NonNull Context context, @NonNull CharSequence message, int duration) { - normal(context, message, duration, null); - } - - public static void normal(@NonNull Context context, @NonNull CharSequence message, int duration, Drawable icon) { - custom(context, message, icon, NORMAL_COLOR, duration, true); - } - - // === warning === - - public static void warning(@NonNull Context context, @NonNull CharSequence message) { - warning(context, message, Toast.LENGTH_SHORT, true); - } - - public static void warning(@NonNull Context context, @NonNull CharSequence message, int duration) { - warning(context, message, duration, true); - } - - public static void warning(@NonNull Context context, @NonNull CharSequence message, int duration, boolean withIcon) { - custom(context, message, ToastyUtils.getDrawable(context, R.drawable.dev_toast_ic_error_outline_white), WARNING_COLOR, duration, withIcon); - } - - // === info === - - public static void info(@NonNull Context context, @NonNull CharSequence message) { - info(context, message, Toast.LENGTH_SHORT, true); - } - - public static void info(@NonNull Context context, @NonNull CharSequence message, int duration) { - info(context, message, duration, true); - } - - public static void info(@NonNull Context context, @NonNull CharSequence message, int duration, boolean withIcon) { - custom(context, message, ToastyUtils.getDrawable(context, R.drawable.dev_toast_ic_info_outline_white), INFO_COLOR, duration, withIcon); - } - - // === success === - - public static void success(@NonNull Context context, @NonNull CharSequence message) { - success(context, message, Toast.LENGTH_SHORT, true); - } - - public static void success(@NonNull Context context, @NonNull CharSequence message, int duration) { - success(context, message, duration, true); - } - - public static void success(@NonNull Context context, @NonNull CharSequence message, int duration, boolean withIcon) { - custom(context, message, ToastyUtils.getDrawable(context, R.drawable.dev_toast_ic_check_white), SUCCESS_COLOR, duration, withIcon); - } - - // === error === - - public static void error(@NonNull Context context, @NonNull CharSequence message) { - error(context, message, Toast.LENGTH_SHORT, true); - } - - public static void error(@NonNull Context context, @NonNull CharSequence message, int duration) { - error(context, message, duration, true); - } - - public static void error(@NonNull Context context, @NonNull CharSequence message, int duration, boolean withIcon) { - custom(context, message, ToastyUtils.getDrawable(context, R.drawable.dev_toast_ic_clear_white), ERROR_COLOR, duration, withIcon); - } - - // === custom === - - public static void custom(@NonNull Context context, @NonNull CharSequence message) { - custom(context, message, null, -1, Toast.LENGTH_SHORT, false); - } - - public static void custom(@NonNull Context context, @NonNull CharSequence message, Drawable icon) { - custom(context, message, icon, -1, Toast.LENGTH_SHORT, true); - } - - public static void custom(@NonNull Context context, @NonNull CharSequence message, @DrawableRes int iconRes, @ColorInt int tintColor) { - custom(context, message, ToastyUtils.getDrawable(context, iconRes), tintColor, Toast.LENGTH_SHORT, true); - } - /** - * 通用自定义显示Toast - * @param context 上下文 - * @param message 显示的内容 - * @param icon 图标 - * @param tintColor 背景颜色渲染 - * @param duration 显示时间 - * @param withIcon 是否显示图标 - */ - public static void custom(@NonNull Context context, @NonNull CharSequence message, Drawable icon, @ColorInt int tintColor, int duration, boolean withIcon) { - custom(context, message, icon, tintColor, duration, withIcon, tintIcon); - } - - /** - * 通用自定义显示Toast - * @param context 上下文 - * @param message 显示的内容 - * @param icon 图标 - * @param tintColor 背景颜色渲染 - * @param duration 显示时间 - * @param withIcon 是否显示图标 - * @param tintIcon 是否处理图标 - */ - public static void custom(@NonNull Context context, @NonNull CharSequence message, Drawable icon, @ColorInt int tintColor, int duration, boolean withIcon, boolean tintIcon) { - // 初始化View - View view = inflaterView(context, message, icon, tintColor, withIcon, tintIcon); - // 显示Toast - showToasty(context, view, duration, isNewToast); - } - - /** - * 实例化 Layout View - * @param context - * @param message - * @param icon - * @param tintColor - * @param withIcon - * @param tintIcon - * @return - */ - static View inflaterView(@NonNull Context context, @NonNull CharSequence message, Drawable icon, @ColorInt int tintColor, boolean withIcon, boolean tintIcon){ - if (context != null){ - try { - // 引入View - final View toastLayout = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.dev_toast_layout, null); - // 初始化View - final ImageView toastIcon = (ImageView) toastLayout.findViewById(R.id.vid_dtl_toast_igview); - final TextView toastTextView = (TextView) toastLayout.findViewById(R.id.vid_dtl_toast_tv); - // 背景图片 - Drawable drawableFrame; - // 判断是否渲染背景 - if (tintColor != -1) { // 根据背景色进行渲染透明图片 - drawableFrame = ToastyUtils.tint9PatchDrawableFrame(context, tintColor); - } else { // 获取背景透明图片 - drawableFrame = ToastyUtils.getDrawable(context, R.drawable.dev_toast_frame); - } - // 进行设置背景 - ToastyUtils.setBackground(toastLayout, drawableFrame); - // 判断是否显示图标 - if (withIcon && icon != null) { - // 是否渲染图标 - if (tintIcon) { - // 进行渲染图标 - icon = ToastyUtils.tintIcon(icon, DEFAULT_TEXT_COLOR); - } - // 设置图标背景图 - ToastyUtils.setBackground(toastIcon, icon); - } else { - // 隐藏图标 - toastIcon.setVisibility(View.GONE); - } - // 设置字体颜色 - toastTextView.setTextColor(DEFAULT_TEXT_COLOR); - // 设置消息内容 - toastTextView.setText(message); - // 设置字体 - toastTextView.setTypeface(currentTypeface); - // 设置字体大小 - toastTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize); - // 返回View - return toastLayout; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "inflaterView"); - } - } - // 默认返回null - return null; - } - - /** - * 最终显示Toast方法 - * @param context - * @param view - * @param duration - * @param isNewToast - */ - public static void showToasty(Context context, View view, int duration, boolean isNewToast) { - // 防止上下文为null - if (context == null){ - return; - } else if (view == null) { // 防止显示的View 为null - return; - } - try { - // 判断是否显示新的 Toast - if (isNewToast){ - // 生成新的Toast - Toast toast = new Toast(context); - // 设置显示的View - toast.setView(view); - // 设置显示的时间 - toast.setDuration(duration); - // 显示Toast - toast.show(); - } else { - if (mToast == null){ - // 生成新的Toast - mToast = new Toast(context); - } - // 设置显示的View - mToast.setView(view); - // 设置显示的时间 - mToast.setDuration(duration); - // 显示Toast - mToast.show(); - } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "showToasty"); - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/toast/cus/ToastyUtils.java b/DevLibUtils/src/main/java/dev/utils/app/toast/cus/ToastyUtils.java deleted file mode 100644 index e90b94d031..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/toast/cus/ToastyUtils.java +++ /dev/null @@ -1,48 +0,0 @@ -package dev.utils.app.toast.cus; - -import android.content.Context; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.NinePatchDrawable; -import android.os.Build; -import android.support.annotation.ColorInt; -import android.support.annotation.DrawableRes; -import android.support.annotation.NonNull; -import android.view.View; - -import dev.utils.R; - -/** - * detail: Toasty 快捷操作工具类 - * Created by Ttt - * https://github.com/GrenderG/Toasty - */ -public final class ToastyUtils { - - private ToastyUtils() { - } - - public static Drawable tintIcon(@NonNull Drawable drawable, @ColorInt int tintColor) { - drawable.setColorFilter(tintColor, PorterDuff.Mode.SRC_IN); - return drawable; - } - - public static Drawable tint9PatchDrawableFrame(@NonNull Context context, @ColorInt int tintColor) { - final NinePatchDrawable toastDrawable = (NinePatchDrawable) getDrawable(context, R.drawable.dev_toast_frame); - return tintIcon(toastDrawable, tintColor); - } - - public static void setBackground(@NonNull View view, Drawable drawable) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) - view.setBackground(drawable); - else - view.setBackgroundDrawable(drawable); - } - - public static Drawable getDrawable(@NonNull Context context, @DrawableRes int id) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) - return context.getDrawable(id); - else - return context.getResources().getDrawable(id); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiUtils.java b/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiUtils.java deleted file mode 100644 index 3ffb795904..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiUtils.java +++ /dev/null @@ -1,923 +0,0 @@ -package dev.utils.app.wifi; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiConfiguration.KeyMgmt; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Build; -import android.text.TextUtils; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.List; - -import dev.utils.LogPrintUtils; - -/** - * detail: wifi工具类 - * Created by Ttt - * - * - * - */ -public final class WifiUtils { - - // 日志TAG - private final static String TAG = WifiUtils.class.getSimpleName(); - - // ======= wifi管理类对象 ======= - /** 定义WifiManager对象 */ - private WifiManager mWifiManager; - // ======= 常量 ======= - /** 没有密码 */ - public static final int NOPWD = 0; - /** wep加密方式 */ - public static final int WEP = 1; - /** wpa加密方式 */ - public static final int WPA = 2; - // ==================================================== - - /** 构造器(只能进行初始化WifiManager操作,其他靠方法定义) */ - public WifiUtils(Context context) { - // 初始化WifiManager对象 - mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - } - - /** - * 获取wifi管理对象 - * @return - */ - public WifiManager getWifiManager(){ - return this.mWifiManager; - } - - // ================= wifi 开关、连接状态获取 ================= - - /** 判断是否打开wifi */ - public boolean isOpenWifi(){ - return mWifiManager.isWifiEnabled(); - } - - /** 打开WIFI */ - public void openWifi() { - // 如果没有打开wifi,才进行打开 - if (!isOpenWifi()) { - mWifiManager.setWifiEnabled(true); - } - } - - /** 关闭WIFI */ - public void closeWifi() { - // 如果已经打开了wifi,才进行关闭 - if (isOpenWifi()) { - mWifiManager.setWifiEnabled(false); - } - } - - /** 自动切换wifi开关状态 */ - public void toggleWifiEnabled(){ - // 如果打开了,则关闭 - // 如果关闭了,则打开 - // ================= - mWifiManager.setWifiEnabled(!isOpenWifi()); - } - - /** 获取当前WIFI连接状态 */ - public int getWifiState() { - // WifiManager.WIFI_STATE_ENABLED: // 已打开 - // WifiManager.WIFI_STATE_ENABLING: // 正在打开 - // WifiManager.WIFI_STATE_DISABLED: // 已关闭 - // WifiManager.WIFI_STATE_DISABLING: // 正在关闭 - // WifiManager.WIFI_STATE_UNKNOWN: // 未知 - return mWifiManager.getWifiState(); - } - - // ================= GET 操作 ================= - - /** 开始扫描wifi */ - public boolean startScan(){ - // 开始扫描 - return mWifiManager.startScan(); - } - - /** 获取已配置的网络 */ - public List getConfiguration() { - return mWifiManager.getConfiguredNetworks(); - } - - /** 获取网络列表 */ - public List getWifiList() { - return mWifiManager.getScanResults(); - } - - /** 获取WifiInfo对象 */ - public WifiInfo getWifiInfo(){ - return mWifiManager.getConnectionInfo(); - } - - /** 获取MAC地址 */ - public String getMacAddress(WifiInfo wifiInfo) { - return wifiInfo.getMacAddress(); - } - - /** 获取接入点的BSSID */ - public String getBSSID(WifiInfo wifiInfo) { - return wifiInfo.getBSSID(); - } - - /** 获取IP地址 */ - public int getIPAddress(WifiInfo wifiInfo) { - return wifiInfo.getIpAddress(); - } - - /** 获取连接的ID */ - public int getNetworkId(WifiInfo wifiInfo) { - return wifiInfo.getNetworkId(); - } - - // === - - /** - * 获取SSID - * @param wifiInfo wifi信息 - * @return - */ - public static String getSSID(WifiInfo wifiInfo){ - try { - // 获取SSID,并进行处理 - return formatSSID(wifiInfo.getSSID(), false); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getSSID"); - } - return null; - } - - /** - * 通过上下文获取当前连接的ssid - * @param context - */ - public static String getSSID(Context context){ - try { - // 初始化WifiManager对象 - WifiManager mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - // 获取当前连接的wifi - WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); - // 获取wifi - SSID - return formatSSID(wifiInfo.getSSID(), false); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getSSID"); - } - return null; - } - - // === - - /** - * 判断是否存在\"ssid\",存在则裁剪返回 - */ - public static String formatSSID(String ssid){ - // 自动去掉SSID - if(ssid != null && ssid.startsWith("\"") && ssid.endsWith("\"")){ - try { - // 裁剪连接的ssid,并返回 - return ssid.substring(1, ssid.length() - 1); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "formatSSID"); - } - } - return ssid; - } - - /** - * 格式化,处理SSID - * @param ssid - * @param isHandler true = 添加引号,false = 删除引号 - * @return - */ - public static String formatSSID(String ssid, boolean isHandler){ - if(isHandler){ - return "\"" + ssid + "\""; - } else { - return formatSSID(ssid); - } - } - - /** - * 获取密码(经过处理) - * @param pwd 需要处理的密码 - * @param isJudge 是否需要判断 - * @return - */ - public String getPassword(String pwd, boolean isJudge){ - if (isJudge && isHexWepKey(pwd)) { - return pwd; - } else { - return "\"" + pwd + "\""; - } - } - - protected static boolean isHexWepKey(String wepKey) { - // WEP-40, WEP-104, and some vendors using 256-bit WEP (WEP-232?) - int len = wepKey.length(); - if (len != 10 && len != 26 && len != 58) { - return false; - } - return isHex(wepKey); - } - - protected static boolean isHex(String key) { - for (int i = key.length() - 1;i >= 0;i--) { - char c = key.charAt(i); - if (!(c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f')) { - return false; - } - } - return true; - } - - // ================= 快捷操作 ================= - - /** - * 获取加密类型(int常量) - 判断String - * @param type - */ - public static int getWifiType(String type) { - // WPA 是本机的用法 - if (type.contains("WPA")) { - return WPA; - } else if (type.contains("WEP")) { - return WEP; - } - // 默认没有密码 - return NOPWD; - } - - /** - * 获取加密类型(int常量) - 判断int(String) - * @param type - */ - public static int getWifiTypeInt(String type){ - // WPA 是本机的用法 - if (type.equals("2")) { - return WPA; - } else if (type.equals("1")) { - return WEP; - } - // 默认没有密码 - return NOPWD; - } - - /** - * 获取加密类型(int常量) - * @param type - */ - public static String getWifiType(int type){ - switch(type){ - case WPA: - return "2"; - case WEP: - return "1"; - case NOPWD: - return "0"; - } - return "0"; - } - - /** - * 获取加密类型(String) - * @param type - * @return - */ - public static String getWifiTypeStr(int type) { - switch(type){ - case WPA: - return "WPA"; - case WEP: - return "WEP"; - default: - return ""; - } - } - - /** - * 判断是否连接为null - - * @param ssid - * @return - */ - public static boolean isConnNull(String ssid){ - if(ssid == null){ - return true; - } else if (ssid.indexOf("unknown") != -1){ // - return true; - } - return false; - } - - /** - * 判断是否连接上Wifi(非连接中) - * @return 返回ssid - */ - public static String isConnectAphot(Context context){ - try { - // 连接管理 - ConnectivityManager cManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - // 连接状态 - NetworkInfo.State nState = cManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState(); - if((nState == NetworkInfo.State.CONNECTED)){ - // 获取连接的ssid - return getSSID(context); - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "isConnectAphot"); - } - return null; - } - - - // ================= Wifi配置操作 ================= - - /** 默认没有密码 */ - static final int SECURITY_NONE = 0; - /** WEP加密方式 */ - static final int SECURITY_WEP = 1; - /** PSK加密方式 */ - static final int SECURITY_PSK = 2; - /** EAP加密方式 */ - static final int SECURITY_EAP = 3; - - /** - * 获取Wifi配置,加密类型 - * @param wifiConfig - * @return - */ - public static int getSecurity(WifiConfiguration wifiConfig) { - if (wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { - return SECURITY_PSK; - } - if (wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_EAP) - || wifiConfig.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { - return SECURITY_EAP; - } - return (wifiConfig.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE; - } - - /** - * 获知Wifi配置,是否属于密码加密类型 - * @param wifiConfig - * @return - */ - public static boolean isExsitsPwd(WifiConfiguration wifiConfig){ - int wifiSecurity = getSecurity(wifiConfig); - // 判断是否加密 - return (wifiSecurity != SECURITY_NONE); - } - - /** - * 查看以前是否也配置过这个网络 - * @param ssid 需要判断的wifi SSID - */ - public WifiConfiguration isExsits(String ssid){ - // 获取wifi 连接过的配置信息 - List listWifiConfigs = getConfiguration(); - // 防止为null - if(listWifiConfigs == null){ - return null; - } - // 遍历判断是否存在 - for(int i = 0, len = listWifiConfigs.size();i < len ;i++){ - WifiConfiguration wConfig = listWifiConfigs.get(i); - if(wConfig != null){ - if (wConfig.SSID.equals("\"" + ssid + "\"")){ - return wConfig; - } - } - } - return null; - } - - /** - * 查看以前是否也配置过这个网络 - * @param networkId 网络id - */ - public WifiConfiguration isExsits(int networkId){ - // 获取wifi 连接过的配置信息 - List listWifiConfigs = getConfiguration(); - // 防止为null - if(listWifiConfigs == null){ - return null; - } - // 遍历判断是否存在 - for(int i = 0, len = listWifiConfigs.size();i < len ;i++){ - WifiConfiguration wConfig = listWifiConfigs.get(i); - if(wConfig != null){ - if (wConfig.networkId == networkId){ - return wConfig; - } - } - } - return null; - } - - // ================= 配置操作 ================= - /** - * 删除指定的 Wifi(SSID) 配置信息 - * @param context - * @param ssid - * @return 删除结果 - */ - public static boolean delWifiConfig(Context context, String ssid){ - try { - // 初始化WifiManager对象 - WifiManager mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - // 获取wifi 连接过的配置信息 - List listWifiConfigs = mWifiManager.getConfiguredNetworks(); - // 防止为null - if(listWifiConfigs != null){ - // 遍历判断是否存在 - for(int i = 0, len = listWifiConfigs.size();i < len ;i++){ - WifiConfiguration wConfig = listWifiConfigs.get(i); - if(wConfig != null){ - if (wConfig.SSID.equals("\"" + ssid + "\"")){ - // 删除操作 - mWifiManager.removeNetwork(wConfig.networkId); - } - } - } - // 保存操作 - mWifiManager.saveConfiguration(); - return true; - } - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "delWifiConfig"); - } - return false; - } - - /** - * 快速连接Wifi(不使用静态ip方式) - * @param ssid wifi SSID - * @param pwd wifi密码 - * @param wType 加密类型 - */ - public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType){ - return quickConnWifi(ssid, pwd, wType, false, null); - } - - /** - * 快速连接Wifi - * @param ssid wifi SSID - * @param pwd wifi密码 - * @param wType 加密类型 - * @param isStatic 是否使用静态ip连接 - * @param ip 静态IP地址 - * @return 连接成功的 WifiConfiguration - */ - public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType, boolean isStatic, String ip){ - // 步骤 - // 1.创建Wifi静态Ip连接配置 - // 2.创建正常Wifi连接配置 - // 3.查询准备连接的Wifi-SSID 是否存在配置文件,准备进行删除 - // 4.查询当前连接的Wifi-SSID 准备进行断开 - // 5.同步进行断开,删除操作,并且进行保存 - // 6.调用连接方法 - // 7.返回连接的配置信息 - // ============= - // -- - try { - // 正常的Wifi连接配置 - WifiConfiguration connWifiConfig = null; - // 如果需要通过静态ip方式连接,则进行设置 - if(isStatic && !TextUtils.isEmpty(ip)){ - // 创建Wifi静态IP连接配置 - WifiConfiguration staticWifiConfig = setStaticWifiConfig(createWifiConfig(ssid, pwd, wType, true), ip); - // 如果静态ip方式,配置失败,则初始化正常连接的Wifi配置 - if(staticWifiConfig == null){ - // 创建正常的配置信息 - connWifiConfig = createWifiConfig(ssid, pwd, wType, true); - // -- - LogPrintUtils.dTag(TAG, "属于正常方式连接(DHCP)"); - } else { - // 设置静态信息 - connWifiConfig = staticWifiConfig; - // -- - LogPrintUtils.dTag(TAG, "属于静态IP方式连接"); - } - } else { - // 创建正常的配置信息 - connWifiConfig = createWifiConfig(ssid, pwd, wType, true); - // -- - LogPrintUtils.dTag(TAG, "属于正常方式连接(DHCP)"); - } - // 判断当前准备连接的wifi,是否存在配置文件 - WifiConfiguration preWifiConfig = this.isExsits(ssid); - // -- - if(preWifiConfig != null){ - // 存在则删除 - boolean isRemove = mWifiManager.removeNetwork(preWifiConfig.networkId); - // 打印结果 - LogPrintUtils.dTag(TAG, "删除旧的配置信息 - " + preWifiConfig.SSID + ", isRemove: " + isRemove); - // 保存配置 - mWifiManager.saveConfiguration(); - } - // -- - // 连接网络 - int _nId = mWifiManager.addNetwork(connWifiConfig); - if(_nId != -1){ - try { - // 获取当前连接的Wifi对象 - WifiInfo wifiInfo = getWifiInfo(); - // 获取连接的id - int networdId = wifiInfo.getNetworkId(); - // 禁用网络 - boolean isDisable = mWifiManager.disableNetwork(networdId); - // 断开之前的连接 - boolean isDisConnect = mWifiManager.disconnect(); - // 打印断开连接结果 - LogPrintUtils.dTag(TAG, "isDisConnect : " + isDisConnect + ", isDisable : " + isDisable); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "quickConnWifi - 关闭连接出错:" + _nId); - } - // 开始连接 - boolean isResult = mWifiManager.enableNetwork(_nId, true); - // -- - if(!isResult){ - isResult = mWifiManager.enableNetwork(_nId, true); - } - // 打印结果 - LogPrintUtils.dTag(TAG, "addNetwork(enableNetwork) - result : " + isResult); - } else { - // 尝试不带引号SSID连接 - connWifiConfig.SSID = formatSSID(connWifiConfig.SSID, false); - // 连接网络 - _nId = mWifiManager.addNetwork(connWifiConfig); - if(_nId !=-1){ - try { - // 获取当前连接的Wifi对象 - WifiInfo wifiInfo = getWifiInfo(); - // 获取连接的id - int networdId = wifiInfo.getNetworkId(); - // 禁用网络 - boolean isDisable = mWifiManager.disableNetwork(networdId); - // 断开之前的连接 - boolean isDisConnect = mWifiManager.disconnect(); - // 打印断开连接结果 - LogPrintUtils.dTag(TAG, "isDisConnect : " + isDisConnect + ", isDisable : " + isDisable); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "quickConnWifi - 关闭连接出错:" + _nId); - } - // 开始连接 - boolean isResult = mWifiManager.enableNetwork(_nId, true); - // -- - if(!isResult){ - isResult = mWifiManager.enableNetwork(_nId, true); - } - // 打印结果 - LogPrintUtils.dTag(TAG, "addNetwork(enableNetwork) - result : " + isResult); - } - } - // 保存id - connWifiConfig.networkId = _nId; - // 连接的networkId - LogPrintUtils.dTag(TAG, "连接的SSID - networkId : " + _nId); - // 返回连接的信息 - return connWifiConfig; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, "quickConnWifi", e); - } - return null; - } - - /** - * 创建Wifi配置信息(无其他操作,单独返回WifiConfig) - * @param ssid - * @param pwd - * @param wType - * @param isHandler 是否处理双引号 - */ - public static WifiConfiguration createWifiConfig(String ssid, String pwd, int wType, boolean isHandler) { - try { - // 创建一个新的网络配置 - WifiConfiguration wifiConfig = new WifiConfiguration(); - wifiConfig.allowedAuthAlgorithms.clear(); - wifiConfig.allowedGroupCiphers.clear(); - wifiConfig.allowedKeyManagement.clear(); - wifiConfig.allowedPairwiseCiphers.clear(); - wifiConfig.allowedProtocols.clear(); - wifiConfig.priority = 0; - /** 设置连接的SSID */ - if (isHandler) { - wifiConfig.SSID = formatSSID(ssid, true); - } else { - wifiConfig.SSID = ssid; - } - switch(wType){ - case WifiUtils.NOPWD: // 不存在密码 - wifiConfig.hiddenSSID = true; - wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE); - break; - case WifiUtils.WEP: // WEP 加密方式 - wifiConfig.hiddenSSID = true; - if (isHandler) { - if (isHexWepKey(pwd)) { - wifiConfig.wepKeys[0] = pwd; - } else { - wifiConfig.wepKeys[0] = "\"" + pwd + "\""; - } - } else { - wifiConfig.wepKeys[0] = pwd; - } - wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE); - wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); - // wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); - wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); - wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); - wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40); - wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104); - wifiConfig.wepTxKeyIndex = 0; - break; - case WifiUtils.WPA: // WPA 加密方式 - if (isHandler) { - wifiConfig.preSharedKey = "\"" + pwd + "\""; - } else { - wifiConfig.preSharedKey = pwd; - } - wifiConfig.hiddenSSID = true; - wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); - wifiConfig.allowedKeyManagement.set(KeyMgmt.WPA_PSK); -// wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA); - wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); - wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); - wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); - wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); - wifiConfig.status = WifiConfiguration.Status.ENABLED; - break; - } - return wifiConfig; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "createWifiConfig"); - } - return null; - } - - // ==================== 连接操作 ======================== - /** - * 移除某个Wifi配置信息 - * @param wcg - * @return - */ - public boolean removeWifiConfig(WifiConfiguration wcg){ - // 如果等于null则直接返回 - if(wcg == null) - return false; - try { - // 删除配置 - boolean isResult = mWifiManager.removeNetwork(wcg.networkId); - // 保存操作 - mWifiManager.saveConfiguration(); - // 返回删除结果 - return isResult; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, "removeWifiConfig", e); - } - return false; - } - - /** - * 断开指定ID的网络 - * @param netId wifiid - */ - public void disconnectWifi(int netId) { - try { - mWifiManager.disableNetwork(netId); - mWifiManager.disconnect(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, "disconnectWifi", e); - } - } - - // ========================== 设置静态ip,域名,等信息 ===================================== - - /** - * 设置静态Wifi信息 - * @param wifiConfig Wifi配置信息 - * @param ip 静态ip - * @return - */ - private WifiConfiguration setStaticWifiConfig(WifiConfiguration wifiConfig, String ip){ - String gateway = null; - String dns = null; - if(ip != null){ - try { - InetAddress intetAddress = InetAddress.getByName(ip); - int intIp = inetAddressToInt(intetAddress); - dns = (intIp & 0xFF) + "." + ((intIp >> 8) & 0xFF) + "." + ((intIp >> 16) & 0xFF) + ".1"; - gateway = dns; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "setStaticWifiConfig"); - return null; - } - } - // 暂时不需要设置dns,所以dns参数传入null - return setStaticWifiConfig(wifiConfig, ip, gateway, null, 24); - } - - /** - * 设置静态Wifi信息 - * @param wifiConfig Wifi配置信息 - * @param ip 静态ip - * @param gateway 网关 - * @param dns dns - * @param networkPrefixLength 网络前缀长度 - * @return - */ - private WifiConfiguration setStaticWifiConfig(WifiConfiguration wifiConfig, String ip, String gateway, String dns, int networkPrefixLength){ - try { - if(ip == null || gateway == null){ - return null; - } - // 设置Inet地址 - InetAddress intetAddress = InetAddress.getByName(ip); - if(Build.VERSION.SDK_INT <= 20){ // 旧的版本,5.0之前 - // 设置IP分配方式,静态ip - setEnumField(wifiConfig, "STATIC", "ipAssignment"); - // 设置不用代理 - setEnumField(wifiConfig, "NONE", "proxySettings"); - // 设置ip地址 - setIpAddress(intetAddress, networkPrefixLength, wifiConfig); - // 设置网关 - setGateway(InetAddress.getByName(gateway), wifiConfig); - if(dns != null){ // 判断是否需要设置域名 - // 设置DNS - setDNS(InetAddress.getByName(dns), wifiConfig); - } - } else { // 5.0新版本改变到其他地方 - Object obj = getDeclaredField(wifiConfig, "mIpConfiguration"); - // 设置IP分配方式,静态ip - setEnumField(obj, "STATIC", "ipAssignment"); - // 设置不用代理 - setEnumField(obj, "NONE", "proxySettings"); - // 设置ip地址、网关、DNS - setStaticIpConfig(ip, gateway, dns, networkPrefixLength, obj); - } - return wifiConfig; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "setStaticWifiConfig"); - } - return null; - } - - private int inetAddressToInt(InetAddress inetAddr) throws Exception { - byte[] addr = inetAddr.getAddress(); - if (addr.length != 4) { - throw new IllegalArgumentException("Not an IPv4 address"); - } - return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) | ((addr[1] & 0xff) << 8) | (addr[0] & 0xff); - } - - /** - * 设置DNS - * @param dns - * @param wifiConfig - * @throws Exception - */ - private void setDNS(InetAddress dns, WifiConfiguration wifiConfig) throws Exception { - Object linkProperties = getField(wifiConfig, "linkProperties"); - if (linkProperties == null) - throw new NullPointerException(); - - ArrayList mDnses = (ArrayList) getDeclaredField(linkProperties, "mDnses"); - mDnses.clear(); // or add a new dns address , here I just want to replace DNS1 - mDnses.add(dns); - } - - /** - * 设置网关 - * @param gateway - * @param wifiConfig - * @throws Exception - */ - private void setGateway(InetAddress gateway, WifiConfiguration wifiConfig) throws Exception { - Object linkProperties = getField(wifiConfig, "linkProperties"); - if (linkProperties == null) - throw new NullPointerException(); - - Class routeInfoClass = Class.forName("android.net.RouteInfo"); - Constructor routeInfoConstructor = routeInfoClass.getConstructor(new Class[] { InetAddress.class }); - Object routeInfo = routeInfoConstructor.newInstance(gateway); - ArrayList mRoutes = (ArrayList) getDeclaredField(linkProperties, "mRoutes"); - mRoutes.clear(); - mRoutes.add(routeInfo); - } - - /** - * 设置Ip地址 - * @param addr ip地址 - * @param prefixLength 网络前缀长度 - * @param wifiConfig Wifi配置信息 - * @throws Exception - */ - private void setIpAddress(InetAddress addr, int prefixLength, WifiConfiguration wifiConfig) throws Exception { - Object linkProperties = getField(wifiConfig, "linkProperties"); - if (linkProperties == null) - throw new NullPointerException(); - - Class laClass = Class.forName("android.net.LinkAddress"); - Constructor laConstructor = laClass.getConstructor(new Class[] { InetAddress.class, int.class }); - Object linkAddress = laConstructor.newInstance(addr, prefixLength); - ArrayList mLinkAddresses = (ArrayList) getDeclaredField(linkProperties, "mLinkAddresses"); - mLinkAddresses.clear(); - mLinkAddresses.add(linkAddress); - } - - /** - * 设置Ip地址、网关、DNS(5.0之后) - * @param ip 静态ip - * @param gateway 网关 - * @param dns dns - * @param prefixLength 网络前缀长度 - * @param obj Wifi配置信息 - * @throws Exception - */ - private void setStaticIpConfig(String ip, String gateway, String dns, int prefixLength, Object obj) throws Exception { - // 从WifiConfig -> mIpConfiguration 获取staticIpConfiguration - // 获取 staticIpConfiguration 变量 - Object staticIpConfigClass = getField(obj, "staticIpConfiguration"); - if(staticIpConfigClass == null){ - // 创建静态ip配置类 - staticIpConfigClass = Class.forName("android.net.StaticIpConfiguration").newInstance(); - } - // 初始化LinkAddress 并设置ip地址 - Class laClass = Class.forName("android.net.LinkAddress"); - Constructor laConstructor = laClass.getConstructor(new Class[] { InetAddress.class, int.class }); - Object linkAddress = laConstructor.newInstance(InetAddress.getByName(ip), prefixLength); - // 设置地址ip地址 ipAddress - setValueField(staticIpConfigClass, linkAddress, "ipAddress"); - // 设置网关 gateway - setValueField(staticIpConfigClass, InetAddress.getByName(gateway), "gateway"); - if(dns != null){ // 判断是否需要设置域名 - // 设置DNS - ArrayList mDnses = (ArrayList) getDeclaredField(staticIpConfigClass, "dnsServers"); - mDnses.clear(); // or add a new dns address , here I just want to replace DNS1 - mDnses.add(InetAddress.getByName(dns)); - } - // 设置赋值 staticIpConfiguration 属性 - setValueField(obj, staticIpConfigClass, "staticIpConfiguration"); - } - - /** - * 通过反射获取public字段 - * @param obj - * @param name - * @return - * @throws Exception - */ - private Object getField(Object obj, String name) throws Exception { - Field f = obj.getClass().getField(name); - Object out = f.get(obj); - return out; - } - - /** - * 通过反射获取全部字段 - * @param obj - * @param name - * @return - * @throws Exception - */ - private Object getDeclaredField(Object obj, String name) throws Exception { - Field f = obj.getClass().getDeclaredField(name); - f.setAccessible(true); - Object out = f.get(obj); - return out; - } - - /** - * 通过反射枚举类,进行设置 - * @param obj 设置对象 - * @param value 设置参数值 - * @param name 变量名 - * @throws Exception 抛出异常 - */ - private void setEnumField(Object obj, String value, String name) throws Exception { - Field f = obj.getClass().getField(name); - f.set(obj, Enum.valueOf((Class) f.getType(), value)); - } - - /** - * 通过反射,进行设置 - * @param obj 设置对象 - * @param val 设置参数值 - * @param name 变量名 - * @throws Exception 抛出异常 - */ - private void setValueField(Object obj, Object val, String name) throws Exception { - Field f = obj.getClass().getField(name); - f.set(obj, val); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiVo.java b/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiVo.java deleted file mode 100644 index 1c37c7f259..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiVo.java +++ /dev/null @@ -1,119 +0,0 @@ -package dev.utils.app.wifi; - -import android.net.wifi.ScanResult; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.List; - -import dev.utils.LogPrintUtils; - -/** - * detail: Wifi信息实体类 - * Created by Ttt - */ -public class WifiVo implements Parcelable { - - // 日志TAG - private static final String TAG = WifiVo.class.getSimpleName(); - /** wifi SSID */ - public String wSSID = null; - /** wifi 密码 */ - public String wPwd = null; - /** wifi 加密类型 */ - public int wType = WifiUtils.NOPWD; - /** wifi 信号等级 */ - public int wLevel = 0; - - // -- - - public WifiVo(){ - } - - /** - * 获取Wifi信息 - * @param sResult 扫描的Wifi信息 - */ - public static WifiVo createWifiVo(ScanResult sResult){ - if (sResult != null){ - try { - // 防止wifi名长度为0 - if(sResult.SSID.length() == 0){ - return null; - } - // 初始化wifi信息实体类 - WifiVo wifiVo = new WifiVo(); - // 保存ssid - wifiVo.wSSID = WifiUtils.formatSSID(sResult.SSID, false); - // 保存加密类型 - wifiVo.wType = WifiUtils.getWifiType(sResult.capabilities); - // 保存wifi信号等级 - wifiVo.wLevel = sResult.level; - return wifiVo; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "createWifiVo"); - } - } - return null; - } - - /** - * 扫描Wifi信息 - * @param listWifiVos 处理后数据源 - * @param listScanResults 扫描返回的数据 - */ - public static void scanWifiVos(ArrayList listWifiVos, List listScanResults){ - // 清空旧数据 - listWifiVos.clear(); - // 遍历wifi列表数据 - for(int i = 0, len = listScanResults.size();i < len;i++){ - // 如果出现异常,或者失败,则无视当前的索引wifi信息 - try { - // 获取当前索引的wifi信息 - ScanResult sResult = listScanResults.get(i); - // 防止wifi名长度为0 - if(sResult.SSID.length() == 0){ - continue; - } - // 保存wifi信息 - listWifiVos.add(createWifiVo(sResult)); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "scanWifiVos"); - } - } - } - - // =============== Parcelable =============== - - protected WifiVo(Parcel in) { - wSSID = in.readString(); - wPwd = in.readString(); - wType = in.readInt(); - wLevel = in.readInt(); - } - - public static final Creator CREATOR = new Creator() { - @Override - public WifiVo createFromParcel(Parcel in) { - return new WifiVo(in); - } - @Override - public WifiVo[] newArray(int size) { - return new WifiVo[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(wSSID); - dest.writeString(wPwd); - dest.writeInt(wType); - dest.writeInt(wLevel); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/AssistUtils.java b/DevLibUtils/src/main/java/dev/utils/common/AssistUtils.java deleted file mode 100644 index 4eca7a3b20..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/AssistUtils.java +++ /dev/null @@ -1,108 +0,0 @@ -package dev.utils.common; - -import java.util.Random; -import java.util.UUID; - -import dev.utils.common.encrypt.MD5Utils; - -/** - * detail: 快捷辅助工具类 - * Created by Ttt - */ -public final class AssistUtils { - - private AssistUtils(){ - } - - /** - * 获取随机唯一数 - * @return - */ - public static UUID randomUUID(){ - return UUID.randomUUID(); - } - - /** - * 获取随机唯一数 HashCode - * @return - */ - public static int randomUUIDToHashCode(){ - return UUID.randomUUID().hashCode(); - } - - /** - * 获取随机唯一数 HashCode - * @param uuid - * @return - */ - public static int randomUUIDToHashCode(UUID uuid){ - if (uuid != null){ - return uuid.hashCode(); - } - return -1; - } - - /** - * 获取随机数 唯一id - * @return - */ - public static String getRandomUUID(){ - // 获取随机数 - String random1 = (900000 + new Random().nextInt(10000)) + ""; - // 获取随机数 - String random2 = (900000 + new Random().nextInt(10000)) + ""; - // 获取当前时间 - String cTime = Long.toString(System.currentTimeMillis()) + random1 + random2; - // 生成唯一随机uuid cTime.hashCode(), random1.hashCode() | random2.hashCode() - UUID randomUUID = new UUID(cTime.hashCode(), ((long) random1.hashCode() << 32) | random2.hashCode()); - // 获取uid - return randomUUID.toString(); - } - - // - - - /** - * 循环MD5 加密处理 - * @param data - * @param number - * @param isUppercase - * @param salts - * @return - */ - public static String whileMD5(String data, int number, boolean isUppercase, String... salts){ - if (data != null && number >= 1){ - int saltLen = (salts != null) ? salts.length : 0; - // 判断是否大写 - if (isUppercase){ - // 循环加密 - for (int i = 0; i < number; i++){ - if (saltLen >= i){ - String salt = salts[i]; - if (salt != null){ - data = MD5Utils.md5Upper(data + salt); - } else { - data = MD5Utils.md5Upper(data); - } - } else { - data = MD5Utils.md5Upper(data); - } - } - } else { - // 循环加密 - for (int i = 0; i < number; i++){ - if (saltLen >= i){ - String salt = salts[i]; - if (salt != null){ - data = MD5Utils.md5(data + salt); - } else { - data = MD5Utils.md5(data); - } - } else { - data = MD5Utils.md5(data); - } - } - } - } - return data; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/BigDecimalUtils.java b/DevLibUtils/src/main/java/dev/utils/common/BigDecimalUtils.java deleted file mode 100644 index 86c7c37bdd..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/BigDecimalUtils.java +++ /dev/null @@ -1,629 +0,0 @@ -package dev.utils.common; - -import java.math.BigDecimal; - -import dev.utils.JCLogUtils; - -/** - * detail: 资金运算工具类 - * Created by Ttt - */ -public final class BigDecimalUtils { - - private BigDecimalUtils(){ - } - - // 日志TAG - private static final String TAG = BigDecimalUtils.class.getSimpleName(); - - /** 默认保留位数 */ - private static final int DEF_DIV_SCALE = 10; - - // = - - /** - * 提供精确的加法运算 - * @param v1 被加数 - * @param v2 加数 - * @return 两个参数的和 - */ - public static double add(double v1, double v2) { - BigDecimal b1 = new BigDecimal(Double.toString(v1)); - BigDecimal b2 = new BigDecimal(Double.toString(v2)); - return b1.add(b2).doubleValue(); - } - - /** - * 提供精确的加法运算 - * @param v1 被加数 - * @param v2 加数 - * @return 两个参数的和 - */ - public static BigDecimal add(String v1, String v2) { - try { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); - return b1.add(b2); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "add"); - } - return new BigDecimal(0); - } - - /** - * 提供精确的加法运算 - * @param v1 被加数 - * @param v2 加数 - * @param scale 保留scale 位小数 - * @return 两个参数的和 - */ - public static String add(String v1, String v2, int scale) { - if (scale < 0) { - return "0"; - } - try { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); - return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "add"); - } - return "0"; - } - - /** - * 提供精确的减法运算 - * @param v1 被减数 - * @param v2 减数 - * @return 两个参数的差 - */ - public static double substract(double v1, double v2) { - BigDecimal b1 = new BigDecimal(Double.toString(v1)); - BigDecimal b2 = new BigDecimal(Double.toString(v2)); - return b1.subtract(b2).doubleValue(); - } - - /** - * 提供精确的减法运算。 - * @param v1 被减数 - * @param v2 减数 - * @return 两个参数的差 - */ - public static BigDecimal substract(String v1, String v2) { - try { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); - return b1.subtract(b2); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "substract"); - } - return new BigDecimal(0); - } - - /** - * 提供精确的减法运算 - * @param v1 被减数 - * @param v2 减数 - * @param scale 保留scale 位小数 - * @return 两个参数的差 - */ - public static String substract(String v1, String v2, int scale) { - if (scale < 0) { - return "0"; - } - try { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); - return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "substract"); - } - return "0"; - } - - /** - * 提供精确的乘法运算 - * @param v1 被乘数 - * @param v2 乘数 - * @return 两个参数的积 - */ - public static double multiply(double v1, double v2) { - BigDecimal b1 = new BigDecimal(Double.toString(v1)); - BigDecimal b2 = new BigDecimal(Double.toString(v2)); - return b1.multiply(b2).doubleValue(); - } - - /** - * 提供精确的乘法运算 - * @param v1 被乘数 - * @param v2 乘数 - * @param scale 保留scale 位小数 - * @return 两个参数的积 - */ - public static double multiply(double v1, double v2, int scale) { - if (scale < 0) { - return 0d; - } - BigDecimal b1 = new BigDecimal(Double.toString(v1)); - BigDecimal b2 = new BigDecimal(Double.toString(v2)); - return round(b1.multiply(b2).doubleValue(), scale); - } - - /** - * 提供精确的乘法运算 - * @param v1 被乘数 - * @param v2 乘数 - * @param scale 保留scale 位小数 - * @return 两个参数的积 - */ - public static String multiply(String v1, String v2, int scale) { - if (scale < 0) { - return "0"; - } - try { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); - return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "multiply"); - } - return "0"; - } - - /** - * 提供(相对)精确的除法运算,当发生除不尽的情况时, - * 精确到小数点以后10位,以后的数字四舍五入. - * @param v1 被除数 - * @param v2 除数 - * @return 两个参数的商 - */ - public static double divide(double v1, double v2) { - return divide(v1, v2, DEF_DIV_SCALE); - } - - - /** - * 提供(相对)精确的除法运算. - * 当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入. - * @param v1 被除数 - * @param v2 除数 - * @param scale 表示需要精确到小数点以后几位 - * @return 两个参数的商 - */ - public static double divide(double v1, double v2, int scale) { - if (scale < 0) { - return 0d; - } - BigDecimal b1 = new BigDecimal(Double.toString(v1)); - BigDecimal b2 = new BigDecimal(Double.toString(v2)); - return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); - } - - /** - * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 - * 定精度,以后的数字四舍五入 - * @param v1 被除数 - * @param v2 除数 - * @param scale 表示需要精确到小数点以后几位 - * @return 两个参数的商 - */ - public static String divide(String v1, String v2, int scale) { - if (scale < 0) { - return "0"; - } - try { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v1); - return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "divide"); - } - return "0"; - } - - /** - * 提供精确的小数位四舍五入处理 - * @param v 需要四舍五入的数字 - * @param scale 小数点后保留几位 - * @return 四舍五入后的结果 - */ - public static double round(double v, int scale) { - if (scale < 0) { - return 0d; - } - BigDecimal b = new BigDecimal(Double.toString(v)); - BigDecimal one = new BigDecimal("1"); - return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); - } - - /** - * 提供精确的小数位四舍五入处理 - * @param v 需要四舍五入的数字 - * @param scale 小数点后保留几位 - * @return 四舍五入后的结果 - */ - public static String round(String v, int scale) { - if (scale < 0) { - return "0"; - } - try { - BigDecimal b = new BigDecimal(v); - return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "round"); - } - return "0"; - } - - /** - * 提供精确的小数位获取 - * @param v 需要处理的数字 - * @param scale 小数点后保留几位 - * @return 最后的结果 - */ - public static double round(double v, int scale, int round) { - if (scale < 0) { - return 0d; - } - // https://www.cnblogs.com/liqforstudy/p/5652517.html - // 向下取 - // round = BigDecimal.ROUND_DOWN; - BigDecimal b = new BigDecimal(Double.toString(v)); - BigDecimal one = new BigDecimal("1"); - return b.divide(one, scale, round).doubleValue(); - } - - /** - * 取余数 - * @param v1 被除数 - * @param v2 除数 - * @param scale 小数点后保留几位 - * @return 余数 - */ - public static String remainder(String v1, String v2, int scale) { - if (scale < 0) { - return "0"; - } - try { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); - return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "remainder"); - } - return "0"; - } - - /** - * 取余数 BigDecimal - * @param v1 被除数 - * @param v2 除数 - * @param scale 小数点后保留几位 - * @return 余数 - */ - public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) { - if (scale < 0) { - return new BigDecimal(0); - } - try { - return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "remainder"); - } - return new BigDecimal(0); - } - - /** - * 金额分割,四舍五入金额 - * @param s - * @return - */ - public static String formatMoney(BigDecimal s) { - String retVal = ""; - String str = ""; - boolean is_positive_integer = false; - if (null == s) { - return "0.00"; - } - - if (0 == s.doubleValue()) { - return "0.00"; - } - // 判断是否正整数 - if (s.toString().indexOf("-") != -1) { - is_positive_integer = true; - } else { - is_positive_integer = false; - } - // 是负整数 - if (is_positive_integer) { - // 去掉 - 号 - s = new BigDecimal(s.toString().substring(1, s.toString().length())); - } - str = s.setScale(2, BigDecimal.ROUND_HALF_UP).toString(); - StringBuffer sb = new StringBuffer(); - String[] strs = str.split("\\."); - int j = 1; - for (int i = 0; i < strs[0].length(); i++) { - char a = strs[0].charAt(strs[0].length() - i - 1); - sb.append(a); - if (j % 3 == 0 && i != strs[0].length() - 1) { - sb.append(","); - } - j++; - } - String str1 = sb.toString(); - StringBuffer sb1 = new StringBuffer(); - for (int i = 0; i < str1.length(); i++) { - char a = str1.charAt(str1.length() - 1 - i); - sb1.append(a); - } - sb1.append("."); - sb1.append(strs[1]); - retVal = sb1.toString(); - - if (is_positive_integer) { - retVal = "-" + retVal; - } - return retVal; - } - - /** - * 四舍五入金额 - * @param s - * @return - */ - public static String formatMoney1(BigDecimal s) { - String retVal = ""; - String str = ""; - boolean is_positive_integer = false; - if (null == s) { - return "0.00"; - } - - if (0 == s.doubleValue()) { - return "0.00"; - } - // 判断是否正整数 - if (s.toString().indexOf("-") != -1) { - is_positive_integer = true; - } else { - is_positive_integer = false; - } - // 是负整数 - if (is_positive_integer) { - // 去掉 - 号 - s = new BigDecimal(s.toString().substring(1, s.toString().length())); - } - str = s.setScale(2, BigDecimal.ROUND_HALF_UP).toString(); - StringBuffer sb = new StringBuffer(); - String[] strs = str.split("\\."); - int j = 1; - for (int i = 0; i < strs[0].length(); i++) { - char a = strs[0].charAt(strs[0].length() - i - 1); - sb.append(a); - if (j % 3 == 0 && i != strs[0].length() - 1) { - sb.append(""); - } - j++; - } - String str1 = sb.toString(); - StringBuffer sb1 = new StringBuffer(); - for (int i = 0; i < str1.length(); i++) { - char a = str1.charAt(str1.length() - 1 - i); - sb1.append(a); - } - sb1.append("."); - sb1.append(strs[1]); - retVal = sb1.toString(); - - if (is_positive_integer) { - retVal = "-" + retVal; - } - return retVal; - } - - /** - * 比较大小 - * @param amount 输入的数值 - * @param compare 被比较的数字 - * @return true 大于被比较的数 - */ - public static Boolean compareBigDecimal(String amount, double compare) { - try { - BigDecimal lenth = new BigDecimal(amount); - if (lenth.compareTo(BigDecimal.valueOf(compare)) == -1) { - return false; - } - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "compareBigDecimal"); - } - return true; - } - - /** - * 获取自己想要的数据格式 - * @param s 需处理的数据 - * @param numOfIntPart 整数位数 - * @param numOfDecimalPart 小数位数 - * @return 处理过的数据 - */ - public static String adjustDouble(String s, int numOfIntPart, int numOfDecimalPart) { - if (s == null){ - return null; - } - // 按小数点的位置分割成整数部分和小数部分 - String[] array = s.split("\\."); - char[] tempA = new char[numOfIntPart]; - char[] tempB = new char[numOfDecimalPart]; - // 整数部分满足精度要求(情况1) - if (array[0].length() == numOfIntPart) { - // 直接获取整数部分长度字符 - for (int i = 0; i < array[0].length(); i++) { - tempA[i] = array[0].charAt(i); - } - // 小数部分精度大于或等于指定的精度 - if (numOfDecimalPart <= array[1].length()) { - for (int i = 0; i < numOfDecimalPart; i++) { - tempB[i] = array[1].charAt(i); - } - } - // 小数部分精度小于指定的精度 - if (numOfDecimalPart > array[1].length()) { - for (int i = 0; i < numOfDecimalPart; i++) { - if (i < array[1].length()) { - tempB[i] = array[1].charAt(i); - } else { - tempB[i] = '0'; - } - } - } - if (numOfDecimalPart == 0) { - return String.valueOf(tempA) + String.valueOf(tempB); - } - return String.valueOf(tempA) + "." + String.valueOf(tempB); - } - // 整数部分位数大于精度要求(情况2) - if (array[0].length() > numOfIntPart) { - // 先倒序获取指定位数的整数 - for (int i = array[0].length() - 1, j = 0; (i >= array[0].length() - numOfIntPart) && (j < numOfIntPart); i--, j++) { - tempA[j] = array[0].charAt(i); - } - char[] tempA1 = new char[numOfIntPart]; - // 调整顺序 - for (int j = 0, k = tempA.length - 1; j < numOfIntPart && (k >= 0); j++, k--) { - tempA1[j] = tempA[k]; - - } - // 小数部分精度大于或等于指定的精度 - if (numOfDecimalPart <= array[1].length()) { - for (int i = 0; i < numOfDecimalPart; i++) { - tempB[i] = array[1].charAt(i); - } - } - // 小数部分精度小于指定的精度 - if (numOfDecimalPart > array[1].length()) { - for (int i = 0; i < numOfDecimalPart; i++) { - if (i < array[1].length()) { - tempB[i] = array[1].charAt(i); - } else { - tempB[i] = '0'; - } - } - } - return String.valueOf(tempA1) + "." + String.valueOf(tempB); - } - // 整数部分满足精度要求(情况3) - if (array[0].length() == numOfIntPart) { - //直接获取整数部分长度字符 - for (int i = 0; i < array[0].length(); i++) { - tempA[i] = array[0].charAt(i); - } - // 小数部分精度小于指定的精度 - if (numOfDecimalPart > array[1].length()) { - for (int i = 0; i < numOfDecimalPart; i++) { - if (i < array[1].length()) { - tempB[i] = array[1].charAt(i); - } else { - tempB[i] = '0'; - } - } - } - // 小数部分精度大于或等于指定的精度 - if (numOfDecimalPart <= array[1].length()) { - for (int i = 0; i < numOfDecimalPart; i++) { - tempB[i] = array[1].charAt(i); - } - } - if (numOfDecimalPart == 0) { - return String.valueOf(tempA) + String.valueOf(tempB); - } - return String.valueOf(tempA) + "." + String.valueOf(tempB); - } - // 整数部分大于精度要求(情况4) - if (array[0].length() > numOfIntPart) { - // 先倒序获取指定位数的整数 - for (int i = array[0].length() - 1, j = 0; (i >= array[0].length() - numOfIntPart + 1) && (j < numOfIntPart); i--, j++) { - tempA[j] = array[0].charAt(i); - } - char[] tempA1 = new char[numOfIntPart]; - // 调整顺序 - for (int j = 0, k = tempA.length - 1; j < numOfIntPart && (k >= 0); j++) { - tempA1[j] = tempA[k]; - k--; - } - // 小数部分精度小于指定的精度 - if (numOfDecimalPart > array[1].length()) { - for (int i = 0; i < numOfDecimalPart; i++) { - if (i >= array[1].length()) { - tempB[i] = '0'; - } else { - tempB[i] = array[1].charAt(i); - } - } - } - // 小数部分精度大于或等于指定的精度 - if (numOfDecimalPart <= array[1].length()) { - for (int i = 0; i < numOfDecimalPart; i++) { - tempB[i] = array[1].charAt(i); - } - } - if (numOfDecimalPart == 0) { - return String.valueOf(tempA1) + String.valueOf(tempB); - } - return String.valueOf(tempA1) + "." + String.valueOf(tempB); - } - // 整数部分小于精度要求(情况5) - if (array[0].length() < numOfIntPart) { - // 先倒序获取指定位数的整数 - char[] tempA1 = new char[numOfIntPart]; - for (int i = array[0].length() - 1, j = 0; (i >= numOfIntPart - array[0].length() - (numOfIntPart - array[0].length())) && (j < numOfIntPart); i--, j++) { - tempA1[j] = array[0].charAt(i); - } - // 补0 - for (int i = array[0].length(); i < array[0].length() + numOfIntPart - array[0].length(); i++) { - tempA1[i] = '0'; - } - - char[] tempA2 = new char[numOfIntPart]; - // 调整顺序 - for (int j = 0, k = tempA1.length - 1; j < numOfIntPart && (k >= 0); j++) { - tempA2[j] = tempA1[k]; - k--; - } - // 小数部分精度小于指定的精度 - if (numOfDecimalPart > array[1].length()) { - for (int i = 0; i < numOfDecimalPart; i++) { - if (i < array[1].length()) { - tempB[i] = array[1].charAt(i); - } else { - tempB[i] = '0'; - } - } - } - // 小数部分精度大于或等于指定的精度 - if (numOfDecimalPart <= array[1].length()) { - for (int i = 0; i < numOfDecimalPart; i++) { - tempB[i] = array[1].charAt(i); - - } - } - if (numOfDecimalPart == 0) { - return String.valueOf(tempA2) + String.valueOf(tempB); - } - return String.valueOf(tempA2) + "." + String.valueOf(tempB); - } - // 情况(6) - if ((array[0].length() < numOfIntPart) && (array[1].length() < numOfDecimalPart)) { - for (int i = 0; i < numOfIntPart - array[0].length(); i++) { - s = "0" + s; - } - - for (int i = 0; i < numOfDecimalPart - array[1].length(); i++) { - s = s + "0"; - } - return s; - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/ByteUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ByteUtils.java deleted file mode 100644 index 6141a597a3..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/ByteUtils.java +++ /dev/null @@ -1,233 +0,0 @@ -package dev.utils.common; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import dev.utils.JCLogUtils; - -/** - * detail: 字节工具类,提供一些有关字节的便捷方法 - * (01)、位移加密:static void byteJiaMi(byte[] bytes) - * (02)、从bytes上截取一段:static byte[] cutOut(byte[] bytes, int off, int length) - * Created by Ttt - * http://www.runoob.com/java/java-operators.html - */ -public final class ByteUtils { - - private ByteUtils(){ - } - - // 日志TAG - private static final String TAG = ByteUtils.class.getSimpleName(); - - // 按位补运算符 例子: - // ByteUtils.byteToBit(new byte[] { 1 }) = 00000001 (二进制字符串) - // 进行反转 变成 11111110 - // 在通过二进制字符串转换 byte 数组 ByteUtils.bits2Bytes("11111110") => byte[] { -2 } - // ~1 => -2 结果是正常的 - - /** - * (01)、位移加密、解密,调同一个方法 - * @param bytes - */ - public static void byteJiaMi(byte[] bytes) { - if (bytes == null){ - return; - } - for (int w = 0; w < bytes.length; w++) { - int a = bytes[w]; - a = ~a; // 按位补运算符 => 翻转操作数的每一位,即0变成1,1变成0,再通过反转后的二进制初始化回16进制 - bytes[w] = (byte) a; - } - } - - /** - * 字符串转数组 - * @param str - * @return - */ - public static byte[] hexStrToByteArray(String str) { - if (str == null) { - return null; - } - if (str.length() == 0) { - return new byte[0]; - } - byte[] byteArray = new byte[str.length() / 2]; - for (int i = 0; i < byteArray.length; i++) { - String subStr = str.substring(2 * i, 2 * i + 2); - byteArray[i] = ((byte) Integer.parseInt(subStr, 16)); - } - return byteArray; - } - - /** - * (02)、从bytes上截取一段 - * @param bytes 母体 - * @param off 起始 - * @param length 个数 - * @return byte[] - */ - public static byte[] cutOut(byte[] bytes, int off, int length) { - byte[] bytess = new byte[length]; - System.arraycopy(bytes, off, bytess, 0, length); - return bytess; - } - - /** - * 将字节转换为二进制字符串 - * @param bytes 字节数组 - * @return 二进制字符串 - */ - public static String byteToBit(byte... bytes) { - StringBuffer sb = new StringBuffer(); - int z, len; - String str; - for (int w = 0; w < bytes.length; w++) { - z = bytes[w]; - z |= 256; - str = Integer.toBinaryString(z); - len = str.length(); - sb.append(str.substring(len - 8, len)); - } - return sb.toString(); - } - - /** - * 二进制字符串, 转换成byte数组 - * 例: "011000010111001101100100" 传入 bits2Bytes, 返回 byte[], 通过new String(byte()) 获取 asd => 配合 bytes2Bits 使用 - * @param bits The bits. - * @return bytes - */ - public static byte[] bits2Bytes(String bits) { - int lenMod = bits.length() % 8; - int byteLen = bits.length() / 8; - // add "0" until length to 8 times - if (lenMod != 0) { - for (int i = lenMod; i < 8; i++) { - bits = "0" + bits; - } - byteLen++; - } - byte[] bytes = new byte[byteLen]; - for (int i = 0; i < byteLen; ++i) { - for (int j = 0; j < 8; ++j) { - bytes[i] <<= 1; - bytes[i] |= bits.charAt(i * 8 + j) - '0'; - } - } - return bytes; - } - - /** - * 字节数组转换成16进制字符串 - * @param raw - * @return - */ - public static String getHex(byte[] raw) { - String HEXES = "0123456789ABCDEF"; - if (raw == null) { - return null; - } - final StringBuilder hex = new StringBuilder(2 * raw.length); - for (final byte b : raw) { - hex.append(HEXES.charAt((b & 0xF0) >> 4)) - .append(HEXES.charAt((b & 0x0F))); - } - return hex.toString(); - } - - /** - * 将一个short转换成字节数组 - * @param sh short - * @return 字节数组 - */ - public static byte[] valueOf(short sh) { - byte[] shortBuf = new byte[2]; - for (int i = 0; i < 2; i++) { - int offset = (shortBuf.length - 1 - i) * 8; - shortBuf[i] = (byte) ((sh >>> offset) & 0xff); - } - return shortBuf; - } - - /** - * 将一个int转换成字节数组 - * @param in int - * @return 字节数组 - */ - public static byte[] valueOf(int in) { - byte[] b = new byte[4]; - for (int i = 0; i < 4; i++) { - int offset = (b.length - 1 - i) * 8; - b[i] = (byte) ((in >>> offset) & 0xFF); - } - return b; - } - - /** - * 从一个byte[]数组中截取一部分 - * @param src - * @param begin - * @param count - * @return - */ - public static byte[] subBytes(byte[] src, int begin, int count) { - byte[] bs = new byte[count]; - for (int i = begin; i < begin + count; i++) { - bs[i - begin] = src[i]; - } - return bs; - } - - - /** - * byte[] 转为 对象 - * @param bytes - * @return - */ - public static Object byteToObject(byte[] bytes) { - ObjectInputStream ois = null; - try { - ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); - return ois.readObject(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "byteToObject"); - } finally { - if (ois != null) { - try { - ois.close(); - } catch (Exception e) { - } - } - } - return null; - } - - /** - * 对象 转为 byte[] - * @param obj - * @return - */ - public static byte[] objectToByte(Object obj) { - ObjectOutputStream oos = null; - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - oos = new ObjectOutputStream(bos); - oos.writeObject(obj); - return bos.toByteArray(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "objectToByte"); - } finally { - if (oos != null) { - try { - oos.close(); - } catch (Exception e) { - } - } - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/ClassUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ClassUtils.java deleted file mode 100644 index 16e8da28e9..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/ClassUtils.java +++ /dev/null @@ -1,93 +0,0 @@ -package dev.utils.common; - -import java.lang.reflect.Constructor; -import java.util.Collection; -import java.util.Date; - -import dev.utils.JCLogUtils; - -/** - * detail: 类工具 - * @author mty - */ -public final class ClassUtils { - - private ClassUtils(){ - } - - // 日志TAG - private static final String TAG = ClassUtils.class.getSimpleName(); - - /** - * 判断类是否是基础数据类型 - * 目前支持11种 - * @param clazz - * @return - */ - public static boolean isBaseDataType(Class clazz) { - return clazz.isPrimitive() || clazz.equals(String.class) || clazz.equals(Boolean.class) - || clazz.equals(Integer.class) || clazz.equals(Long.class) || clazz.equals(Float.class) - || clazz.equals(Double.class) || clazz.equals(Byte.class) || clazz.equals(Character.class) - || clazz.equals(Short.class) || clazz.equals(Date.class) || clazz.equals(byte[].class) - || clazz.equals(Byte[].class); - } - - /** - * 根据类获取对象:不再必须一个无参构造 - * @param claxx - * @return - */ - public static T newInstance(Class claxx){ - try { - Constructor[] cons = claxx.getDeclaredConstructors(); - for (Constructor c : cons) { - Class[] cls = c.getParameterTypes(); - if (cls.length == 0) { - c.setAccessible(true); - return (T) c.newInstance(); - } else { - Object[] objs = new Object[cls.length]; - for (int i = 0; i < cls.length; i++) { - objs[i] = getDefaultPrimiticeValue(cls[i]); - } - c.setAccessible(true); - return (T) c.newInstance(objs); - } - } - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "newInstance"); - } - return null; - } - - /** - * 判断 Class 是否为原始类型(boolean、char、byte、short、int、long、float、double) - * @param clazz - * @return - */ - public static Object getDefaultPrimiticeValue(Class clazz) { - if (clazz.isPrimitive()) { - return clazz == boolean.class ? false : 0; - } - return null; - } - - /** - * 判断是否集合类型 - * @param claxx - * @return - */ - public static boolean isCollection(Class claxx) { - return Collection.class.isAssignableFrom(claxx); - } - - /** - * 是否数组类型 - * @param claxx - * @return - */ - public static boolean isArray(Class claxx) { - return claxx.isArray(); - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/CloneUtils.java b/DevLibUtils/src/main/java/dev/utils/common/CloneUtils.java deleted file mode 100644 index 956a67c602..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/CloneUtils.java +++ /dev/null @@ -1,84 +0,0 @@ -package dev.utils.common; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - -import dev.utils.JCLogUtils; - -/** - * detail: 克隆相关工具类 - * Created by Ttt - */ -public final class CloneUtils { - - private CloneUtils(){ - } - - // 日志TAG - private static final String TAG = CloneUtils.class.getSimpleName(); - - /** - * 进行克隆 - * @param data - * @param - * @return - */ - public static T deepClone(final Serializable data) { - if (data == null) return null; - return (T) bytes2Object(serializable2Bytes(data)); - } - - /** - * 通过序列化实体类, 获取对应的byte数组数据 - * @param serializable - * @return - */ - private static byte[] serializable2Bytes(final Serializable serializable) { - if (serializable == null) return null; - ByteArrayOutputStream baos; - ObjectOutputStream oos = null; - try { - oos = new ObjectOutputStream(baos = new ByteArrayOutputStream()); - oos.writeObject(serializable); - return baos.toByteArray(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "serializable2Bytes"); - return null; - } finally { - if (oos != null) { - try { - oos.close(); - } catch (IOException e) { - } - } - } - } - - /** - * 通过 byte数据, 生成Object对象 - * @param bytes - * @return - */ - private static Object bytes2Object(final byte[] bytes) { - if (bytes == null) return null; - ObjectInputStream ois = null; - try { - ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); - return ois.readObject(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "bytes2Object"); - return null; - } finally { - if (ois != null) { - try { - ois.close(); - } catch (IOException e) { - } - } - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/ColorsUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ColorsUtils.java deleted file mode 100644 index ec48afa5c9..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/ColorsUtils.java +++ /dev/null @@ -1,240 +0,0 @@ -package dev.utils.common; - -/** - * detail: 颜色工具类 包括常用的色值 - * Created by Ttt - */ -public final class ColorsUtils { - - private ColorsUtils() { - } - - /** - 黑色 透明度10-90 - 19 - 33 - 4C - 66 - 7F - 99 - B2 - CC - E5 - */ - - // 0-255 十进值转换成十六进制,如255 就是 ff - // 百分之10 透明度 = 19 - // 百分之20 透明度 = 33 - // 255 * 0.x = 十进制 -> 十六进制 - // import android.graphics.Color; - // Color.argb() - // Color.rgb() - - /** 白色 */ - public static final int WHITE = 0xffffffff; - - /** 白色 - 半透明 */ - public static final int WHITE_TRANSLUCENT = 0x80ffffff; - - /** 黑色 */ - public static final int BLACK = 0xff000000; - - /** 黑色 - 半透明 */ - public static final int BLACK_TRANSLUCENT = 0x80000000; - - /** 透明 */ - public static final int TRANSPARENT = 0x00000000; - - /** 红色 */ - public static final int RED = 0xffff0000; - - /** 红色 - 半透明 */ - public static final int RED_TRANSLUCENT = 0x80ff0000; - - /** 红色 - 深的 */ - public static final int RED_DARK = 0xff8b0000; - - /** 红色 - 深的 - 半透明 */ - public static final int RED_DARK_TRANSLUCENT = 0x808b0000; - - /** 绿色 */ - public static final int GREEN = 0xff00ff00; - - /** 绿色 - 半透明 */ - public static final int GREEN_TRANSLUCENT = 0x8000ff00; - - /** 绿色 - 深的 */ - public static final int GREEN_DARK = 0xff003300; - - /** 绿色 - 深的 - 半透明 */ - public static final int GREEN_DARK_TRANSLUCENT = 0x80003300; - - /** 绿色 - 浅的 */ - public static final int GREEN_LIGHT = 0xffccffcc; - - /** 绿色 - 浅的 - 半透明 */ - public static final int GREEN_LIGHT_TRANSLUCENT = 0x80ccffcc; - - /** 蓝色 */ - public static final int BLUE = 0xff0000ff; - - /** 蓝色 - 半透明 */ - public static final int BLUE_TRANSLUCENT = 0x800000ff; - - /** 蓝色 - 深的 */ - public static final int BLUE_DARK = 0xff00008b; - - /** 蓝色 - 深的 - 半透明 */ - public static final int BLUE_DARK_TRANSLUCENT = 0x8000008b; - - /** 蓝色 - 浅的 */ - public static final int BLUE_LIGHT = 0xff36a5E3; - - /** 蓝色 - 浅的 - 半透明 */ - public static final int BLUE_LIGHT_TRANSLUCENT = 0x8036a5E3; - - /** 天蓝 */ - public static final int SKYBLUE = 0xff87ceeb; - - /** 天蓝 - 半透明 */ - public static final int SKYBLUE_TRANSLUCENT = 0x8087ceeb; - - /** 天蓝 - 深的 */ - public static final int SKYBLUE_DARK = 0xff00bfff; - - /** 天蓝 - 深的 - 半透明 */ - public static final int SKYBLUE_DARK_TRANSLUCENT = 0x8000bfff; - - /** 天蓝 - 浅的 */ - public static final int SKYBLUE_LIGHT = 0xff87cefa; - - /** 天蓝 - 浅的 - 半透明 */ - public static final int SKYBLUE_LIGHT_TRANSLUCENT = 0x8087cefa; - - /** 灰色 */ - public static final int GRAY = 0xff969696; - - /** 灰色 - 半透明 */ - public static final int GRAY_TRANSLUCENT = 0x80969696; - - /** 灰色 - 深的 */ - public static final int GRAY_DARK = 0xffa9a9a9; - - /** 灰色 - 深的 - 半透明 */ - public static final int GRAY_DARK_TRANSLUCENT = 0x80a9a9a9; - - /** 灰色 - 暗的 */ - public static final int GRAY_DIM = 0xff696969; - - /** 灰色 - 暗的 - 半透明 */ - public static final int GRAY_DIM_TRANSLUCENT = 0x80696969; - - /** 灰色 - 浅的 */ - public static final int GRAY_LIGHT = 0xffd3d3d3; - - /** 灰色 - 浅的 - 半透明 */ - public static final int GRAY_LIGHT_TRANSLUCENT = 0x80d3d3d3; - - /** 橙色 */ - public static final int ORANGE = 0xffffa500; - - /** 橙色 - 半透明 */ - public static final int ORANGE_TRANSLUCENT = 0x80ffa500; - - /** 橙色 - 深的 */ - public static final int ORANGE_DARK = 0xffff8800; - - /** 橙色 - 深的 - 半透明 */ - public static final int ORANGE_DARK_TRANSLUCENT = 0x80ff8800; - - /** 橙色 - 浅的 */ - public static final int ORANGE_LIGHT = 0xffffbb33; - - /** 橙色 - 浅的 - 半透明 */ - public static final int ORANGE_LIGHT_TRANSLUCENT = 0x80ffbb33; - - /** 金色 */ - public static final int GOLD = 0xffffd700; - - /** 金色 - 半透明 */ - public static final int GOLD_TRANSLUCENT = 0x80ffd700; - - /** 粉色 */ - public static final int PINK = 0xffffc0cb; - - /** 粉色 - 半透明 */ - public static final int PINK_TRANSLUCENT = 0x80ffc0cb; - - /** 紫红色 */ - public static final int FUCHSIA = 0xffff00ff; - - /** 紫红色 - 半透明 */ - public static final int FUCHSIA_TRANSLUCENT = 0x80ff00ff; - - /** 灰白色 */ - public static final int GRAYWHITE = 0xfff2f2f2; - - /** 灰白色 - 半透明 */ - public static final int GRAYWHITE_TRANSLUCENT = 0x80f2f2f2; - - /** 紫色 */ - public static final int PURPLE = 0xff800080; - - /** 紫色 - 半透明 */ - public static final int PURPLE_TRANSLUCENT = 0x80800080; - - /** 青色 */ - public static final int CYAN = 0xff00ffff; - - /** 青色 - 半透明 */ - public static final int CYAN_TRANSLUCENT = 0x8000ffff; - - /** 青色 - 深的 */ - public static final int CYAN_DARK = 0xff008b8b; - - /** 青色 - 深的 - 半透明 */ - public static final int CYAN_DARK_TRANSLUCENT = 0x80008b8b; - - /** 黄色 */ - public static final int YELLOW = 0xffffff00; - - /** 黄色 - 半透明 */ - public static final int YELLOW_TRANSLUCENT = 0x80ffff00; - - /** 黄色 - 浅的 */ - public static final int YELLOW_LIGHT = 0xffffffe0; - - /** 黄色 - 浅的 - 半透明 */ - public static final int YELLOW_LIGHT_TRANSLUCENT = 0x80ffffe0; - - /** 巧克力色 */ - public static final int CHOCOLATE = 0xffd2691e; - - /** 巧克力色 - 半透明 */ - public static final int CHOCOLATE_TRANSLUCENT = 0x80d2691e; - - /** 番茄色 */ - public static final int TOMATO = 0xffff6347; - - /** 番茄色 - 半透明 */ - public static final int TOMATO_TRANSLUCENT = 0x80ff6347; - - /** 橙红色 */ - public static final int ORANGERED = 0xffff4500; - - /** 橙红色 - 半透明 */ - public static final int ORANGERED_TRANSLUCENT = 0x80ff4500; - - /** 银白色 */ - public static final int SILVER = 0xffc0c0c0; - - /** 银白色 - 半透明 */ - public static final int SILVER_TRANSLUCENT = 0x80c0c0c0; - - /** 高光 */ - public static final int HIGHLIGHT = 0x33ffffff; - - /** 低光 */ - public static final int LOWLIGHT = 0x33000000; - -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/ConverUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ConverUtils.java deleted file mode 100644 index c0995772dc..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/ConverUtils.java +++ /dev/null @@ -1,554 +0,0 @@ -package dev.utils.common; - -import java.util.Arrays; - -import dev.utils.JCLogUtils; - -/** - * detail: 转换工具类 - * Created by Ttt - */ -public final class ConverUtils { - - private ConverUtils(){ - } - - // 日志TAG - private static final String TAG = ConverUtils.class.getSimpleName(); - - // byte是字节数据类型、有符号型的、占1个字节、大小范围为-128——127 - // char是字符数据类型、无符号型的、占2个字节(unicode码)、大小范围为0-65535 - - // byte[] (-128) - 127 - // 当大于127时则开始缩进 127 = 127, 128 = -128 , 129 = -127 - - /** - * char 数组 转 String - * @param chars - * @param dfStr - * @return - */ - public static String toString(char[] chars, String dfStr){ - if (chars != null){ - try { - return new String(chars); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "toString"); - } - } - return dfStr; - } - - /** - * byte 数组 转 String - * @param bytes - * @param dfStr - * @return - */ - public static String toString(byte[] bytes, String dfStr){ - if (bytes != null){ - try { - return new String(bytes); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "toString"); - } - } - return dfStr; - } - - /** - * char 转 String - * @param val - * @return - * -- - * 97 - 122 = a-z, 48-57 = 0-9 - * toString((char) 97); = a - */ - public static String toString(char val){ - return Character.toString(val); - } - - /** - * Object 转 String - * @param obj - * @return - */ - public static String toString(Object obj, String dfStr) { - if (obj != null){ - // return (obj instanceof String ? (String) obj : obj.toString()); - try { - if (obj instanceof String){ - return (String) obj; - } else { - Class cla = obj.getClass(); - // 判断是否数组类型 - if (cla.isArray()){ - // == 基本数据类型 == - if (cla.isAssignableFrom(int[].class)){ - return Arrays.toString((int[]) obj); - } else if (cla.isAssignableFrom(boolean[].class)){ - return Arrays.toString((boolean[]) obj); - } else if (cla.isAssignableFrom(long[].class)){ - return Arrays.toString((long[]) obj); - } else if (cla.isAssignableFrom(double[].class)){ - return Arrays.toString((double[]) obj); - } else if (cla.isAssignableFrom(float[].class)){ - return Arrays.toString((float[]) obj); - } else if (cla.isAssignableFrom(byte[].class)){ - return Arrays.toString((byte[]) obj); - } else if (cla.isAssignableFrom(char[].class)){ - return Arrays.toString((char[]) obj); - } else if (cla.isAssignableFrom(short[].class)){ - return Arrays.toString((short[]) obj); - } - // == 基本类型封装 == - if (cla.isAssignableFrom(Integer[].class)){ - return Arrays.toString((Integer[]) obj); - } else if (cla.isAssignableFrom(Boolean[].class)){ - return Arrays.toString((Boolean[]) obj); - } else if (cla.isAssignableFrom(Long[].class)){ - return Arrays.toString((Long[]) obj); - } else if (cla.isAssignableFrom(Double[].class)){ - return Arrays.toString((Double[]) obj); - } else if (cla.isAssignableFrom(Float[].class)){ - return Arrays.toString((Float[]) obj); - } else if (cla.isAssignableFrom(Byte[].class)){ - return Arrays.toString((Byte[]) obj); - } else if (cla.isAssignableFrom(Character[].class)){ - return Arrays.toString((Character[]) obj); - } else if (cla.isAssignableFrom(Short[].class)){ - return Arrays.toString((Short[]) obj); - } - } - return obj.toString(); - } - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "toString"); - } - } - return dfStr; - } - - /** - * 字符串 转 int - * @param str - * @param dfValue - * @return - */ - public static int toInt(String str, int dfValue) { - try { - return Integer.parseInt(str); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toInt"); - } - return dfValue; - } - - /** - * 字符串 转 boolean - * @param str - * @param dfValue - * @return - */ - public static boolean toBoolean(String str, boolean dfValue){ - try { - // 判断是否0 - if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("1")){ - return true; - } else { - return false; - } - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toBoolean"); - } - return dfValue; - } - - /** - * 字符串 转 float - * @param str - * @param dfValue - * @return - */ - public static float toFloat(String str, float dfValue){ - try { - return Float.parseFloat(str); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toFloat"); - } - return dfValue; - } - - /** - * 字符串 转 double - * @param str - * @param dfValue - * @return - */ - public static double toDouble(String str, double dfValue){ - try { - return Double.parseDouble(str); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toDouble"); - } - return dfValue; - } - - /** - * 字符串 转 long - * @param str - * @param dfValue - * @return - */ - public static long toLong(String str, long dfValue){ - try { - return Long.parseLong(str); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toLong"); - } - return dfValue; - } - - // == 转换对象 == - - /** - * 基本类型对象 转 int - * @param val - * @param dfValue - * @return - */ - public static int toInt(Integer val, int dfValue) { - try { - return val; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toInt"); - } - return dfValue; - } - - /** - * 基本类型对象 转 boolean - * @param val - * @return - */ - public static boolean toBoolean(Boolean val){ - return (val != null && val); - } - - /** - * 基本类型对象 转 float - * @param val - * @param dfValue - * @return - */ - public static float toFloat(Float val, float dfValue){ - try { - return val; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toFloat"); - } - return dfValue; - } - - /** - * 基本类型对象 转 double - * @param val - * @param dfValue - * @return - */ - public static double toDouble(Double val, double dfValue){ - try { - return val; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toDouble"); - } - return dfValue; - } - - /** - * 基本类型对象 转 long - * @param val - * @param dfValue - * @return - */ - public static long toLong(Long val, long dfValue){ - try { - return val; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toLong"); - } - return dfValue; - } - - // == 平常其他 == - - /** - * char 转换 int - * @param val - * @return - */ - public static int toInt(char val){ - return (int) val; - } - - /** - * 字符串 获取 char(默认第一位) - * @param str - * @param dfValue - * @return - */ - public static char toChar(String str, char dfValue){ - return toChar(str, 0, dfValue); - } - - /** - * 字符串 获取 char - * @param str - * @param pos - * @param dfValue - * @return - */ - public static char toChar(String str, int pos, char dfValue){ - try { - return str.charAt(pos); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "toChar"); - } - return dfValue; - } - - /** - * char 转换 unicode 编码 - * @param val - * @return - * -- - * toCharInt('a') = 97 - */ - public static int toCharInt(char val){ - return (int) val; - } - - /** - * 字符串 获取 char数组 - * @param str - * @return - */ - public static char[] toCharArys(String str){ - try { - return str.toCharArray(); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "toCharArys"); - } - return null; - } - - /** - * 字符串 获取 byte数组 - * @param str - * @return - */ - public static byte[] toByteArys(String str){ - if (str != null) { - try { - return str.getBytes(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toByteArys"); - } - } - return null; - } - - // ====================================================================== - - // 以0x开始的数据表示16进制 - - /** - * 一个整数参数的字符串表示形式在基数为16的无符号整数 - * @param val - * @return - * -- - * 例如 -> 传入 0x1f603 => toHexString(0x1f603); 返回: 1f603 - */ - public static String toHexString(int val){ - return Integer.toHexString(val); - } - - public static String toHexString(long val){ - return Long.toHexString(val); - } - - public static String toHexString(double val){ - return Double.toHexString(val); - } - - public static String toHexString(float val){ - return Float.toHexString(val); - } - - // -- - - /** - * 字符串转换对应的进制 - * @param s - * @param radix - * @return - * -- - * 如: parseInt("1f603", 16) = 128515 - */ - public static int parseInt(String s, int radix){ - return Integer.parseInt(s, radix); - } - - // == - - // toHexString(0x1f603) = 1f603 - // parseInt("1f603", 16) = 128515 - // toHexString(128515) = 1f603 - - // == - - // ====================================================================== - - // 小写 - public static final char HEX_DIGITS[]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; - // 大写 - public static final char HEX_DIGITS_UPPER[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - - /** - * 进行转换 十六进制字符 - * @param bData - * @param hexDigits - * @return - */ - public static String toHexString(byte[] bData, char[] hexDigits) { - if (bData == null || hexDigits == null){ - return null; - } - StringBuilder sBuilder = new StringBuilder(bData.length * 2); - for (int i = 0, len = bData.length; i < len; i++) { - sBuilder.append(hexDigits[(bData[i] & 0xf0) >>> 4]); - sBuilder.append(hexDigits[bData[i] & 0x0f]); - } - return sBuilder.toString(); - } - - /** - * 十六进制字符串 转换byte数组 - * @param hexString - * @return - */ - public static byte[] hexString2Bytes(String hexString) { - if (isSpace(hexString)) return null; - int len = hexString.length(); - if (len % 2 != 0) { - hexString = "0" + hexString; - len = len + 1; - } - char[] hexBytes = hexString.toUpperCase().toCharArray(); - byte[] ret = new byte[len >> 1]; - for (int i = 0; i < len; i += 2) { - ret[i >> 1] = (byte) (hex2Int(hexBytes[i]) << 4 | hex2Int(hexBytes[i + 1])); - } - return ret; - } - - private static int hex2Int(final char hexChar) { - if (hexChar >= '0' && hexChar <= '9') { - return hexChar - '0'; - } else if (hexChar >= 'A' && hexChar <= 'F') { - return hexChar - 'A' + 10; - } else { - throw new IllegalArgumentException(); - } - } - - // == - - /** - * 把 bytes 数据, 转换成二进制数据 - * 例: "asd".getBytes() 传入 bytes2Bits 返回 011000010111001101100100 二进制的字符串数据 - * @param bytes The bytes. - * @return bits - */ - public static String bytes2Bits(final byte[] bytes) { - StringBuilder sb = new StringBuilder(); - for (byte aByte : bytes) { - for (int j = 7; j >= 0; --j) { - sb.append(((aByte >> j) & 0x01) == 0 ? '0' : '1'); - } - } - return sb.toString(); - } - - /** - * 二进制字符串, 转换成byte数组 - * 例: "011000010111001101100100" 传入 bits2Bytes, 返回 byte[], 通过new String(byte()) 获取 asd => 配合 bytes2Bits 使用 - * @param bits The bits. - * @return bytes - */ - public static byte[] bits2Bytes(String bits) { - int lenMod = bits.length() % 8; - int byteLen = bits.length() / 8; - // add "0" until length to 8 times - if (lenMod != 0) { - for (int i = lenMod; i < 8; i++) { - bits = "0" + bits; - } - byteLen++; - } - byte[] bytes = new byte[byteLen]; - for (int i = 0; i < byteLen; ++i) { - for (int j = 0; j < 8; ++j) { - bytes[i] <<= 1; - bytes[i] |= bits.charAt(i * 8 + j) - '0'; - } - } - return bytes; - } - - /** - * byte 数组 转换 char 数组, 并且进行补码 - * @param bytes The bytes. - * @return chars - */ - public static char[] bytes2Chars(final byte[] bytes) { - if (bytes == null) return null; - int len = bytes.length; - if (len <= 0) return null; - char[] chars = new char[len]; - for (int i = 0; i < len; i++) { - chars[i] = (char) (bytes[i] & 0xff); - } - return chars; - } - - /** - * char 数组 转换 byte 数组 - * @param chars The chars. - * @return bytes - */ - public static byte[] chars2Bytes(final char[] chars) { - if (chars == null || chars.length <= 0) return null; - int len = chars.length; - byte[] bytes = new byte[len]; - for (int i = 0; i < len; i++) { - bytes[i] = (byte) (chars[i]); - } - return bytes; - } - - /** - * 判断字符串是否为 null 或全为空白字符 - * @param str 待校验字符串 - * @return - */ - private static boolean isSpace(final String str) { - if (str == null) return true; - for (int i = 0, len = str.length(); i < len; ++i) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/DateUtils.java b/DevLibUtils/src/main/java/dev/utils/common/DateUtils.java deleted file mode 100644 index df2e8ca147..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/DateUtils.java +++ /dev/null @@ -1,613 +0,0 @@ -package dev.utils.common; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; - -import dev.utils.JCLogUtils; - -/** - * detail: 日期工具类 - * Created by Ttt - */ -public final class DateUtils { - - private DateUtils() { - } - - // 日志TAG - private static final String TAG = DateUtils.class.getSimpleName(); - - /** 日期格式类型 */ - public static final String yyyyMMdd = "yyyy-MM-dd"; - public static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; - public static final String yyyyMMddHHmmss_2 = "yyyy年M月d日 HH:mm:ss"; - public static final String HHmmss = "HH:mm:ss"; - public static final String hhmmMMDDyyyy = "hh:mm M月d日 yyyy"; - public static final String MMdd = "MM月dd日"; - // -- - /** 一分钟 60秒 */ - public static final int MINUTE_S = 60; - /** 一小时 60 * 60秒 */ - public static final int HOUR_S = 3600; - /** 一天 24 * 60 * 60*/ - public static final int DAY_S = 86400; - - /** 秒与毫秒的倍数 */ - public static final long SEC = 1000; - /** 分与毫秒的倍数 */ - public static final long MIN = SEC * 60; - /** 时与毫秒的倍数 */ - public static final long HOUR = MIN * 60; - /** 天与毫秒的倍数 */ - public static final long DAY = HOUR * 24; - /** 周与毫秒的倍数 */ - public static final long WEEK = DAY * 7; - /** 月与毫秒的倍数 */ - public static final long MONTH = DAY * 30; - /** 年与毫秒的倍数 */ - public static final long YEAR = DAY * 365; - - /** - * 获取当前日期的字符串 - "yyyy-MM-dd HH:mm:ss" - * @return 字符串 - */ - public static String getDateNow(){ - return getDateNow(yyyyMMddHHmmss); - } - - /** - * 获取当前日期的字符串 - * @param format 日期格式,譬如:"yyyy-MM-dd HH:mm:ss" - * @return 字符串 - */ - public static String getDateNow(String format) { - try { - if ((format == null) || (format != null && format.equals(""))) - format = yyyyMMddHHmmss; - - Calendar cld = Calendar.getInstance(); - DateFormat df = new SimpleDateFormat(format); - return df.format(cld.getTime()); - } catch (Exception e) { - } - return null; - } - - // == - - /** - * 将时间戳转换日期字符串 - * @param time 时间戳 - * @param format 日期格式 - * @return 按照需求格式的日期字符串 - */ - public static String formatTime(long time, String format){ - try { - return new SimpleDateFormat(format).format(time); - } catch (Exception e) { - } - return null; - } - - /** - * 将Date类型转换日期字符串 - * @param date 日期 - * @param format 日期格式 - * @return 按照需求格式的日期字符串 - */ - public static String formatDate(Date date, String format) { - try { - return new SimpleDateFormat(format).format(date); - } catch (Exception e) { - } - return null; - } - - // === - - /** - * 将时间戳转换成Date类型 - * @param time - * @return - */ - public static Date parseDate(long time){ - try { - return new Date(time); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "parseDate"); - } - return null; - } - - /** - * 将日期字符串转换为Date类型 - 默认表示time 属于 yyyy-MM-dd HH:mm:ss 格式 - * @param time - * @return - */ - public static Date parseDate(String time){ - return parseDate(time, yyyyMMddHHmmss); - } - - /** - * 将日期字符串转换为Date类型 - * @param time - * @param format - * @return - */ - public static Date parseDate(String time, String format) { - try { - if ((format == null) || (format != null && format.equals(""))) - format = yyyyMMddHHmmss; - - return new SimpleDateFormat(format).parse(time); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "parseDate"); - } - return null; - } - - // == - - /** - * 解析时间字符串转换为long毫秒 - 默认表示time 属于 yyyy-MM-dd HH:mm:ss 格式 - * @param time - * @return - */ - public static long parseLong(String time){ - return parseLong(time, yyyyMMddHHmmss); - } - - /** - * 解析时间字符串转换为long毫秒 - * @param time 时间 - * @param format 时间的格式 - * @return - */ - public static long parseLong(String time, String format) { - try { - if ((format == null) || (format != null && format.equals(""))) - format = yyyyMMddHHmmss; - - SimpleDateFormat sdf = new SimpleDateFormat(format); - // 按规定的时间格式,进行格式化时间,并且获取long时间毫秒 - long millionSeconds = sdf.parse(time).getTime(); - // 返回毫秒时间 - return millionSeconds; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "parseLong"); - } - return 0l; - } - - // == - - /** - * 获取时间差 - 分钟 - * @param time(毫秒) - * @return - */ - public static int getTimeDiffMinute(long time){ - return (int) (time / 60000); // 60 * 1000 - } - - /** - * 获取时间差 - 小时 - * @param time(毫秒) - * @return - */ - public static int getTimeDiffHour(long time){ - return (int) (time / 3600000); // 60 * 1000 * 60 - } - - /** - * 获取时间差 - 天 - * @param time(毫秒) - * @return - */ - public static int getTimeDiffDay(long time){ - return (int) (time / 86400000); // 60 * 1000 * 60 * 24 - } - - /** - * 获取时间差 - (传入时间 - 当前时间) - * @param time - * @return - */ - public static long getTimeDiff(long time){ - return time - System.currentTimeMillis(); - } - - /** - * 获取时间差 - * @param timeVal1 - * @param timeVal2 - * @return - */ - public static long getTimeDiff(String timeVal1, String timeVal2){ - long time1 = parseLong(timeVal1); - long time2 = parseLong(timeVal2); - if (time1 > 1l && time2 > 1l){ - return time1 - time2; - } - return -2l; - } - - /** - * 获取时间差 - * @param timeVal1 - * @param timeFormat1 - * @param timeVal2 - * @param timeFormat2 - * @return - */ - public static long getTimeDiff(String timeVal1, String timeFormat1, String timeVal2, String timeFormat2){ - long time1 = parseLong(timeVal1, timeFormat1); - long time2 = parseLong(timeVal2, timeFormat2); - if (time1 > 1l && time2 > 1l){ - return time1 - time2; - } - return -2l; - } - -// System.out.println("现在时间: " + getDateNow()); -// String startTimeStr = "2017-11-27 14:45:00"; -// System.out.println("开始时间: " + startTimeStr); -// long startTimeL = DateUtils.parseLong(startTimeStr); -// System.out.println("转换long: " + startTimeL); -// long timeDiff = startTimeL - System.currentTimeMillis(); -// System.out.println("时间差: " + timeDiff); - - // ======= 获取时间 ======= - - /** - * 获取年 - * @param date Date对象 - * @return 年 - */ - public static int getYear(Date date) { - try { - Calendar cld = Calendar.getInstance(); - cld.setTime(date); - return cld.get(Calendar.YEAR); - } catch (Exception e) { - } - return -1; - } - - /** - * 获取月 (0 - 11) + 1 - * @param date Date对象 - * @return 月 - */ - public static int getMonth(Date date) { - try { - Calendar cld = Calendar.getInstance(); - cld.setTime(date); - return cld.get(Calendar.MONTH) + 1; - } catch (Exception e) { - } - return -1; - } - - /** - * 获取日 - * @param date Date对象 - * @return 日 - */ - public static int getDay(Date date) { - try { - Calendar c = Calendar.getInstance(); - c.setTime(date); - return c.get(Calendar.DAY_OF_MONTH); - } catch (Exception e) { - } - return -1; - } - - /** - * 获取时 - 24 - * @param date Date对象 - * @return 时 - */ - public static int get24Hour(Date date) { - try { - Calendar c = Calendar.getInstance(); - c.setTime(date); - return c.get(Calendar.HOUR_OF_DAY); - } catch (Exception e) { - } - return -1; - } - - /** - * 获取时 - 12 - * @param date Date对象 - * @return 时 - */ - public static int get12Hour(Date date) { - try { - Calendar c = Calendar.getInstance(); - c.setTime(date); - return c.get(Calendar.HOUR); - } catch (Exception e) { - } - return -1; - } - - /** - * 获取分 - * @param date Date对象 - * @return 分 - */ - public static int getMinute(Date date) { - try { - Calendar c = Calendar.getInstance(); - c.setTime(date); - return c.get(Calendar.MINUTE); - } catch (Exception e) { - } - return -1; - } - - /** - * 获取秒 - * @param date Date对象 - * @return 秒 - */ - public static int getSecond(Date date) { - try { - Calendar c = Calendar.getInstance(); - c.setTime(date); - return c.get(Calendar.SECOND); - } catch (Exception e) { - } - return -1; - } - - public static String convertTime(int time, boolean isAppand){ - if (isAppand){ - return time >= 10 ? time + "" : "0" + time; - } - return time + ""; - } - - // == - - /** - * 获取年 - */ - public static int getYear() { - return Calendar.getInstance().get(Calendar.YEAR); - } - - /** - * 获取月 (0 - 11) + 1 - */ - public static int getMonth() { - int month = Calendar.getInstance().get(Calendar.MONTH) + 1; - return month; - } - - /** - * 获取日 - */ - public static int getDay() { - return Calendar.getInstance().get(Calendar.DATE); - } - - /** - * 获取时 - 24 - */ - public static int get24Hour() { - return Calendar.getInstance().get(Calendar.HOUR_OF_DAY); - } - - /** - * 获取时 - 12 - */ - public static int get12Hour() { - return Calendar.getInstance().get(Calendar.HOUR); - } - - /** - * 获取分 - */ - public static int getMinute() { - return Calendar.getInstance().get(Calendar.MINUTE); - } - - /** - * 获取秒 - * @return - */ - public static int getSecond() { - return Calendar.getInstance().get(Calendar.SECOND); - } - - // == - - /** - * 判断是否闰年 - * @param year 年数 - * @return - */ - public static boolean isLeapYear(int year) { - // 判断是否闰年 - if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { - return true; - } - return false; - } - - /** - * 获取月份 - 对应天数 - * @param year 年数 - * @param month 月份 - * @return - */ - public static int getMonthDayNumber(int year, int month) { - int number = 31; - // 判断返回的标识数字 - switch(month) { - case 1: - case 3: - case 5: - case 7: - case 8: - case 10: - case 12: - number = 31; - break; - case 2: - if(isLeapYear(year)) { - number = 29; - } else { - number = 28; - } - break; - case 4: - case 6: - case 9: - case 11: - number = 30; - break; - } - return number; - } - - // ======================================================= - - /** - * 传入时间,获取时间(00:00:00 格式) - 不处理大于一天 - * @param time 时间(秒为单位) - * @return - */ - public static String secToTimeRetain(int time) { - return secToTimeRetain(time, false); - } - - /** - * 传入时间,获取时间(00:00:00 格式) - * @param time 时间(秒为单位) - * @param isHandlerMDay 是否处理大于一天的时间 - * @return - */ - public static String secToTimeRetain(int time, boolean isHandlerMDay) { - try { - if(time <= 0) { - return "00:00:00"; - } else { - // 取模 - int rSecond = 0; - int rMinute = 0; - // 差数 - int dSecond = 0; - int dMinute = 0; - int dHour = 0; - // 转换时间格式 - if(time < MINUTE_S) { // 小于1分钟 - return "00:00:" + ((time >=10)?time:("0" + time)); - } else if(time >= MINUTE_S && time < HOUR_S) { // 小于1小时 - dSecond = time % MINUTE_S; // 取模分钟,获取多出的秒数 - dMinute = (time - dSecond) / MINUTE_S; - return "00:" + ((dMinute >=10)?dMinute:("0" + dMinute)) + ":" + ((dSecond >=10)?dSecond:("0" + dSecond)); - } else if(time >= HOUR_S && time < DAY_S) { // 小于等于一天 - rMinute = time % HOUR_S; // 取模小时,获取多出的分钟 - dHour = (time - rMinute) / HOUR_S; // 获取小时 - dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 - dMinute = dSecond / MINUTE_S; // 获取多出的分钟 - rSecond = dSecond % MINUTE_S; // 取模分钟,获取多余的秒速 - return ((dHour >= 10) ? dHour : ("0" + dHour)) + ":" + ((dMinute >= 10) ? dMinute:("0" + dMinute)) + ":" + ((rSecond >= 10) ? rSecond:"0" + rSecond); - } else { // 多余的时间,直接格式化 - // 大于一天的情况 - if(isHandlerMDay) { - rMinute = time % HOUR_S; // 取模小时,获取多出的分钟 - dHour = (time - rMinute) / HOUR_S; // 获取小时 - dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 - dMinute = dSecond / MINUTE_S; // 获取多出的分钟 - rSecond = dSecond % MINUTE_S; // 取模分钟,获取多余的秒速 - return ((dHour >= 10) ? dHour : ("0" + dHour)) + ":" + ((dMinute >= 10) ? dMinute:("0" + dMinute)) + ":" + ((rSecond >= 10) ? rSecond:"0" + rSecond); - } - } - } - } catch (Exception e) { - } - return null; - } - - /** - * 传入时间,时间参数(小时、分钟、秒) - * @param time 时间(秒为单位) - * @return - */ - public static int[] convertTimeArys(int time) { - try { - if(time <= 0) { - return new int[] { 0, 0, 0 }; - } else { - // 取模 - int rSecond = 0; - int rMinute = 0; - // 差数 - int dSecond = 0; - int dMinute = 0; - int dHour = 0; - // 转换时间格式 - if(time < MINUTE_S) { // 小于1分钟 - return new int[]{ 0, 0, time }; - } else if(time >= MINUTE_S && time < HOUR_S) { // 小于1小时 - dSecond = time % MINUTE_S; // 取模分钟,获取多出的秒数 - dMinute = (time - dSecond) / MINUTE_S; - return new int[]{ 0, dMinute, dSecond }; - } else if(time >= HOUR_S && time < DAY_S) { // 小于等于一天 - rMinute = time % HOUR_S; // 取模小时,获取多出的分钟 - dHour = (time - rMinute) / HOUR_S; // 获取小时 - dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 - dMinute = dSecond / MINUTE_S; // 获取多出的分钟 - rSecond = dSecond % MINUTE_S; // 取模分钟,获取多余的秒速 - return new int[]{ dHour, dMinute, rSecond }; - } else { // 多余的时间,直接格式化 - // 大于一天的情况 - rMinute = time % HOUR_S; // 取模小时,获取多出的分钟 - dHour = (time - rMinute) / HOUR_S; // 获取小时 - dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 - dMinute = dSecond / MINUTE_S; // 获取多出的分钟 - rSecond = dSecond % MINUTE_S; // 取模分钟,获取多余的秒速 - return new int[]{ dHour, dMinute, rSecond }; - } - } - } catch (Exception e) { - } - return null; - } - - /** - * 转换时间 - * @param millis - * @param precision - * precision = 0, return null - * precision = 1, return 天 - * precision = 2, return 天, 小时 - * precision = 3, return 天, 小时, 分钟 - * precision = 4, return 天, 小时, 分钟, 秒 - * precision = 5,return 天, 小时, 分钟, 秒, 毫秒 - * @return fit time span - */ - public static String millis2FitTimeSpan(long millis, int precision) { - if (millis <= 0 || precision <= 0) return null; - StringBuilder sb = new StringBuilder(); - String[] units = {"天", "小时", "分钟", "秒", "毫秒"}; - int[] unitLen = {86400000, 3600000, 60000, 1000, 1}; - precision = Math.min(precision, 5); - for (int i = 0; i < precision; i++) { - if (millis >= unitLen[i]) { - long mode = millis / unitLen[i]; - millis -= mode * unitLen[i]; - sb.append(mode).append(units[i]); - } - } - return sb.toString(); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/DevCommonUtils.java b/DevLibUtils/src/main/java/dev/utils/common/DevCommonUtils.java deleted file mode 100644 index 58d67fd827..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/DevCommonUtils.java +++ /dev/null @@ -1,1255 +0,0 @@ -package dev.utils.common; - -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; - -import dev.utils.JCLogUtils; - -/** - * detail: 开发常用方法 - 工具类 - * Created by Ttt - */ -public final class DevCommonUtils { - - private DevCommonUtils() { - } - - // 日志TAG - private static final String TAG = DevCommonUtils.class.getSimpleName(); - /** 换行字符串 */ - public static final String NEW_LINE_STR = System.getProperty("line.separator"); - /** 换行字符串 - 两行 */ - public static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; - - /** - * 判断是否网络资源 - * @param resPath 资源地址 - * @return - */ - public static boolean isHttpRes(String resPath){ - if (!isEmpty(resPath)){ - // 属于第一位开始, 才是属于网络资源 - if (resPath.toLowerCase().startsWith("http:") || - resPath.toLowerCase().startsWith("https:")){ - return true; - } - } - return false; - } - - /** - * 判断字符串是否为 null 或全为空白字符 - * @param str 待校验字符串 - * @return - */ - public static boolean isSpace(final String str) { - if (str == null) return true; - for (int i = 0, len = str.length(); i < len; ++i) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } - - /** - * 获取空格 - * @param number 空格数量 - * @return - */ - private static String getSpace(int number){ - StringBuffer buffer = new StringBuffer(); - // 循环空格 - for (int i = 0; i < number; i++){ - buffer.append(" "); - } - return buffer.toString(); - } - - /** - * 获取 Tab - * @param number tab 键数量 - * @return - */ - private static String getTab(int number){ - StringBuffer buffer = new StringBuffer(); - // 循环空格 - for (int i = 0; i < number; i++){ - buffer.append("\t"); - } - return buffer.toString(); - } - - // ======================== - // == 判断数据是否为null == - // ======================== - - /** - * 判断是否为null - * @param str - * @return - */ - public static boolean isEmpty(String str) { - return (str == null || str.length() == 0); - } - - /** - * 判断字符串是否为 null 或全为空格 - * @param str - * @return - */ - public static boolean isTrimEmpty(final String str) { - return (str == null || str.trim().length() == 0); - } - - /** - * 判断多个字符串是否为null - * @param strs - * @return - */ - public static boolean isEmpty(String... strs){ - if (strs != null && strs.length != 0){ - for (int i = 0, len = strs.length; i < len; i++){ - if (isEmpty(strs[i])){ - return true; - } - } - return false; - } - // 默认表示属于null - return true; - } - - // ==================== - // 单独需要判断是否为null,而不需要判断长度,只需要调用length方法 - // length(list, -1) == -1 => true 表示为null, false 表示不为null,存在数据(可能返回0) - // ==================== - - /** - * 判断是否为null to Object - * @param obj - * @return - */ - public static boolean isEmpty(Object obj){ - if (obj != null){ - // 判断是否属于基本类型数组 - if (obj.getClass().isArray()){ - try { - Class cla = obj.getClass(); - // == 基本数据类型 == - if (cla.isAssignableFrom(int[].class)){ - return (((int[]) obj).length == 0); - } else if (cla.isAssignableFrom(boolean[].class)){ - return (((boolean[]) obj).length == 0); - } else if (cla.isAssignableFrom(long[].class)){ - return (((long[]) obj).length == 0); - } else if (cla.isAssignableFrom(double[].class)){ - return (((double[]) obj).length == 0); - } else if (cla.isAssignableFrom(float[].class)){ - return (((float[]) obj).length == 0); - } else if (cla.isAssignableFrom(byte[].class)){ - return (((byte[]) obj).length == 0); - } else if (cla.isAssignableFrom(char[].class)){ - return (((char[]) obj).length == 0); - } else if (cla.isAssignableFrom(short[].class)){ - return (((short[]) obj).length == 0); - } - } catch (Exception e){ - } - } - return false; - } - return true; - } - - /** - * 判断是否为null to 数组 - * @param objs - * @return - */ - public static boolean isEmpty(Object[] objs){ - if (objs != null){ - return (objs.length == 0); - } - return true; - } - - /** - * 判读是否为null to List - * @param list - * @return - */ - public static boolean isEmpty(List list){ - if (list != null){ - return (list.size() == 0); - } - return true; - } - - /** - * 判读是否为null to Map - * @param map - * @return - */ - public static boolean isEmpty(Map map){ - if (map != null){ - return (map.size() == 0); - } - return true; - } - - /** - * 判读是否为null to Set - * @param set - * @return - */ - public static boolean isEmpty(Set set){ - if (set != null){ - return (set.size() == 0); - } - return true; - } - - /** - * 判读是否为null to Queue - * @param queue - * @return - */ - public static boolean isEmpty(Queue queue){ - if (queue != null){ - return (queue.size() == 0); - } - return true; - } - - /** - * 判读是否为null to 可变数组 - * @param args - * @return - */ - public static boolean isEmptyObjs(Object... args){ - if (args != null){ - return (args.length == 0); - } - return true; - } - - // ================== - // == 判断数据长度 == - // ================== - - /** - * 获取长度,如果字符串为null,则返回0 - * @param str - * @return - */ - public static int length(String str) { - return str == null ? 0 : str.length(); - } - - /** - * 获取数组长度 - * @param objs - * @return - */ - public static int length(Object[] objs){ - return length(objs, 0); - } - - /** - * 获取长度 to List - * @param list - * @return - */ - public static int length(List list){ - return length(list, 0); - } - - /** - * 获取长度 to Map - * @param map - * @return - */ - public static int length(Map map){ - return length(map, 0); - } - - /** - * 获取长度 to Set - * @param set - * @return - */ - public static int length(Set set){ - return length(set, 0); - } - - /** - * 获取长度 to Queue - * @param queue - * @return - */ - public static int length(Queue queue){ - return length(queue, 0); - } - - // = - - /** - * 获取字符串长度 - * @param str - * @param dfLength - * @return - */ - public static int length(String str, int dfLength) { - if (str != null){ - return str.length(); - } - return dfLength; - } - - /** - * 获取数组长度 - * @param objs - * @param dfLength - * @return - */ - public static int length(Object[] objs, int dfLength){ - if (objs != null){ - return objs.length; - } - return dfLength; - } - - /** - * 获取长度 to List - * @param list - * @param dfLength - * @return - */ - public static int length(List list, int dfLength){ - if (list != null){ - return list.size(); - } - return dfLength; - } - - /** - * 获取长度 to Map - * @param map - * @param dfLength - * @return - */ - public static int length(Map map, int dfLength){ - if (map != null){ - return map.size(); - } - return dfLength; - } - - /** - * 获取长度 to Set - * @param set - * @param dfLength - * @return - */ - public static int length(Set set, int dfLength){ - if (set != null){ - return set.size(); - } - return dfLength; - } - - /** - * 获取长度 to Queue - * @param queue - * @param dfLength - * @return - */ - public static int length(Queue queue, int dfLength){ - if (queue != null){ - return queue.size(); - } - return dfLength; - } - - // == - - /** - * 获取可变数组长度 - * @param args - * @return - */ - public static int lengthObjs(Object... args){ - return lengthObjsDf(0, args); - } - - /** - * 获取可变数组长度 - * @param args - * @param dfLength - * @return - */ - public static int lengthObjsDf(int dfLength, Object... args){ - if (args != null){ - return args.length; - } - return dfLength; - } - - // === - - /** - * 字符串长度匹配 - * @param str 源字符串 - * @param length 期望长度 - * @return - */ - public static boolean isLength(String str, int length) { - return str != null && str.length() == length; - } - - /** - * 获取数组长度 是否等于 期望长度 - * @param objs - * @param length - * @return - */ - public static boolean isLength(Object[] objs, int length){ - return objs != null && objs.length == length; - } - - /** - * 获取长度 to List 是否等于 期望长度 - * @param list - * @param length - * @return - */ - public static boolean isLength(List list, int length){ - return list != null && list.size() == length; - } - - /** - * 获取长度 to Map 是否等于 期望长度 - * @param map - * @param length - * @return - */ - public static boolean isLength(Map map, int length){ - return map != null && map.size() == length; - } - - /** - * 获取长度 to Set 是否等于 期望长度 - * @param set - * @param length - * @return - */ - public static boolean isLength(Set set, int length){ - return set != null && set.size() == length; - } - - /** - * 获取长度 to Queue 是否等于 期望长度 - * @param queue - * @param length - * @return - */ - public static boolean isLength(Queue queue, int length){ - return queue != null && queue.size() == length; - } - - // === - - /** - * 判断两字符串是否相等 - * @param a 待校验字符串 a - * @param b 待校验字符串 b - * @return true : 相等, false : 不相等 - */ - public static boolean equals(final CharSequence a, final CharSequence b) { - if (a == b) return true; - int length; - if (a != null && b != null && (length = a.length()) == b.length()) { - if (a instanceof String && b instanceof String) { - return a.equals(b); - } else { - for (int i = 0; i < length; i++) { - if (a.charAt(i) != b.charAt(i)) return false; - } - return true; - } - } - return false; - } - - /** - * 判断多个字符串是否相等, 只有全相等才返回true - 对比大小写 - * @param args - * @return - */ - public static boolean isEquals(String... args) { - return isEquals(false, args); - } - - /** - * 判断多个字符串是否相等, 只有全相等才返回true - * @param isIgnore 是否忽略大小写 - * @param args - * @return - */ - public static boolean isEquals(boolean isIgnore, String... args) { - if (args != null) { - String last = null; - // 获取数据长度 - int len = args.length; - // 如果最多只有一个数据判断,则直接跳过 - if (len <= 1){ - return false; - } - // 遍历判断 - for (int i = 0; i < len; i++) { - // 获取临时变量 - String val = args[i]; - // 如果等于null,则跳过 - if (val == null) { - return false; - } - if (last != null) { - if (isIgnore) { - if (!val.equalsIgnoreCase(last)) { - return false; - } - } else { - if (!val.equals(last)) { - return false; - } - } - } - last = val; - } - return true; - } - return false; - } - - /** - * 判断多个字符串,只要有一个符合条件,则通过 - * @param content - * @param args - * @return - */ - public static boolean isOrEquals(String content, String... args){ - return isOrEquals(false, content, args); - } - - /** - * 判断多个字符串,只要有一个符合条件,则通过 - * @param isIgnore 是否忽略大小写 - * @param content - * @param args - * @return - */ - public static boolean isOrEquals(boolean isIgnore, String content, String... args){ - if (content != null && args != null && args.length != 0) { - // 获取数据长度 - int len = args.length; - // 如果最多只有一个数据判断,则直接跳过 - if (len <= 1){ - return false; - } - // 遍历判断 - for (int i = 0; i < len; i++) { - // 获取临时变量 - String val = args[i]; - // 如果等于null,则跳过 - if (val == null) { - continue; - } else { - if (isIgnore) { - if (val.equalsIgnoreCase(content)) { - return true; - } - } else { - if (val.equals(content)) { - return true; - } - } - } - } - } - return false; - } - - /** - * 判断一堆值中,是否存在符合该条件的(包含) - * @param content - * @param args - * @return - */ - public static boolean isContains(String content, String... args){ - return isContains(false, content, args); - } - - /** - * 判断一堆值中,是否存在符合该条件的(包含) - * @param isIgnore - * @param content - * @param args - * @return - */ - public static boolean isContains(boolean isIgnore, String content, String... args){ - if (content != null && args != null && args.length != 0){ - // 判断是否需要忽略大小写 - if (isIgnore){ - content = content.toLowerCase(); - } - // 获取内容长度 - int cLength = content.length(); - // 遍历判断 - for (int i = 0, len = args.length; i < len; i++){ - // 获取参数 - String val = args[i]; - // 判断是否为null,或者长度为0 - if (!isEmpty(val) && cLength != 0){ - if (isIgnore) { - // 转换小写 - String valIgnore = val.toLowerCase(); - // 判断是否包含 - if (valIgnore.indexOf(content) != -1) { - return true; - } - } else { - // 判断是否包含 - if (val.indexOf(content) != -1) { - return true; - } - } - } else { - // 下面这一串可以不要,因为判断字符串是否包含 - // 已经处理了值不为null,并且需要判断的值长度不能为0,下面则不需要加上 - if (content.equals(val)){ - return true; - } - } - } - } - return false; - } - - /** - * 判断内容, 是否属于特定字符串数组开头 - 对比大小写 - * @param content - * @param args - * @return - */ - public static boolean isStartsWith(String content, String... args) { - return isStartsWith(false, content, args); - } - - /** - * 判断内容, 是否属于特定字符串数组开头 - * @param isIgnore 是否忽略大小写 - * @param content - * @param args - * @return - */ - public static boolean isStartsWith(boolean isIgnore, String content, String... args) { - if (!isEmpty(content) && args != null && args.length != 0){ - // 判断是否需要忽略大小写 - if (isIgnore){ - content = content.toLowerCase(); - } - // 获取数据长度 - int len = args.length; - // 遍历判断 - for (int i = 0; i < len; i++) { - // 获取临时变量 - String val = args[i]; - // 判断是否为null,或者长度为0 - if (!isEmpty(val)){ - if (isIgnore) { - // 转换小写 - String valIgnore = val.toLowerCase(); - // 判断是否属于 val 开头 - if (content.startsWith(valIgnore)) { - return true; - } - } else { - // 判断是否属于 val 开头 - if (content.startsWith(val)) { - return true; - } - } - } - } - } - return false; - } - - /** - * 判断内容, 是否属于特定字符串数组结尾 - 对比大小写 - * @param content - * @param args - * @return - */ - public static boolean isEndsWith(String content, String... args) { - return isEndsWith(false, content, args); - } - - /** - * 判断内容, 是否属于特定字符串数组结尾 - * @param isIgnore 是否忽略大小写 - * @param content - * @param args - * @return - */ - public static boolean isEndsWith(boolean isIgnore, String content, String... args) { - if (!isEmpty(content) && args != null && args.length != 0){ - // 判断是否需要忽略大小写 - if (isIgnore){ - content = content.toLowerCase(); - } - // 获取数据长度 - int len = args.length; - // 遍历判断 - for (int i = 0; i < len; i++) { - // 获取临时变量 - String val = args[i]; - // 判断是否为null,或者长度为0 - if (!isEmpty(val)){ - if (isIgnore) { - // 转换小写 - String valIgnore = val.toLowerCase(); - // 判断是否属于 val 结尾 - if (content.endsWith(valIgnore)) { - return true; - } - } else { - // 判断是否属于 val 结尾 - if (content.endsWith(val)) { - return true; - } - } - } - } - } - return false; - } - - // -- - - /** - * 清空全部空格,并返回处理后的字符串 - * @param str - * @return - */ - public static String toClearSpace(String str) { - if (isEmpty(str)) { - return str; - } - return str.replaceAll(" ", ""); - } - - /** - * 清空前后空格,并返回处理后的字符串 - * @param str - * @return - */ - public static String toClearSpaceTrim(String str) { - if (isEmpty(str)) { - return str; - } - // 如果前面或者后面都是 空格开头,就一直进行处理 - while(str.startsWith(" ") || str.endsWith(" ")) { - str = str.trim(); - } - return str; - } - - // == - - /** - * 检查字符串,如果为null,返回 "" - * @param str - * @return - */ - public static String toCheckValue(String str) { - return toCheckValue("", str); - } - - /** - * 检查字符串,如果为null,返回 默认字符串 - * @param dfStr - * @param str - * @return - */ - public static String toCheckValue(String dfStr, String str) { - return isEmpty(str) ? dfStr : str; - } - - /** - * 单独检查两个值,减少循环,不直接调用toCheckValues - * @param dfStr - * @param value1 - * @param value2 - * @return - */ - public static String toCheckValue(String dfStr, String value1, String value2){ - if (isEmpty(value1)){ - if (isEmpty(value2)){ - return dfStr; - } else { - return value2; - } - } else { - return value1; - } - } - - /** - * 检查多个值,并返回第一个非null and "" 的字符串,如果都不符合条件,则返回默认值 - * @param dfStr - * @param params - * @return - */ - public static String toCheckValues(String dfStr, String... params){ - if (params != null && params.length != 0){ - for (int i = 0, len = params.length; i < len; i++){ - String param = params[i]; - if (isEmpty(param)){ - if (i == len - 1){ - return dfStr; // 属于最后一个,则返回默认值 - } else { - continue; // 不属于最后一个则跳过 - } - } else { - return param; - } - } - } - return dfStr; - } - - /** - * 检查多个值,并返回第一个非null and "" and 全部不是属于空格 的字符串,如果都不符合条件,则返回默认值 - * @param dfStr - * @param params - * @return - */ - public static String toCheckValuesSpace(String dfStr, String... params){ - if (params != null && params.length != 0){ - for (int i = 0, len = params.length; i < len; i++){ - // 处理后,进行返回 => 删除前后空格 - String param = toClearSpaceTrim(params[i]); - if (isEmpty(param)){ - if (i == len - 1){ - return dfStr; // 属于最后一个,则返回默认值 - } else { - continue; // 不属于最后一个则跳过 - } - } else { - return param; - } - } - } - return dfStr; - } - - /** - * 裁剪符号处理 - * @param start 开始位置 - * @param symbolNumber 转换数量 - * @param content - * @param symbol - * @return - */ - public static String subSymbolHide(int start, int symbolNumber, String content, String symbol){ - if (!isEmpty(content)){ - if (start <= 0 || symbolNumber <= 0){ - return content; - } - // 获取数据长度 - int length = content.length(); - // 如果数据小于 start 位则直接返回 - if (length <= start){ - return content; - } else { // 大于 start 位 - StringBuffer stringBuffer = new StringBuffer(); - stringBuffer.append(content.substring(0, start)); - int len = length - start - symbolNumber; - // 如果超出总长度, 则进行控制 - if (len <= 0){ // 表示后面的全部转换 - len = length - start; - } else { // 需要裁剪的数量 - len = symbolNumber; - } - // 进行遍历保存 - for (int i = 0; i < len; i++) { - stringBuffer.append(symbol); - } - stringBuffer.append(content.substring(start + len, length)); - return stringBuffer.toString(); - } - } - return ""; - } - - /** - * 转换符号处理 - * @param start 开始位置 - * @param content - * @param symbol - * @return - */ - public static String converSymbolHide(int start, String content, String symbol){ - if (!isEmpty(content)){ - if (start <= 0){ - return content; - } - // 获取数据长度 - int length = content.length(); - // 如果数据小于 start 位则直接返回 - if (length <= start){ - return content; - } else { // 大于 start 位 - StringBuffer stringBuffer = new StringBuffer(); - stringBuffer.append(content.substring(0, start)); - int len = length - start; - // 进行平分 - len /= 2; - // 进行遍历保存 - for (int i = 0; i < len; i++){ - stringBuffer.append(symbol); - } - stringBuffer.append(content.substring(start + len, length)); - return stringBuffer.toString(); - } - } - return ""; - } - - // == 替换截取操作 == - - /** - * 替换(删除 - 替换成"") 字符串中符合 特定标记字符的 startsWith - endsWith - * * 如 _____a_a_a_a________ 传入 _ 等于 ____a_a_a_a____ - * @param str - * @param key 需要判断的标记 - * @return - */ - public static String toReplaceSEWith(String str, String key){ - return toReplaceSEWith(str, key, ""); - } - - /** - * 替换字符串中符合 特定标记字符的 startsWith - endsWith - * 如 _____a_a_a_a________ 传入 _ , c 等于 c____a_a_a_a____c - * @param str - * @param key 需要判断的标记 - * @param value 需要替换的内容 - * @return - */ - public static String toReplaceSEWith(String str, String key, String value){ - if (!(!isEmpty(str) && !isEmpty(key) && !isEmpty(value) && !key.equals(value))) { - return str; - } - try { - // 获取编辑内容长度 - int kLength = key.length(); - // 保存新的Buffer中,减少内存开销 - StringBuffer sBuffer = new StringBuffer(str); - // 判断是否在最头部 - if (sBuffer.indexOf(key) == 0) { - sBuffer.delete(0, kLength); - // 追加内容 - sBuffer.insert(0, value); - } - // 获取尾部的位置 - int lastIndexOf = -1; - // 数据长度 - int bufLength = -1; - // 判断是否在最尾部 - if ((lastIndexOf = sBuffer.lastIndexOf(key)) == ((bufLength = sBuffer.length()) - kLength)) { - sBuffer.delete(lastIndexOf, bufLength); - // 追加内容 - sBuffer.insert(lastIndexOf, value); - } - return sBuffer.toString(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toReplaceSEWith"); - } - return str; - } - - /** - * (这个方法功能主要把字符符合标记的 头部和尾部都替换成 "") - * 如 _____a_a_a_a________ 传入 _ 等于 a_a_a_a - * 替换字符串中符合 特定标记字符的 startsWith(indexOf) - endsWith(lastIndexOf) ,while - * @param str - * @param key 需要判断的标记 - * @return - */ - public static String toClearSEWiths(String str, String key){ - if (!(!isEmpty(str) && !isEmpty(key))) { - return str; - } - try { - // 获取编辑内容长度 - int kLength = key.length(); - // 保存新的Buffer中,减少内存开销 - StringBuffer sBuffer = new StringBuffer(str); - // 进行循环判断 - 属于最前面的,才进行处理 - while (sBuffer.indexOf(key) == 0) { - sBuffer.delete(0, kLength); - } - // 获取尾部的位置 - int lastIndexOf = -1; - // 数据长度 - int bufLength = -1; - // 进行循环判断 - 属于最后面的,才进行处理 - while ((lastIndexOf = sBuffer.lastIndexOf(key)) == ((bufLength = sBuffer.length()) - kLength)) { - sBuffer.delete(lastIndexOf, bufLength); - } - return sBuffer.toString(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toClearSEWiths"); - } - return str; - } - - /** - * 裁剪字符串 - * @param content 需要裁剪的字符串 - * @param endIndex 结束裁剪的位置 - * @return - */ - public static String substring(String content, int endIndex){ - return substring(content, 0, endIndex, true); - } - - /** - * 裁剪字符串 - * @param content 需要裁剪的字符串 - * @param endIndex 结束裁剪的位置 - * @param isReturn 开始位置超过限制是否返回内容 - * @return - */ - public static String substring(String content, int endIndex, boolean isReturn){ - return substring(content, 0, endIndex, isReturn); - } - - /** - * 裁剪字符串 - * @param content 需要裁剪的字符串 - * @param beginIndex 开始裁剪的位置 - * @param endIndex 结束裁剪的位置 - * @param isReturn 开始位置超过限制是否返回内容 - * @return - */ - public static String substring(String content, int beginIndex, int endIndex, boolean isReturn){ - if (!isEmpty(content) && beginIndex >= 0 && endIndex >= 0 && endIndex >= beginIndex){ - // 获取数据长度 - int len = length(content); - // 防止超过限制 - if (beginIndex > len){ - return isReturn ? content : ""; - } - // 防止超过限制 - if (endIndex >= len){ - endIndex = len; - } - return content.substring(beginIndex, endIndex); - } - return isReturn ? content : ""; - } - - // == - - /** - * 替换开头字符串 - * @param str 内容 - * @param key 需要判断的kye - * @return - */ - public static String toReplaceStartsWith(String str, String key){ - return toReplaceStartsWith(str, key, ""); - } - - /** - * 替换开头字符串 - * @param str 内容 - * @param key 需要判断的kye - * @param value 需要替换的内容 - * @return - */ - public static String toReplaceStartsWith(String str, String key, String value){ - if (!isEmpty(str) && !isEmpty(key)) { - try { - if (str.startsWith(key)){ - return value + str.substring(key.length(), str.length()); - } - } catch (Exception e) { - } - } - return str; - } - - /** - * 清空属于特定字符串开头的字段 - * 如 _____a_a_a_a________ 传入 _ 等于 a_a_a_a_____ - * 替换字符串中符合 特定标记字符的 endsWith(lastIndexOf) ,while - * @param str - * @param key 需要判断的标记 - * @return - */ - public static String toClearStartsWith(String str, String key){ - if (!(!isEmpty(str) && !isEmpty(key))) { - return str; - } - try { - // 获取编辑内容长度 - int kLength = key.length(); - // 保存新的Buffer中,减少内存开销 - StringBuffer sBuffer = new StringBuffer(str); - // 进行循环判断 - 属于最前面的,才进行处理 - while (sBuffer.indexOf(key) == 0) { - sBuffer.delete(0, kLength); - } - return sBuffer.toString(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toClearStartsWith"); - } - return str; - } - - // == - - /** - * 替换结尾字符串 - * @param content 内容 - * @param key 需要判断的kye - * @return - */ - public static String toReplaceEndsWith(String content, String key){ - return toReplaceEndsWith(content, key, ""); - } - - /** - * 替换结尾字符串 - * @param content 内容 - * @param key 需要判断的kye - * @param value 需要替换的内容 - * @return - */ - public static String toReplaceEndsWith(String content, String key, String value){ - if (!isEmpty(content) && !isEmpty(key)) { - try { - if (content.endsWith(key)){ - return content.substring(0, content.length() - key.length()) + value; - } - } catch (Exception e) { - } - } - return content; - } - - /** - * 清空属于特定字符串结尾的字段 - * 如 _____a_a_a_a________ 传入 _ 等于 _____a_a_a_a - * 替换字符串中符合 特定标记字符的 endsWith(lastIndexOf) ,while - * @param str - * @param key 需要判断的标记 - * @return - */ - public static String toClearEndsWith(String str, String key){ - if (!(!isEmpty(str) && !isEmpty(key))) { - return str; - } - try { - // 获取编辑内容长度 - int kLength = key.length(); - // 保存新的Buffer中,减少内存开销 - StringBuffer sBuffer = new StringBuffer(str); - // 获取最后一位位置 - int sLength = 0; - // 进行循环判断 - 属于最前面的,才进行处理 - while (sBuffer.lastIndexOf(key) == ((sLength = sBuffer.length()) - kLength)) { - sBuffer.delete(sLength - kLength, sLength); - } - return sBuffer.toString(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toClearEndsWith"); - } - return str; - } - - // === - - /** - * 替换字符串 - * @param content 内容 - * @param - * @return - */ - public static String replaceStrs(String content, String[] spArys, String[] reArys){ - // 防止数据为null - if (content != null && spArys != null && reArys != null){ - String cStr = content; - // 替换的特殊字符串长度 - int spCount = spArys.length; - // 替换的内容长度 - int reCount = reArys.length; - // 相同才进行处理 - if (spCount == reCount){ - // 遍历进行判断 - for (int i = 0; i < spCount; i++){ - // 进行替换字符串 - cStr = replaceStr(cStr, spArys[i], reArys[i]); - } - // 最终不为null,则进行返回 - return cStr; - } - } - return null; - } - - /** - * 替换字符串 - * @param content 需要替换的内容 - * @param spStr 特殊的字符串 - * @param reStr 替换的内容 - * @return - */ - public static String replaceStr(String content, String spStr, String reStr){ - // 如果替换的内容或者判断的字符串为null,则直接跳过 - if (!isEmpty(content) && !isEmpty(spStr) && reStr != null){ - try { - return content.replaceAll(spStr, reStr); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "replaceStr"); - } - } - return content; - } - - /** - * 替换字符串 - * @param content 需要替换的内容 - * @param spStr 特殊的字符串 - * @param reStr 替换的内容 - * @return 如果异常则直接返回null - */ - public static String replaceStrToNull(String content, String spStr, String reStr){ - // 如果替换的内容或者判断的字符串为null,则直接跳过 - if (!isEmpty(content) && !isEmpty(spStr) && reStr != null){ - try { - return content.replaceAll(spStr, reStr); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "replaceStrToNull"); - } - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/FieldUtils.java b/DevLibUtils/src/main/java/dev/utils/common/FieldUtils.java deleted file mode 100644 index d259d8bf11..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/FieldUtils.java +++ /dev/null @@ -1,133 +0,0 @@ -package dev.utils.common; - -import java.io.Serializable; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.LinkedList; -import java.util.List; - -import dev.utils.JCLogUtils; - -/** - * detail: 域工具 - * @author mty - */ -public final class FieldUtils { - - private FieldUtils(){ - } - - // 日志TAG - private static final String TAG = FieldUtils.class.getSimpleName(); - - /** - * 判断是否序列化 - * @param f - * @return - */ - public static boolean isSerializable(Field f) { - Class[] cls = f.getType().getInterfaces(); - for (Class c : cls) { - if (Serializable.class == c) { - return true; - } - } - return false; - } - - /** - * 设置域的值 - * @param f - * @param obj - * @return - */ - public static Object set(Field f, Object obj, Object value) { - try { - f.setAccessible(true); - f.set(obj, value); - return f.get(obj); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "set"); - } - return null; - } - - /** - * 获取域的值 - * @param f - * @param obj - * @return - */ - public static Object get(Field f, Object obj) { - try { - f.setAccessible(true); - return f.get(obj); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "get"); - } - return null; - } - - public static boolean isLong(Field field) { - return field.getType() == long.class || field.getType() == Long.class; - } - - public static boolean isInteger(Field field) { - return field.getType() == int.class || field.getType() != Integer.class; - } - - /** - * 获取域的泛型类型,如果不带泛型返回null - * @param f - * @return - */ - public static Class getGenericType(Field f) { - Type type = f.getGenericType(); - if (type instanceof ParameterizedType) { - type = ((ParameterizedType) type).getActualTypeArguments()[0]; - if (type instanceof Class) return (Class) type; - } else if (type instanceof Class) return (Class) type; - return null; - } - - /** - * 获取数组的类型 - * @param f - * @return - */ - public static Class getComponentType(Field f) { - return f.getType().getComponentType(); - } - - /** - * 获取全部Field,包括父类 - * @param claxx - * @return - */ - public static List getAllDeclaredFields(Class claxx) { - // find all field. - LinkedList fieldList = new LinkedList(); - while (claxx != null && claxx != Object.class) { - Field[] fs = claxx.getDeclaredFields(); - for (int i = 0; i < fs.length; i++) { - Field f = fs[i]; - if (!isInvalid(f)) { - fieldList.addLast(f); - } - } - claxx = claxx.getSuperclass(); - } - return fieldList; - } - - /** - * 是静态常量或者内部结构属性 - * @param f - * @return - */ - public static boolean isInvalid(Field f) { - return (Modifier.isStatic(f.getModifiers()) && Modifier.isFinal(f.getModifiers())) || f.isSynthetic(); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/FileIOUtils.java b/DevLibUtils/src/main/java/dev/utils/common/FileIOUtils.java deleted file mode 100644 index 57bd51669f..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/FileIOUtils.java +++ /dev/null @@ -1,674 +0,0 @@ -package dev.utils.common; - -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.ArrayList; -import java.util.List; - -import dev.utils.JCLogUtils; - -/** - * detail: 文件IO流工具类 - * Created by Ttt - */ -public final class FileIOUtils { - - private FileIOUtils() { - } - - // 日志TAG - private static final String TAG = FileIOUtils.class.getSimpleName(); - - // 换行符 - private static final String LINE_SEP = System.getProperty("line.separator"); - // 缓存大小 - private static int sBufferSize = 8192; - - /** - * Set the buffer's size. - Default size equals 8192 bytes. - * @param bufferSize The buffer's size. - */ - public static void setBufferSize(final int bufferSize) { - sBufferSize = bufferSize; - } - - /** - * Write file from input stream. - * @param filePath The path of file. - * @param is The input stream. - * @return true : success, false : fail - */ - public static boolean writeFileFromIS(final String filePath, final InputStream is) { - return writeFileFromIS(getFileByPath(filePath), is, false); - } - - /** - * Write file from input stream. - * @param filePath The path of file. - * @param is The input stream. - * @param append True to append, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromIS(final String filePath, final InputStream is, final boolean append) { - return writeFileFromIS(getFileByPath(filePath), is, append); - } - - /** - * Write file from input stream. - * @param file The file. - * @param is The input stream. - * @return true : success, false : fail - */ - public static boolean writeFileFromIS(final File file, final InputStream is) { - return writeFileFromIS(file, is, false); - } - - /** - * Write file from input stream. - * @param file The file. - * @param is The input stream. - * @param append True to append, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromIS(final File file, final InputStream is, final boolean append) { - if (!createOrExistsFile(file) || is == null) return false; - OutputStream os = null; - try { - os = new BufferedOutputStream(new FileOutputStream(file, append)); - byte data[] = new byte[sBufferSize]; - int len; - while ((len = is.read(data, 0, sBufferSize)) != -1) { - os.write(data, 0, len); - } - return true; - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "writeFileFromIS"); - return false; - } finally { - CloseUtils.closeIO(is, os); - } - } - - /** - * Write file from bytes by stream. - * @param filePath The path of file. - * @param bytes The bytes. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes) { - return writeFileFromBytesByStream(getFileByPath(filePath), bytes, false); - } - - /** - * Write file from bytes by stream. - * @param filePath The path of file. - * @param bytes The bytes. - * @param append True to append, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes, final boolean append) { - return writeFileFromBytesByStream(getFileByPath(filePath), bytes, append); - } - - /** - * Write file from bytes by stream. - * @param file The file. - * @param bytes The bytes. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes) { - return writeFileFromBytesByStream(file, bytes, false); - } - - /** - * Write file from bytes by stream. - * @param file The file. - * @param bytes The bytes. - * @param append True to append, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes, final boolean append) { - if (bytes == null || !createOrExistsFile(file)) return false; - BufferedOutputStream bos = null; - try { - bos = new BufferedOutputStream(new FileOutputStream(file, append)); - bos.write(bytes); - return true; - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "writeFileFromBytesByStream"); - return false; - } finally { - CloseUtils.closeIO(bos); - } - } - - /** - * Write file from bytes by channel. - * @param filePath The path of file. - * @param bytes The bytes. - * @param isForce 是否写入文件 - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByChannel(final String filePath, final byte[] bytes, final boolean isForce) { - return writeFileFromBytesByChannel(getFileByPath(filePath), bytes, false, isForce); - } - - /** - * Write file from bytes by channel. - * @param filePath The path of file. - * @param bytes The bytes. - * @param append True to append, false otherwise. - * @param isForce True to force write file, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByChannel(final String filePath, final byte[] bytes, final boolean append, final boolean isForce) { - return writeFileFromBytesByChannel(getFileByPath(filePath), bytes, append, isForce); - } - - /** - * Write file from bytes by channel. - * @param file The file. - * @param bytes The bytes. - * @param isForce True to force write file, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByChannel(final File file, final byte[] bytes, final boolean isForce) { - return writeFileFromBytesByChannel(file, bytes, false, isForce); - } - - /** - * Write file from bytes by channel. - * @param file The file. - * @param bytes The bytes. - * @param append True to append, false otherwise. - * @param isForce True to force write file, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByChannel(final File file, final byte[] bytes, final boolean append, final boolean isForce) { - if (bytes == null) return false; - FileChannel fc = null; - try { - fc = new FileOutputStream(file, append).getChannel(); - fc.position(fc.size()); - fc.write(ByteBuffer.wrap(bytes)); - if (isForce) fc.force(true); - return true; - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "writeFileFromBytesByChannel"); - return false; - } finally { - CloseUtils.closeIO(fc); - } - } - - /** - * Write file from bytes by map. - * @param filePath The path of file. - * @param bytes The bytes. - * @param isForce True to force write file, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByMap(final String filePath, final byte[] bytes, final boolean isForce) { - return writeFileFromBytesByMap(filePath, bytes, false, isForce); - } - - /** - * Write file from bytes by map. - * @param filePath The path of file. - * @param bytes The bytes. - * @param append True to append, false otherwise. - * @param isForce True to force write file, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByMap(final String filePath, final byte[] bytes, final boolean append, final boolean isForce) { - return writeFileFromBytesByMap(getFileByPath(filePath), bytes, append, isForce); - } - - /** - * Write file from bytes by map. - * @param file The file. - * @param bytes The bytes. - * @param isForce True to force write file, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByMap(final File file, final byte[] bytes, final boolean isForce) { - return writeFileFromBytesByMap(file, bytes, false, isForce); - } - - /** - * Write file from bytes by map. - * @param file The file. - * @param bytes The bytes. - * @param append True to append, false otherwise. - * @param isForce True to force write file, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromBytesByMap(final File file, final byte[] bytes, final boolean append, final boolean isForce) { - if (bytes == null || !createOrExistsFile(file)) return false; - FileChannel fc = null; - try { - fc = new FileOutputStream(file, append).getChannel(); - MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, fc.size(), bytes.length); - mbb.put(bytes); - if (isForce) mbb.force(); - return true; - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "writeFileFromBytesByMap"); - return false; - } finally { - CloseUtils.closeIO(fc); - } - } - - /** - * Write file from string. - * @param filePath The path of file. - * @param content The string of content. - * @return true : success, false : fail - */ - public static boolean writeFileFromString(final String filePath, final String content) { - return writeFileFromString(getFileByPath(filePath), content, false); - } - - /** - * Write file from string. - * @param filePath The path of file. - * @param content The string of content. - * @param append True to append, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromString(final String filePath, final String content, final boolean append) { - return writeFileFromString(getFileByPath(filePath), content, append); - } - - /** - * Write file from string. - * @param file The file. - * @param content The string of content. - * @return true : success, false : fail - */ - public static boolean writeFileFromString(final File file, final String content) { - return writeFileFromString(file, content, false); - } - - /** - * Write file from string. - * @param file The file. - * @param content The string of content. - * @param append True to append, false otherwise. - * @return true : success, false : fail - */ - public static boolean writeFileFromString(final File file, final String content, final boolean append) { - if (file == null || content == null) return false; - if (!createOrExistsFile(file)) return false; - BufferedWriter bw = null; - try { - bw = new BufferedWriter(new FileWriter(file, append)); - bw.write(content); - return true; - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "writeFileFromString"); - return false; - } finally { - CloseUtils.closeIO(bw); - } - } - - // ================================= - // the divide line of write and read - // ================================= - - /** - * Return the lines in file. - * @param filePath The path of file. - * @return the lines in file - */ - public static List readFile2List(final String filePath) { - return readFile2List(getFileByPath(filePath), null); - } - - /** - * Return the lines in file. - * @param filePath The path of file. - * @param charsetName The name of charset. - * @return the lines in file - */ - public static List readFile2List(final String filePath, final String charsetName) { - return readFile2List(getFileByPath(filePath), charsetName); - } - - /** - * Return the lines in file. - * @param file The file. - * @return the lines in file - */ - public static List readFile2List(final File file) { - return readFile2List(file, 0, 0x7FFFFFFF, null); - } - - /** - * Return the lines in file. - * @param file The file. - * @param charsetName The name of charset. - * @return the lines in file - */ - public static List readFile2List(final File file, final String charsetName) { - return readFile2List(file, 0, 0x7FFFFFFF, charsetName); - } - - /** - * Return the lines in file. - * @param filePath The path of file. - * @param st The line's index of start. - * @param end The line's index of end. - * @return the lines in file - */ - public static List readFile2List(final String filePath, final int st, final int end) { - return readFile2List(getFileByPath(filePath), st, end, null); - } - - /** - * Return the lines in file. - * @param filePath The path of file. - * @param st The line's index of start. - * @param end The line's index of end. - * @param charsetName The name of charset. - * @return the lines in file - */ - public static List readFile2List(final String filePath, final int st, final int end, final String charsetName) { - return readFile2List(getFileByPath(filePath), st, end, charsetName); - } - - /** - * Return the lines in file. - * @param file The file. - * @param st The line's index of start. - * @param end The line's index of end. - * @return the lines in file - */ - public static List readFile2List(final File file, final int st, final int end) { - return readFile2List(file, st, end, null); - } - - /** - * Return the lines in file. - * @param file The file. - * @param st The line's index of start. - * @param end The line's index of end. - * @param charsetName The name of charset. - * @return the lines in file - */ - public static List readFile2List(final File file, final int st, final int end, final String charsetName) { - if (!isFileExists(file)) return null; - if (st > end) return null; - BufferedReader reader = null; - try { - String line; - int curLine = 1; - List list = new ArrayList<>(); - if (isSpace(charsetName)) { - reader = new BufferedReader(new InputStreamReader(new FileInputStream(file))); - } else { - reader = new BufferedReader( new InputStreamReader(new FileInputStream(file), charsetName)); - } - while ((line = reader.readLine()) != null) { - if (curLine > end) break; - if (st <= curLine && curLine <= end) list.add(line); - ++curLine; - } - return list; - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "readFile2List"); - return null; - } finally { - CloseUtils.closeIO(reader); - } - } - - /** - * Return the string in file. - * @param filePath The path of file. - * @return the string in file - */ - public static String readFile2String(final String filePath) { - return readFile2String(getFileByPath(filePath), null); - } - - /** - * Return the string in file. - * @param filePath The path of file. - * @param charsetName The name of charset. - * @return the string in file - */ - public static String readFile2String(final String filePath, final String charsetName) { - return readFile2String(getFileByPath(filePath), charsetName); - } - - /** - * Return the string in file. - * @param file The file. - * @return the string in file - */ - public static String readFile2String(final File file) { - return readFile2String(file, null); - } - - /** - * Return the string in file. - * @param file The file. - * @param charsetName The name of charset. - * @return the string in file - */ - public static String readFile2String(final File file, final String charsetName) { - if (!isFileExists(file)) return null; - BufferedReader reader = null; - try { - StringBuilder sb = new StringBuilder(); - if (isSpace(charsetName)) { - reader = new BufferedReader(new InputStreamReader(new FileInputStream(file))); - } else { - reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName)); - } - String line; - if ((line = reader.readLine()) != null) { - sb.append(line); - while ((line = reader.readLine()) != null) { - sb.append(LINE_SEP).append(line); - } - } - return sb.toString(); - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "readFile2String"); - return null; - } finally { - CloseUtils.closeIO(reader); - } - } - - /** - * Return the bytes in file by stream. - * @param filePath The path of file. - * @return the bytes in file - */ - public static byte[] readFile2BytesByStream(final String filePath) { - return readFile2BytesByStream(getFileByPath(filePath)); - } - - /** - * Return the bytes in file by stream. - * @param file The file. - * @return the bytes in file - */ - public static byte[] readFile2BytesByStream(final File file) { - if (!isFileExists(file)) return null; - FileInputStream fis = null; - ByteArrayOutputStream os = null; - try { - fis = new FileInputStream(file); - os = new ByteArrayOutputStream(); - byte[] b = new byte[sBufferSize]; - int len; - while ((len = fis.read(b, 0, sBufferSize)) != -1) { - os.write(b, 0, len); - } - return os.toByteArray(); - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "readFile2BytesByStream"); - return null; - } finally { - CloseUtils.closeIO(fis, os); - } - } - - /** - * Return the bytes in file by channel. - * @param filePath The path of file. - * @return the bytes in file - */ - public static byte[] readFile2BytesByChannel(final String filePath) { - return readFile2BytesByChannel(getFileByPath(filePath)); - } - - /** - * Return the bytes in file by channel. - * @param file The file. - * @return the bytes in file - */ - public static byte[] readFile2BytesByChannel(final File file) { - if (!isFileExists(file)) return null; - FileChannel fc = null; - try { - fc = new RandomAccessFile(file, "r").getChannel(); - ByteBuffer byteBuffer = ByteBuffer.allocate((int) fc.size()); - while (true) { - if (!((fc.read(byteBuffer)) > 0)) break; - } - return byteBuffer.array(); - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "readFile2BytesByChannel"); - return null; - } finally { - CloseUtils.closeIO(fc); - } - } - - /** - * Return the bytes in file by map. - * @param filePath The path of file. - * @return the bytes in file - */ - public static byte[] readFile2BytesByMap(final String filePath) { - return readFile2BytesByMap(getFileByPath(filePath)); - } - - /** - * Return the bytes in file by map. - * @param file The file. - * @return the bytes in file - */ - public static byte[] readFile2BytesByMap(final File file) { - if (!isFileExists(file)) return null; - FileChannel fc = null; - try { - fc = new RandomAccessFile(file, "r").getChannel(); - int size = (int) fc.size(); - MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, size).load(); - byte[] result = new byte[size]; - mbb.get(result, 0, size); - return result; - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "readFile2BytesByMap"); - return null; - } finally { - CloseUtils.closeIO(fc); - } - } - - // = - - /** - * 获取文件 - * @param filePath - * @return - */ - private static File getFileByPath(final String filePath){ - return filePath != null ? new File(filePath) : null; - } - - /** - * 判断文件是否存在,不存在则判断是否创建成功 - * @param filePath 文件路径 - * @return true : 存在或创建成功, false : 不存在或创建失败 - */ - private static boolean createOrExistsFile(final String filePath) { - return createOrExistsFile(getFileByPath(filePath)); - } - - /** - * 判断文件是否存在,不存在则判断是否创建成功 - * @param file 文件 - * @return true : 存在或创建成功, false : 不存在或创建失败 - */ - private static boolean createOrExistsFile(final File file) { - if (file == null) return false; - // 如果存在,是文件则返回 true,是目录则返回 false - if (file.exists()) return file.isFile(); - // 判断文件是否存在, 不存在则直接返回 - if (!createOrExistsDir(file.getParentFile())) return false; - try { - // 存在, 则返回新的路径 - return file.createNewFile(); - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "createOrExistsFile"); - return false; - } - } - - /** - * 判断目录是否存在,不存在则判断是否创建成功 - * @param file 文件 - * @return true : 存在或创建成功, false : 不存在或创建失败 - */ - private static boolean createOrExistsDir(final File file) { - // 如果存在,是目录则返回 true,是文件则返回 false,不存在则返回是否创建成功 - return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); - } - - /** - * 检查是否存在某个文件 - * @param file 文件路径 - * @return 是否存在文件 - */ - private static boolean isFileExists(final File file){ - return file != null && file.exists(); - } - - /** - * 判断字符串是否为 null 或全为空白字符 - * @param str 待校验字符串 - * @return - */ - private static boolean isSpace(final String str) { - if (str == null) return true; - for (int i = 0, len = str.length(); i < len; ++i) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/FileUtils.java b/DevLibUtils/src/main/java/dev/utils/common/FileUtils.java deleted file mode 100644 index 3d8991e5da..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/FileUtils.java +++ /dev/null @@ -1,2020 +0,0 @@ -package dev.utils.common; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.net.HttpURLConnection; -import java.net.URL; -import java.security.DigestInputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; - -import dev.utils.JCLogUtils; - -/** - * detail: 文件操作工具类 - * Created by Ttt - */ -public final class FileUtils { - - private FileUtils() { - } - - // 日志TAG - private static final String TAG = FileUtils.class.getSimpleName(); - /** 换行字符串 */ - public static final String NEW_LINE_STR = System.getProperty("line.separator"); - /** 换行字符串 - 两行 */ - public static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; - - /** - * 判断是否为null - * @param str - * @return - */ - private static boolean isEmpty(final String str) { - return (str == null || str.length() == 0); - } - - /** - * 判断字符串是否为 null 或全为空白字符 - * @param str 待校验字符串 - * @return - */ - private static boolean isSpace(final String str) { - if (str == null) return true; - for (int i = 0, len = str.length(); i < len; ++i) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } - - // = - - /** - * 获取文件 - to getFileByPath - * @param filePath - * @return - */ - public static File getFile(final String filePath){ - return getFileByPath(filePath); - } - - /** - * 获取文件 - * @param filePath 文件路径 - * @param fName 文件名 - * @return - */ - public static File getFile(final String filePath, final String fName){ - return (filePath != null && fName != null) ? new File(filePath, fName) : null; - } - - /** - * 获取文件 - * @param filePath - * @return - */ - public static File getFileByPath(final String filePath){ - return filePath != null ? new File(filePath) : null; - } - - /** - * 获取路径, 并且进行创建目录 - * @param filePath 保存目录 - * @param fName 文件名 - * @return - */ - public static File getFileCreateFolder(final String filePath, final String fName) { - // 防止不存在目录文件,自动创建 - createFolder(filePath); - // 返回处理过后的File - return getFile(filePath, fName); - } - - /** - * 判断某个文件夹是否创建,未创建则创建(纯路径 - 无文件名) - * @param dirPath 文件夹路径 (无文件名字.后缀) - */ - public static boolean createFolder(final String dirPath) { - return createFolder(getFileByPath(dirPath)); - } - - /** - * 判断某个文件夹是否创建,未创建则创建(纯路径 - 无文件名) - * @param file 文件夹路径 (无文件名字.后缀) - */ - public static boolean createFolder(final File file) { - if (file != null) { - try { - // 当这个文件夹不存在的时候则创建文件夹 - if (!file.exists()) { - // 允许创建多级目录 - return file.mkdirs(); - // 这个无法创建多级目录 - // rootFile.mkdir(); - } - return true; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "createFolder"); - } - } - return false; - } - - /** - * 创建文件夹目录 - 可以传入文件名 - * @param filePath 文件路径 + 文件名 - * @return - */ - public static boolean createFolderByPath(final String filePath) { - return createFolderByPath(getFileByPath(filePath)); - } - - /** - * 创建文件夹目录 - 可以传入文件名 - * @param file - * @return - */ - public static boolean createFolderByPath(final File file){ - // 创建文件夹 - 如果失败才创建 - if (file != null) { - if (file.exists()){ - return true; - } else if (!file.getParentFile().mkdirs()) { - return createFolder(file.getParent()); - } - } - return false; - } - - /** - * 创建多个文件夹, 如果不存在则创建 - * @param filePaths - */ - public static void createFolderByPaths(final String... filePaths){ - if (filePaths != null && filePaths.length != 0){ - for (int i = 0, len = filePaths.length; i < len; i++){ - createFolder(filePaths[i]); - } - } - } - - /** - * 创建多个文件夹, 如果不存在则创建 - * @param files - */ - public static void createFolderByPaths(final File... files){ - if (files != null && files.length != 0){ - for (int i = 0, len = files.length; i < len; i++){ - createFolder(files[i]); - } - } - } - - // == - - /** - * 判断目录是否存在,不存在则判断是否创建成功 - * @param dirPath 目录路径 - * @return true : 存在或创建成功, false : 不存在或创建失败 - */ - public static boolean createOrExistsDir(final String dirPath) { - return createOrExistsDir(getFileByPath(dirPath)); - } - - /** - * 判断目录是否存在,不存在则判断是否创建成功 - * @param file 文件 - * @return true : 存在或创建成功, false : 不存在或创建失败 - */ - public static boolean createOrExistsDir(final File file) { - // 如果存在,是目录则返回 true,是文件则返回 false,不存在则返回是否创建成功 - return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); - } - - /** - * 判断文件是否存在,不存在则判断是否创建成功 - * @param filePath 文件路径 - * @return true : 存在或创建成功, false : 不存在或创建失败 - */ - public static boolean createOrExistsFile(final String filePath) { - return createOrExistsFile(getFileByPath(filePath)); - } - - /** - * 判断文件是否存在,不存在则判断是否创建成功 - * @param file 文件 - * @return true : 存在或创建成功, false : 不存在或创建失败 - */ - public static boolean createOrExistsFile(final File file) { - if (file == null) return false; - // 如果存在,是文件则返回 true,是目录则返回 false - if (file.exists()) return file.isFile(); - // 判断文件是否存在, 不存在则直接返回 - if (!createOrExistsDir(file.getParentFile())) return false; - try { - // 存在, 则返回新的路径 - return file.createNewFile(); - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "createOrExistsFile"); - return false; - } - } - - /** - * 判断文件是否存在,存在则在创建之前删除 - * @param filePath - * @return true : 创建成功, false : 创建失败 - */ - public static boolean createFileByDeleteOldFile(final String filePath) { - return createFileByDeleteOldFile(getFileByPath(filePath)); - } - - /** - * 判断文件是否存在,存在则在创建之前删除 - * @param file - * @return true : 创建成功, false : 创建失败 - */ - public static boolean createFileByDeleteOldFile(final File file) { - if (file == null) return false; - // 文件存在并且删除失败返回 false - if (file.exists() && !file.delete()) return false; - // 创建目录失败返回 false - if (!createOrExistsDir(file.getParentFile())) return false; - try { - return file.createNewFile(); - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "createFileByDeleteOldFile"); - return false; - } - } - - /** - * 获取文件路径 - * @param file - * @return - */ - public static String getPath(final File file){ - return file != null ? file.getPath() : null; - } - - /** - * 获取文件绝对路径 - * @param file - * @return - */ - public static String getAbsolutePath(final File file){ - return file != null ? file.getAbsolutePath() : null; - } - - // == - - /** - * 获取文件名 - * @param file - * @return - */ - public static String getName(final File file){ - return file != null ? file.getName() : null; - } - - /** - * 获取文件名 - * @param filePath 文件路径 - * @return - */ - public static String getName(final String filePath){ - return getName(filePath, ""); - } - - /** - * 获取文件名 - * @param filePath 文件路径 - * @param dfStr - * @return - */ - public static String getName(final String filePath, final String dfStr){ - if (!isSpace(filePath)) { - return new File(filePath).getName(); - } - return dfStr; - } - - /** - * 获得文件后缀名(无.,单独后缀) - * @param file - * @return - */ - public static String getFileSuffix(final File file){ - return getFileSuffix(getAbsolutePath(file)); - } - - /** - * 获得文件后缀名(无.,单独后缀) - * @param filePath 文件地址、文件名都行 - * @return - */ - public static String getFileSuffix(final String filePath) { - // 获取最后的索引 - int lastIndexOf = -1; - // 判断是否存在 - if (filePath != null && (lastIndexOf = filePath.lastIndexOf('.')) != -1) { - String result = filePath.substring(lastIndexOf); - if (result.startsWith(".")) { - return result.substring(1); - } - return result; - } - return null; - } - - /** - * 获得文件名(无后缀) - * @param file - * @return - */ - public static String getFileNotSuffix(final File file) { - return getFileNotSuffix(getName(file)); - } - - /** - * 获得文件名(无后缀) - * @param filePath - * @return - */ - public static String getFileNotSuffixToPath(final String filePath) { - return getFileNotSuffix(getName(filePath)); - } - - /** - * 获得文件名(无后缀) - * @param fileName 文件名 - * @return - */ - public static String getFileNotSuffix(final String fileName) { - if (fileName != null){ - if (fileName.lastIndexOf('.') != -1) { - return fileName.substring(0, fileName.lastIndexOf('.')); - } else { - return fileName; - } - } - return null; - } - - /** - * 获取全路径中的不带拓展名的文件名 - * @param file The file. - * @return 不带拓展名的文件名 - */ - public static String getFileNameNoExtension(final File file) { - if (file == null) return null; - return getFileNameNoExtension(file.getPath()); - } - - /** - * 获取全路径中的不带拓展名的文件名 - * @param filePath - * @return 不带拓展名的文件名 - */ - public static String getFileNameNoExtension(final String filePath) { - if (isSpace(filePath)) return filePath; - int lastPoi = filePath.lastIndexOf('.'); - int lastSep = filePath.lastIndexOf(File.separator); - if (lastSep == -1) { - return (lastPoi == -1 ? filePath : filePath.substring(0, lastPoi)); - } - if (lastPoi == -1 || lastSep > lastPoi) { - return filePath.substring(lastSep + 1); - } - return filePath.substring(lastSep + 1, lastPoi); - } - - /** - * 获取全路径中的文件拓展名 - * @param file The file. - * @return 文件拓展名 - */ - public static String getFileExtension(final File file) { - if (file == null) return null; - return getFileExtension(file.getPath()); - } - - /** - * 获取全路径中的文件拓展名 - * @param filePath - * @return 文件拓展名 - */ - public static String getFileExtension(final String filePath) { - if (isSpace(filePath)) return filePath; - int lastPoi = filePath.lastIndexOf('.'); - int lastSep = filePath.lastIndexOf(File.separator); - if (lastPoi == -1 || lastSep >= lastPoi) return ""; - return filePath.substring(lastPoi + 1); - } - - // == - - /** - * 检查是否存在某个文件 - * @param file 文件路径 - * @return 是否存在文件 - */ - public static boolean isFileExists(final File file){ - return file != null && file.exists(); - } - - /** - * 检查是否存在某个文件 - * @param filePath 文件路径 - * @return 是否存在文件 - */ - public static boolean isFileExists(final String filePath) { - return isFileExists(getFileByPath(filePath)); - } - - /** - * 检查是否存在某个文件 - * @param filePath 文件路径 - * @param fName 文件名 - * @return 是否存在文件 - */ - public static boolean isFileExists(final String filePath, final String fName) { - return filePath != null && fName != null && new File(filePath, fName).exists(); - } - - /** - * 判断是否文件 - * @param filePath - * @return - */ - public static boolean isFile(final String filePath){ - return isFile(getFileByPath(filePath)); - } - - /** - * 判断是否文件 - * @param file - * @return - */ - public static boolean isFile(final File file){ - return file != null && file.exists() && file.isFile(); - } - - /** - * 判断是否文件夹 - * @param filePath - * @return - */ - public static boolean isDir(final String filePath){ - return isDir(getFileByPath(filePath)); - } - - /** - * 判断是否文件夹 - * @param file - * @return - */ - public static boolean isDir(final File file){ - return file != null && file.exists() && file.isDirectory(); - } - - /** - * 判断是否隐藏文件 - * @param filePath - * @return - */ - public static boolean isHide(final String filePath){ - return isHide(getFileByPath(filePath)); - } - - /** - * 判断是否隐藏文件 - * @param file - * @return - */ - public static boolean isHide(final File file){ - return file != null && file.exists() && file.isHidden(); - } - - // == - - /** - * 获取文件最后修改的毫秒时间戳 - * @param filePath 文件路径 - * @return 文件最后修改的毫秒时间戳 - */ - - public static long getFileLastModified(final String filePath) { - return getFileLastModified(getFileByPath(filePath)); - } - - /** - * 获取文件最后修改的毫秒时间戳 - * @param file 文件 - * @return 文件最后修改的毫秒时间戳 - */ - public static long getFileLastModified(final File file) { - if (file == null) return -1; - return file.lastModified(); - } - - /** - * 简单获取文件编码格式 - * @param filePath 文件路径 - * @return 文件编码 - */ - public static String getFileCharsetSimple(final String filePath) { - return getFileCharsetSimple(getFileByPath(filePath)); - } - - /** - * 简单获取文件编码格式 - * @param file 文件 - * @return 文件编码 - */ - public static String getFileCharsetSimple(final File file) { - int p = 0; - InputStream is = null; - try { - is = new BufferedInputStream(new FileInputStream(file)); - p = (is.read() << 8) + is.read(); - } catch (IOException e) { - JCLogUtils.eTag(TAG, e, "getFileCharsetSimple"); - } finally { - CloseUtils.closeIO(is); - } - switch (p) { - case 0xefbb: - return "UTF-8"; - case 0xfffe: - return "Unicode"; - case 0xfeff: - return "UTF-16BE"; - default: - return "GBK"; - } - } - - /** - * 获取文件行数 - * @param filePath - * @return 文件行数 - */ - public static int getFileLines(final String filePath) { - return getFileLines(getFileByPath(filePath)); - } - - /** - * 获取文件行数 => 比 readLine 要快很多 - * @param file The file. - * @return 文件行数 - */ - public static int getFileLines(final File file) { - int count = 1; - InputStream is = null; - try { - is = new BufferedInputStream(new FileInputStream(file)); - byte[] buffer = new byte[1024]; - int readChars; - if (NEW_LINE_STR.endsWith("\n")) { - while ((readChars = is.read(buffer, 0, 1024)) != -1) { - for (int i = 0; i < readChars; ++i) { - if (buffer[i] == '\n') ++count; - } - } - } else { - while ((readChars = is.read(buffer, 0, 1024)) != -1) { - for (int i = 0; i < readChars; ++i) { - if (buffer[i] == '\r') ++count; - } - } - } - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "getFileLines"); - } finally { - CloseUtils.closeIO(is); - } - return count; - } - - // == - - /** - * 获取文件大小 - * @param filePath - * @return 文件大小 - */ - public static String getFileSize(final String filePath) { - return getFileSize(getFileByPath(filePath)); - } - - /** - * 获取文件大小 - * @param file The file. - * @return 文件大小 - */ - public static String getFileSize(final File file) { - return formatByteMemorySize(getFileLength(file)); - } - - /** - * 获取目录大小 - * @param dirPath 目录路径 - * @return 文件大小 - */ - public static String getDirSize(final String dirPath) { - return getDirSize(getFileByPath(dirPath)); - } - - /** - * 获取目录大小 - * @param dir 目录 - * @return 文件大小 - */ - public static String getDirSize(final File dir) { - return formatByteMemorySize(getDirLength(dir)); - } - - /** - * 获取文件大小 - * @param filePath - * @return - */ - public static long getFileLength(final String filePath){ - return getFileLength(getFileByPath(filePath)); - } - - /** - * 获取文件大小 - * @param file - * @return - */ - public static long getFileLength(final File file){ - return file != null ? file.length() : 0l; - } - - /** - * 获取目录长度 - * @param dirPath 目录路径 - * @return 目录长度 - */ - public static long getDirLength(final String dirPath) { - return getDirLength(getFileByPath(dirPath)); - } - - /** - * 获取目录长度 - * @param dir 目录 - * @return 目录长度 - */ - public static long getDirLength(final File dir) { - if (!isDir(dir)) return 0; - long len = 0; - File[] files = dir.listFiles(); - if (files != null && files.length != 0) { - for (File file : files) { - if (file.isDirectory()) { - len += getDirLength(file); - } else { - len += file.length(); - } - } - } - return len; - } - - /** - * 获取文件长度 - 网络资源 - * @param httpUri - * @return 文件长度 - */ - public static long getFileLengthNetwork(final String httpUri) { - boolean isURL = httpUri.matches("[a-zA-z]+://[^\\s]*"); - if (isURL) { - try { - HttpURLConnection conn = (HttpURLConnection) new URL(httpUri).openConnection(); - conn.setRequestProperty("Accept-Encoding", "identity"); - conn.connect(); - if (conn.getResponseCode() == 200) { - return conn.getContentLength(); - } - return 0l; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "getFileLengthNetwork"); - } - } - return 0l; - } - - /** - * 获取全路径中的文件名 - * @param file The file. - * @return 文件名 - */ - public static String getFileName(final File file) { - if (file == null) return null; - return getFileName(file.getPath()); - } - - /** - * 获取全路径中的文件名 - * @param filePath - * @return 文件名 - */ - public static String getFileName(final String filePath) { - if (isSpace(filePath)) return filePath; - int lastSep = filePath.lastIndexOf(File.separator); - return lastSep == -1 ? filePath : filePath.substring(lastSep + 1); - } - - /** - * 获取全路径中的最长目录 - * @param file The file. - * @return filePath 最长目录 - */ - public static String getDirName(final File file) { - if (file == null) return null; - return getDirName(file.getPath()); - } - - /** - * 获取全路径中的最长目录 - * @param filePath - * @return filePath 最长目录 - */ - public static String getDirName(final String filePath) { - if (isSpace(filePath)) return filePath; - int lastSep = filePath.lastIndexOf(File.separator); - return lastSep == -1 ? "" : filePath.substring(0, lastSep + 1); - } - - // == - - /** - * 重命名文件 - 同个目录下, 修改文件名 - * @param filePath 文件路径 - * @param newName 新名称 - * @return - */ - public static boolean rename(final String filePath, final String newName) { - return rename(getFileByPath(filePath), newName); - } - - /** - * 重命名文件 - 同个目录下, 修改文件名 - * @param file 文件 - * @param newName 新名称 - * @return - */ - public static boolean rename(final File file, final String newName) { - // 文件为空返回 false - if (file == null) return false; - // 文件不存在返回 false - if (!file.exists()) return false; - // 如果文件名没有改变返回 true - if (newName.equals(file.getName())) return true; - // 拼接新的文件路径 - File newFile = new File(file.getParent() + File.separator + newName); - // 如果重命名的文件已存在返回 false - return !newFile.exists() && file.renameTo(newFile); - } - - // == 文件大小处理 == - - /** - * 传入文件路径, 返回对应的文件大小 - * @param filePath - * @return - */ - public static String formatFileSize(final String filePath){ - File file = getFileByPath(filePath); - return formatFileSize(file != null ? file.length() : 0); - } - - /** - * 传入文件路径, 返回对应的文件大小 - * @param file - * @return - */ - public static String formatFileSize(final File file){ - return formatFileSize(file != null ? file.length() : 0); - } - - /** - * 传入对应的文件大小double,返回转换后文件大小 - * @param fileS 返回String文件大小 - * @return - */ - public static String formatFileSize(final double fileS) { - // 转换文件大小 - DecimalFormat df = new DecimalFormat("#.00"); - String fileSizeString = ""; - if (fileS <= 0) { - fileSizeString = "0B"; - } else if (fileS < 1024) { - fileSizeString = df.format(fileS) + "B"; - } else if (fileS < 1048576) { - fileSizeString = df.format(fileS / 1024) + "KB"; - } else if (fileS < 1073741824) { - fileSizeString = df.format(fileS / 1048576) + "MB"; - } else if (fileS < 1099511627776d){ - fileSizeString = df.format(fileS / 1073741824) + "GB"; - } else { - fileSizeString = df.format(fileS / 1099511627776d) + "TB"; - } - return fileSizeString; - } - - /** - * 字节数转合适内存大小 保留 3 位小数 (%.位数f) - * @param byteNum 字节数 - * @return 合适内存大小 - */ - public static String formatByteMemorySize(final double byteNum) { - if (byteNum < 0) { - return "0B"; - } else if (byteNum < 1024) { - return String.format("%.3fB", (double) byteNum); - } else if (byteNum < 1048576) { - return String.format("%.3fKB", (double) byteNum / 1024); - } else if (byteNum < 1073741824) { - return String.format("%.3fMB", (double) byteNum / 1048576); - } else if (byteNum < 1099511627776d){ - return String.format("%.3fGB", (double) byteNum / 1073741824); - } else { - return String.format("%.3fTB", (double) byteNum / 1099511627776d); - } - } - - /** - * 字节数转合适内存大小 保留 number 位小数 (%.位数f) - * @param number 字节数 - * @param byteNum 字节数 - * @return 合适内存大小 - */ - public static String formatByteMemorySize(final int number, final double byteNum) { - if (byteNum < 0) { - return "0B"; - } else if (byteNum < 1024) { - return String.format("%." + number + "fB", (double) byteNum); - } else if (byteNum < 1048576) { - return String.format("%." + number + "fKB", (double) byteNum / 1024); - } else if (byteNum < 1073741824) { - return String.format("%." + number + "fMB", (double) byteNum / 1048576); - } else if (byteNum < 1099511627776d){ - return String.format("%." + number + "fGB", (double) byteNum / 1073741824); - } else { - return String.format("%." + number + "fTB", (double) byteNum / 1099511627776d); - } - } - - // == 获取文件MD5值 == - - /** - * 获取文件的 MD5 校验码 - * @param filePath 文件路径 - * @return 文件的 MD5 校验码 - */ - public static String getFileMD5ToString(final String filePath) { - return getFileMD5ToString(getFileByPath(filePath)); - } - - /** - * 获取文件的 MD5 校验码 - * @param file 文件 - * @return 文件的 MD5 校验码 - */ - public static String getFileMD5ToString(final File file) { - try { - return toHexString(getFileMD5(file), HEX_DIGITS); - } catch (Exception e){ - } - return null; - } - - /** - * 获取文件的 MD5 校验码 - * @param filePath 文件路径 - * @return 文件的 MD5 校验码 - */ - public static byte[] getFileMD5(final String filePath) { - return getFileMD5(getFileByPath(filePath)); - } - - /** - * 获取文件的 MD5 校验码 - * @param file 文件 - * @return 文件的 MD5 校验码 - */ - public static byte[] getFileMD5(final File file) { - if (file == null) return null; - DigestInputStream dis = null; - try { - FileInputStream fis = new FileInputStream(file); - MessageDigest md = MessageDigest.getInstance("MD5"); - dis = new DigestInputStream(fis, md); - byte[] buffer = new byte[1024 * 256]; - while (true) { - if (!(dis.read(buffer) > 0)) break; - } - md = dis.getMessageDigest(); - return md.digest(); - } catch (NoSuchAlgorithmException | IOException e) { - JCLogUtils.eTag(TAG, e, "getFileMD5"); - } finally { - CloseUtils.closeIO(dis); - } - return null; - } - - // == - - /** - * 获取文件MD5值 - 小写 - * @param filePath 文件地址 - * @return - */ - public static String getFileMD5ToString2(final String filePath) { - return getFileMD5ToString2(getFileByPath(filePath)); - } - - /** - * 获取文件MD5值 - 小写 - * @param file 文件地址 - * @return - */ - public static String getFileMD5ToString2(final File file) { - try { - InputStream fis = new FileInputStream(file); - byte[] buffer = new byte[1024]; - MessageDigest md5 = MessageDigest.getInstance("MD5"); - int numRead = 0; - while ((numRead = fis.read(buffer)) > 0) { - md5.update(buffer, 0, numRead); - } - fis.close(); - return toHexString(md5.digest(), HEX_DIGITS); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "getFileMD5ToString2"); - } - return null; - } - - // 小写 - private static final char HEX_DIGITS[]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; -// // 大写 -// private static final char HEX_DIGITS_UPPER[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - - /** - * 进行转换 - * @param bData - * @param hexDigits - * @return - */ - private static String toHexString(final byte[] bData, final char[] hexDigits) { - if (bData == null || hexDigits == null){ - return null; - } - StringBuilder sBuilder = new StringBuilder(bData.length * 2); - for (int i = 0, len = bData.length; i < len; i++) { - sBuilder.append(hexDigits[(bData[i] & 0xf0) >>> 4]); - sBuilder.append(hexDigits[bData[i] & 0x0f]); - } - return sBuilder.toString(); - } - - // ============== - // == 文件操作 == - // ============== - - /** - * 删除文件 - * @param filePath - * @return - */ - public static boolean deleteFile(final String filePath) { - return deleteFile(getFileByPath(filePath)); - } - - /** - * 删除文件 - * @param file - * @return - */ - public static boolean deleteFile(final File file) { - // 文件存在,并且不是目录文件,则直接删除 - if (file != null && file.exists() && !file.isDirectory()) { - return file.delete(); - } - return false; - } - - /** - * 删除多个文件 - * @param filePaths - * @return - */ - public static void deleteFiles(final String... filePaths) { - if (filePaths != null && filePaths.length != 0){ - for (int i = 0, len = filePaths.length; i < len; i++){ - deleteFile(filePaths[i]); - } - } - } - - /** - * 删除多个文件 - * @param files - * @return - */ - public static void deleteFiles(final File... files) { - if (files != null && files.length != 0){ - for (int i = 0, len = files.length; i < len; i++){ - deleteFile(files[i]); - } - } - } - - // = - - /** - * 删除文件夹 - * @param filePath - * @return - */ - public static boolean deleteFolder(final String filePath) { - return deleteFolder(getFileByPath(filePath)); - } - - /** - * 删除文件夹 - * @param file - * @return - */ - public static boolean deleteFolder(final File file) { - if (file != null) { - try { - // 文件存在,并且不是目录文件,则直接删除 - if (file.exists()) { - if (file.isDirectory()) { // 属于文件目录 - File[] files = file.listFiles(); - if (null == files || files.length == 0) { - file.delete(); - } - for (File f : files) { - deleteFolder(f.getPath()); - } - return file.delete(); - } else { // 属于文件 - return deleteFile(file); - } - } - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "deleteFolder"); - } - } - return false; - } - - /** - * 保存文件 - * @param file - * @param bytes 保存内容 - * @return 是否保存成功 - */ - public static boolean saveFile(final File file, final byte[] bytes) { - if (bytes != null && file != null) { - try { - // 防止文件没创建 - createFolder(getDirName(file)); - // 保存内容到一个文件 - FileOutputStream fos = new FileOutputStream(file); - BufferedOutputStream bos = new BufferedOutputStream(fos); - bos.write(bytes); - bos.close(); - fos.close(); - return true; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "saveFile"); - } - } - return false; - } - - /** - * 保存文件 - * @param filePath 保存路径 - * @param fName 文件名.后缀 - * @param bytes 保存内容 - * @return 是否保存成功 - */ - public static boolean saveFile(final String filePath, final String fName, final byte[] bytes) { - if (bytes != null && filePath != null && fName != null) { - try { - // 防止文件没创建 - createFolder(filePath); - // 保存路径 - File sFile = new File(filePath, fName); - // 保存内容到一个文件 - FileOutputStream fos = new FileOutputStream(sFile); - BufferedOutputStream bos = new BufferedOutputStream(fos); - bos.write(bytes); - bos.close(); - fos.close(); - return true; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "saveFile"); - } - } - return false; - } - - /** - * 保存文件 - * @param filePath 保存路径 - * @param fName 文件名.后缀 - * @param content 保存内容 - * @return 是否保存成功 - */ - public static boolean saveFile(final String filePath, final String fName, final String content) { - if (content != null && filePath != null && fName != null){ - try { - // 防止文件没创建 - createFolder(filePath); - // 保存路径 - File sFile = new File(filePath, fName); - // 保存内容到一个文件 - FileOutputStream fos = new FileOutputStream(sFile); - fos.write(content.getBytes()); - fos.close(); - return true; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "saveFile"); - } - } - return false; - } - - /** - * 保存文件 - * @param filePath 保存路径 - * @param fName 文件名.后缀 - * @param content 保存内容 - * @param coding 编码 - * @return 是否保存成功 - */ - public static boolean saveFile(final String filePath, final String fName, final String content, final String coding) { - if (content != null && filePath != null && fName != null){ - try { - // 防止文件没创建 - createFolder(filePath); - // 保存路径 - File sFile = new File(filePath, fName); - // 保存内容到一个文件 - FileOutputStream fos = new FileOutputStream(sFile); - Writer out; - if (coding != null) { - out = new OutputStreamWriter(fos, coding); - } else { - out = new OutputStreamWriter(fos); - } - out.write(content); - out.close(); - fos.close(); - return true; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "saveFile"); - } - } - return false; - } - - /** - * 追加文件:使用FileWriter - * @param filePath 文件地址 - * @param content 追加内容 - */ - public static void appendFile(final String filePath, final String content) { - if(filePath == null || content == null) { - return; - } - File file = new File(filePath); - if(!file.exists()) { // 如果文件不存在,则跳过 - return; - } - FileWriter writer = null; - try { - // 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件 - writer = new FileWriter(file, true); - writer.write(content); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "appendFile"); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException e) { - } - } - } - } - - // == - - /** - * 读取文件 - * @param filePath - * @return - */ - public static byte[] readFileBytes(final String filePath) { - return readFileBytes(getFileByPath(filePath)); - } - - /** - * 读取文件 - * @param file - * @return - */ - public static byte[] readFileBytes(final File file) { - if (file != null && file.exists()){ - try { - FileInputStream fin = new FileInputStream(file); - int length = fin.available(); - byte[] buffer = new byte[length]; - fin.read(buffer); - fin.close(); - return buffer; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "readFileBytes"); - } - } - return null; - } - - /** - * 读取文件 - * @return - */ - public static byte[] readFileBytes(final InputStream iStream) { - if (iStream != null){ - try { - int length = iStream.available(); - byte[] buffer = new byte[length]; - iStream.read(buffer); - iStream.close(); - return buffer; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "readFileBytes"); - } - } - return null; - } - - /** - * 读取文件 - * @param filePath - * @return - */ - public static String readFile(final String filePath) { - return readFile(getFileByPath(filePath)); - } - - /** - * 读取文件 - * @param file - * @return - */ - public static String readFile(final File file) { - if (file != null && file.exists()){ - try { - return readFile(new FileInputStream(file)); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "readFile"); - } - } - return null; - } - - /** - * 读取文件 - * @param iStream -> new FileInputStream(path) - * @return - */ - public static String readFile(final InputStream iStream) { - if (iStream != null){ - try { - InputStreamReader isR = new InputStreamReader(iStream); - BufferedReader br = new BufferedReader(isR); - StringBuilder sBuilder = new StringBuilder(); - String line; - while ((line = br.readLine()) != null) { - sBuilder.append(line); - } - isR.close(); - br.close(); - return sBuilder.toString(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "readFile"); - } - } - return null; - } - - /** - * 读取文件 - * @param iStream -> new FileInputStream(path) - * @param encode 编码 - * @return - */ - public static String readFile(final InputStream iStream, final String encode) { - if (iStream != null){ - try { - InputStreamReader isR = null; - if (encode != null){ - new InputStreamReader(iStream, encode); - } else { - new InputStreamReader(iStream); - } - BufferedReader br = new BufferedReader(isR); - StringBuilder sBuilder = new StringBuilder(); - String line; - while ((line = br.readLine()) != null) { - sBuilder.append(line); - } - isR.close(); - br.close(); - return sBuilder.toString(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "readFile"); - } - } - return null; - } - - // == - - /** - * 复制单个文件 - * @param inputStream 文件流(被复制) - * @param destFilePath 目标文件地址 - * @param overlay 如果目标文件存在,是否覆盖 - * @return 如果复制成功返回true,否则返回false - */ - public static boolean copyFile(final InputStream inputStream, final String destFilePath, final boolean overlay){ - if (inputStream == null || destFilePath == null){ - return false; - } - // 判断目标文件是否存在 - File destFile = new File(destFilePath); - // 如果属于文件夹则跳过 - if (destFile.isDirectory()){ - return false; - } - if (destFile.exists()) { - // 如果目标文件存在并允许覆盖 - if (overlay) { - // 删除已经存在的目标文件,无论目标文件是目录还是单个文件 - destFile.delete(); - } - } else { - // 如果目标文件所在目录不存在,则创建目录 - if (!destFile.getParentFile().exists()) { - // 目标文件所在目录不存在 - if (!destFile.getParentFile().mkdirs()) { - // 复制文件失败:创建目标文件所在目录失败 - return false; - } - } - } - // 复制文件 - int byteread = 0; // 读取的字节数 - InputStream in = inputStream; - OutputStream out = null; - try { - out = new FileOutputStream(destFile); - byte[] buffer = new byte[1024]; - while ((byteread = in.read(buffer)) != -1) { - out.write(buffer, 0, byteread); - } - return true; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "copyFile"); - return false; - } finally { - try { - if (out != null) - out.close(); - if (in != null) - in.close(); - } catch (IOException e) { - } - } - } - - /** - * 复制单个文件 - * @param srcFilePath 待复制的文件地址 - * @param destFilePath 目标文件地址 - * @param overlay 如果目标文件存在,是否覆盖 - * @return 如果复制成功返回true,否则返回false - */ - public static boolean copyFile(final String srcFilePath, final String destFilePath, final boolean overlay) { - if (srcFilePath == null || destFilePath == null){ - return false; - } - File srcFile = new File(srcFilePath); - // 判断源文件是否存在 - if (!srcFile.exists()) { - return false; - } else if (!srcFile.isFile()) { // srcFile.isDirectory(); - return false; - } - // 判断目标文件是否存在 - File destFile = new File(destFilePath); - // 如果属于文件夹则跳过 - if (destFile.isDirectory()){ - return false; - } - if (destFile.exists()) { - // 如果目标文件存在并允许覆盖 - if (overlay) { - // 删除已经存在的目标文件,无论目标文件是目录还是单个文件 - new File(destFilePath).delete(); - } - } else { - // 如果目标文件所在目录不存在,则创建目录 - if (!destFile.getParentFile().exists()) { - // 目标文件所在目录不存在 - if (!destFile.getParentFile().mkdirs()) { - // 复制文件失败:创建目标文件所在目录失败 - return false; - } - } - } - // 复制文件 - int byteread = 0; // 读取的字节数 - InputStream in = null; - OutputStream out = null; - try { - in = new FileInputStream(srcFile); - out = new FileOutputStream(destFile); - byte[] buffer = new byte[1024]; - while ((byteread = in.read(buffer)) != -1) { - out.write(buffer, 0, byteread); - } - return true; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "copyFile"); - return false; - } finally { - try { - if (out != null) - out.close(); - if (in != null) - in.close(); - } catch (IOException e) { - } - } - } - - /** - * 复制文件夹 - * @param srcFolderPath 待复制的文件夹地址 - * @param destFolderPath 目标文件夹地址 - * @param overlay 如果目标文件存在,是否覆盖 - * @return 如果复制成功返回true,否则返回false - */ - public static boolean copyFolder(final String srcFolderPath, final String destFolderPath, final boolean overlay) { - return copyFolder(srcFolderPath, destFolderPath, srcFolderPath, overlay); - } - - /** - * 复制文件夹 - * @param srcFolderPath 待复制的文件夹地址 - * @param destFolderPath 目标文件夹地址 - * @param sourcePath 源文件地址 - * @param overlay 如果目标文件存在,是否覆盖 - * @return 如果复制成功返回true,否则返回false - */ - private static boolean copyFolder(final String srcFolderPath, final String destFolderPath, final String sourcePath, boolean overlay) { - if (srcFolderPath == null || destFolderPath == null) { - return false; - } - File srcFile = new File(srcFolderPath); - // 判断源文件是否存在 - if (!srcFile.exists()) { - return false; - } else if (!srcFile.isDirectory()) { // 不属于文件夹则跳过 - return false; - } - // 判断目标文件是否存在 - File destFile = new File(destFolderPath); - // 如果文件夹没创建,则创建 - if (!destFile.exists()) { - // 允许创建多级目录 - destFile.mkdirs(); - } - // 判断是否属于文件夹 - if (!destFile.isDirectory()) { // 不属于文件夹则跳过 - return false; - } - // 判断是否存在 - if (srcFile.exists()) { - // 获取文件路径 - File[] files = srcFile.listFiles(); - // 防止不存在文件 - if (files != null && files.length != 0) { - // 进行遍历 - for (File file : files) { - // 文件存在,才进行处理 - if (file.exists()) { - // 属于文件夹 - if (file.isDirectory()) { - copyFolder(file.getAbsolutePath(), destFolderPath, sourcePath, overlay); - } else { // 属于文件 - // 复制的文件地址 - String filePath = file.getAbsolutePath(); - // 获取源文件地址 - 并且进行判断 - String dealSource = new File(sourcePath).getAbsolutePath(); - // 属于最前才进行处理 - if (filePath.indexOf(dealSource) == 0){ - // 获取处理后的地址 - dealSource = filePath.substring(dealSource.length(), filePath.length()); - // 获得需要复制保存的地址 - String savePath = new File(destFolderPath, dealSource).getAbsolutePath(); - // 进行复制文件 - boolean isResult = copyFile(filePath, savePath, overlay); - } - } - } - } - } - } - return true; - } - - // -- - - /** - * 移动(剪切)文件 - * @param srcFilePath - * @param destFilePath - * @param overlay - * @return - */ - public static boolean moveFile(final String srcFilePath, final String destFilePath, final boolean overlay){ - // 复制文件 - if (copyFile(srcFilePath, destFilePath, overlay)){ - // 删除文件 - return deleteFile(srcFilePath); - } - return false; - } - - /** - * 移动(剪切)文件夹 - * @param srcFilePath - * @param destFilePath - * @param overlay - * @return - */ - public static boolean moveFolder(final String srcFilePath, final String destFilePath, final boolean overlay){ - // 复制文件夹 - if (copyFolder(srcFilePath, destFilePath, overlay)){ - // 删除文件夹 - return deleteFolder(srcFilePath); - } - return false; - } - - // =============== - // == FileUtils == - // =============== - - /** - * 复制或移动目录 - * @param srcDirPath 源目录路径 - * @param destDirPath 目标目录路径 - * @param listener 是否覆盖监听器 - * @param isMove 是否移动 - * @return true : 复制或移动成功, false :复制或移动失败 - */ - private static boolean copyOrMoveDir(final String srcDirPath, final String destDirPath, final OnReplaceListener listener, final boolean isMove) { - return copyOrMoveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener, isMove); - } - - /** - * 复制或移动目录 - * @param srcDir 源目录 - * @param destDir 目标目录 - * @param listener 是否覆盖监听器 - * @param isMove 是否移动 - * @return true : 复制或移动成功, false :复制或移动失败 - */ - private static boolean copyOrMoveDir(final File srcDir, final File destDir, final OnReplaceListener listener, final boolean isMove) { - if (srcDir == null || destDir == null) return false; - // 如果目标目录在源目录中则返回 false,看不懂的话好好想想递归怎么结束 - // srcPath : F:\\MyGithub\\AndroidUtilCode\\utilcode\\src\\test\\res - // destPath: F:\\MyGithub\\AndroidUtilCode\\utilcode\\src\\test\\res1 - // 为防止以上这种情况出现出现误判,须分别在后面加个路径分隔符 - String srcPath = srcDir.getPath() + File.separator; - String destPath = destDir.getPath() + File.separator; - if (destPath.contains(srcPath)) return false; - // 源文件不存在或者不是目录则返回 false - if (!srcDir.exists() || !srcDir.isDirectory()) return false; - if (destDir.exists()) { - if (listener.onReplace()) { // 需要覆盖则删除旧目录 - if (!deleteAllInDir(destDir)) { // 删除文件失败的话返回 false - return false; - } - } else { // 不需要覆盖直接返回即可 true - return true; - } - } - // 目标目录不存在返回 false - if (!createOrExistsDir(destDir)) return false; - File[] files = srcDir.listFiles(); - for (File file : files) { - File oneDestFile = new File(destPath + file.getName()); - if (file.isFile()) { - // 如果操作失败返回 false - if (!copyOrMoveFile(file, oneDestFile, listener, isMove)) return false; - } else if (file.isDirectory()) { - // 如果操作失败返回 false - if (!copyOrMoveDir(file, oneDestFile, listener, isMove)) return false; - } - } - return !isMove || deleteDir(srcDir); - } - - /** - * 复制或移动文件 - * @param srcFilePath 源文件路径 - * @param destFilePath 目标文件路径 - * @param listener 是否覆盖监听器 - * @param isMove 是否移动 - * @return true : 复制或移动成功, false :复制或移动失败 - */ - private static boolean copyOrMoveFile(final String srcFilePath, final String destFilePath, final OnReplaceListener listener, final boolean isMove) { - return copyOrMoveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener, isMove); - } - - /** - * 复制或移动文件 - * @param srcFile 源文件 - * @param destFile 目标文件 - * @param listener 是否覆盖监听器 - * @param isMove 是否移动 - * @return true : 复制或移动成功, false :复制或移动失败 - */ - private static boolean copyOrMoveFile(final File srcFile, final File destFile, final OnReplaceListener listener, final boolean isMove) { - if (srcFile == null || destFile == null) return false; - // 如果源文件和目标文件相同则返回 false - if (srcFile.equals(destFile)) return false; - // 源文件不存在或者不是文件则返回 false - if (!srcFile.exists() || !srcFile.isFile()) return false; - if (destFile.exists()) { // 目标文件存在 - if (listener.onReplace()) { // 需要覆盖则删除旧文件 - if (!destFile.delete()) { // 删除文件失败的话返回 false - return false; - } - } else { // 不需要覆盖直接返回即可 true - return true; - } - } - // 目标目录不存在返回 false - if (!createOrExistsDir(destFile.getParentFile())) return false; - try { - return FileIOUtils.writeFileFromIS(destFile, new FileInputStream(srcFile), false) && !(isMove && !deleteFile(srcFile)); - } catch (FileNotFoundException e) { - JCLogUtils.eTag(TAG, e, "copyOrMoveFile"); - return false; - } - } - - /** - * 复制目录 - * @param srcDirPath 源目录路径 - * @param destDirPath 目标目录路径 - * @param listener 是否覆盖监听器 - * @return true : 复制成功, false :复制失败 - */ - public static boolean copyDir(final String srcDirPath, final String destDirPath, final OnReplaceListener listener) { - return copyDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener); - } - - /** - * 复制目录 - * @param srcDir 源目录 - * @param destDir 目标目录 - * @param listener 是否覆盖监听器 - * @return true : 复制成功, false :复制失败 - */ - public static boolean copyDir(final File srcDir, final File destDir, final OnReplaceListener listener) { - return copyOrMoveDir(srcDir, destDir, listener, false); - } - - /** - * 复制文件 - * @param srcFilePath 源文件路径 - * @param destFilePath 目标文件路径 - * @param listener 是否覆盖监听器 - * @return true : 复制成功, false :复制失败 - */ - public static boolean copyFile(final String srcFilePath, final String destFilePath, final OnReplaceListener listener) { - return copyFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener); - } - - /** - * 复制文件 - * @param srcFile 源文件 - * @param destFile 目标文件 - * @param listener 是否覆盖监听器 - * @return true : 复制成功, false :复制失败 - */ - public static boolean copyFile(final File srcFile, final File destFile, final OnReplaceListener listener) { - return copyOrMoveFile(srcFile, destFile, listener, false); - } - - /** - * 移动目录 - * @param srcDirPath 源目录路径 - * @param destDirPath 目标目录路径 - * @param listener 是否覆盖监听器 - * @return true : 移动成功, false :移动失败 - */ - public static boolean moveDir(final String srcDirPath, final String destDirPath, final OnReplaceListener listener) { - return moveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener); - } - - /** - * 移动目录 - * @param srcDir 源目录 - * @param destDir 目标目录 - * @param listener 是否覆盖监听器 - * @return true : 移动成功, false :移动失败 - */ - public static boolean moveDir(final File srcDir, final File destDir, final OnReplaceListener listener) { - return copyOrMoveDir(srcDir, destDir, listener, true); - } - - /** - * 移动文件 - * @param srcFilePath 源文件路径 - * @param destFilePath 目标文件路径 - * @param listener 是否覆盖监听器 - * @return true : 移动成功, false :移动失败 - */ - public static boolean moveFile(final String srcFilePath, final String destFilePath, final OnReplaceListener listener) { - return moveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener); - } - - /** - * 移动文件 - * @param srcFile 源文件 - * @param destFile 目标文件 - * @param listener 是否覆盖监听器 - * @return true : 移动成功, false :移动失败 - */ - public static boolean moveFile(final File srcFile, final File destFile, final OnReplaceListener listener) { - return copyOrMoveFile(srcFile, destFile, listener, true); - } - - /** - * 删除目录 - * @param dirPath 目录路径 - * @return true : 删除成功, false :删除失败 - */ - public static boolean deleteDir(final String dirPath) { - return deleteDir(getFileByPath(dirPath)); - } - - /** - * 删除目录 - * @param dir 目录 - * @return true : 删除成功, false :删除失败 - */ - public static boolean deleteDir(final File dir) { - if (dir == null) return false; - // dir doesn't exist then return true - if (!dir.exists()) return true; - // dir isn't a directory then return false - if (!dir.isDirectory()) return false; - File[] files = dir.listFiles(); - if (files != null && files.length != 0) { - for (File file : files) { - if (file.isFile()) { - if (!file.delete()) return false; - } else if (file.isDirectory()) { - if (!deleteDir(file)) return false; - } - } - } - return dir.delete(); - } - - /** - * 删除目录下所有东西 - * @param dirPath 目录路径 - * @return true : 删除成功, false :删除失败 - */ - public static boolean deleteAllInDir(final String dirPath) { - return deleteAllInDir(getFileByPath(dirPath)); - } - - /** - * 删除目录下所有东西 - * @param dir 目录 - * @return true : 删除成功, false :删除失败 - */ - public static boolean deleteAllInDir(final File dir) { - return deleteFilesInDirWithFilter(dir, new FileFilter() { - @Override - public boolean accept(File pathname) { - return true; - } - }); - } - - /** - * 删除目录下所有文件 - * @param dirPath 目录路径 - * @return true : 删除成功, false :删除失败 - */ - public static boolean deleteFilesInDir(final String dirPath) { - return deleteFilesInDir(getFileByPath(dirPath)); - } - - /** - * 删除目录下所有文件 - * @param dir 目录 - * @return true : 删除成功, false :删除失败 - */ - public static boolean deleteFilesInDir(final File dir) { - return deleteFilesInDirWithFilter(dir, new FileFilter() { - @Override - public boolean accept(File pathname) { - return pathname.isFile(); - } - }); - } - - /** - * 删除目录下所有过滤的文件 - * @param dirPath 目录路径 - * @param filter 过滤器 - * @return true : 删除成功, false :删除失败 - */ - public static boolean deleteFilesInDirWithFilter(final String dirPath, final FileFilter filter) { - return deleteFilesInDirWithFilter(getFileByPath(dirPath), filter); - } - - /** - * 删除目录下所有过滤的文件 - * @param dir 目录 - * @param filter 过滤器 - * @return true : 删除成功, false :删除失败 - */ - public static boolean deleteFilesInDirWithFilter(final File dir, final FileFilter filter) { - if (dir == null) return false; - // dir doesn't exist then return true - if (!dir.exists()) return true; - // dir isn't a directory then return false - if (!dir.isDirectory()) return false; - File[] files = dir.listFiles(); - if (files != null && files.length != 0) { - for (File file : files) { - if (filter.accept(file)) { - if (file.isFile()) { - if (!file.delete()) return false; - } else if (file.isDirectory()) { - if (!deleteDir(file)) return false; - } - } - } - } - return true; - } - - /** - * 获取目录下所有文件 - 不递归进子目录 - * @param dirPath 目录路径 - * @return 文件链表 - */ - public static List listFilesInDir(final String dirPath) { - return listFilesInDir(dirPath, false); - } - - /** - * 获取目录下所有文件 - 不递归进子目录 - * @param dir 目录 - * @return 文件链表 - */ - public static List listFilesInDir(final File dir) { - return listFilesInDir(dir, false); - } - - /** - * 获取目录下所有文件 - * @param dirPath 目录路径 - * @param isRecursive 是否递归进子目录 - * @return 文件链表 - */ - public static List listFilesInDir(final String dirPath, final boolean isRecursive) { - return listFilesInDir(getFileByPath(dirPath), isRecursive); - } - - /** - * 获取目录下所有文件 - * @param dir 目录 - * @param isRecursive 是否递归进子目录 - * @return 文件链表 - */ - public static List listFilesInDir(final File dir, final boolean isRecursive) { - return listFilesInDirWithFilter(dir, new FileFilter() { - @Override - public boolean accept(File pathname) { - return true; - } - }, isRecursive); - } - - /** - * 获取目录下所有过滤的文件 - 不递归进子目录 - * @param dirPath 目录路径 - * @param filter 过滤器 - * @return 文件链表 - */ - public static List listFilesInDirWithFilter(final String dirPath, final FileFilter filter) { - return listFilesInDirWithFilter(getFileByPath(dirPath), filter, false); - } - - /** - * 获取目录下所有过滤的文件 - 不递归进子目录 - * @param dir 目录 - * @param filter 过滤器 - * @return 文件链表 - */ - public static List listFilesInDirWithFilter(final File dir, final FileFilter filter) { - return listFilesInDirWithFilter(dir, filter, false); - } - - /** - * 获取目录下所有过滤的文件 - * @param dirPath 目录路径 - * @param filter 过滤器 - * @param isRecursive 是否递归进子目录 - * @return 文件链表 - */ - public static List listFilesInDirWithFilter(final String dirPath, final FileFilter filter, final boolean isRecursive) { - return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive); - } - - /** - * 获取目录下所有过滤的文件 - * @param dir 目录 - * @param filter 过滤器 - * @param isRecursive 是否递归进子目录 - * @return 文件链表 - */ - public static List listFilesInDirWithFilter(final File dir, final FileFilter filter, final boolean isRecursive) { - if (!isDir(dir)) return null; - List list = new ArrayList<>(); - File[] files = dir.listFiles(); - if (files != null && files.length != 0) { - for (File file : files) { - if (filter.accept(file)) { - list.add(file); - } - if (isRecursive && file.isDirectory()) { - // noinspection ConstantConditions - list.addAll(listFilesInDirWithFilter(file, filter, true)); - } - } - } - return list; - } - - public interface OnReplaceListener { - boolean onReplace(); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/HexUtils.java b/DevLibUtils/src/main/java/dev/utils/common/HexUtils.java deleted file mode 100644 index ecd95c966a..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/HexUtils.java +++ /dev/null @@ -1,150 +0,0 @@ -package dev.utils.common; - -import dev.utils.JCLogUtils; - -/** - * detail: 十六进制处理 - * Created by Ttt - */ -public final class HexUtils { - - private HexUtils(){ - } - - // 日志TAG - private static final String TAG = HexUtils.class.getSimpleName(); - - /** 用于建立十六进制字符的输出的小写字符数组 */ - private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', - '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - - /** 用于建立十六进制字符的输出的大写字符数组 */ - private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', - '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - - /** - * 将字节数组转换为十六进制字符数组 - * @param data byte[] - * @return 十六进制char[] - */ - public static char[] encodeHex(byte[] data) { - return encodeHex(data, true); - } - - /** - * 将字节数组转换为十六进制字符数组 - * @param data byte[] - * @param toLowerCase true : 传换成小写格式 , false : 传换成大写格式 - * @return 十六进制char[] - */ - public static char[] encodeHex(byte[] data, boolean toLowerCase) { - return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); - } - - /** - * 将字节数组转换为十六进制字符数组 - * @param data byte[] - * @param toDigits 用于控制输出的char[] - * @return 十六进制char[] - */ - protected static char[] encodeHex(byte[] data, char[] toDigits) { - if (data == null || toDigits == null) return null; - try { - int len = data.length; - char[] out = new char[len << 1]; - // two characters form the hex value. - for (int i = 0, j = 0; i < len; i++) { - out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; - out[j++] = toDigits[0x0F & data[i]]; - } - return out; - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "encodeHex"); - } - return null; - } - - /** - * 将字节数组转换为十六进制字符串 - * @param data byte[] - * @return 十六进制String - */ - public static String encodeHexStr(byte[] data) { - return encodeHexStr(data, true); - } - - /** - * 将字节数组转换为十六进制字符串 - * @param data byte[] - * @param toLowerCase true : 传换成小写格式 , false : 传换成大写格式 - * @return 十六进制String - */ - public static String encodeHexStr(byte[] data, boolean toLowerCase) { - return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); - } - - /** - * 将字节数组转换为十六进制字符串 - * @param data byte[] - * @param toDigits 用于控制输出的char[] - * @return 十六进制String - */ - protected static String encodeHexStr(byte[] data, char[] toDigits) { - try { - return new String(encodeHex(data, toDigits)); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "encodeHexStr"); - } - return null; - } - - /** - * 将十六进制字符数组转换为字节数组 - * @param data 十六进制char[] - * @return byte[] - * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常 - */ - public static byte[] decodeHex(char[] data) { - if (data == null) return null; - try { - int len = data.length; - byte[] out = new byte[len >> 1]; - // two characters form the hex value. - for (int i = 0, j = 0; j < len; i++) { - int f = toDigit(data[j], j) << 4; - j++; - f = f | toDigit(data[j], j); - j++; - out[i] = (byte) (f & 0xFF); - } - return out; - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "decodeHex"); - } - return null; - } - - /** - * 将十六进制字符转换成一个整数 - * @param ch 十六进制char - * @param index 十六进制字符在字符数组中的位置 - * @return 一个整数 - * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常 - */ - protected static int toDigit(char ch, int index) { - int digit = Character.digit(ch, 16); - if (digit == -1) { - throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index); - } - return digit; - } - -// public static void main(String[] args) { -// String srcStr = "待转换字符串"; -// String encodeStr = encodeHexStr(srcStr.getBytes()); -// String decodeStr = new String(decodeHex(encodeStr.toCharArray())); -// System.out.println("转换前:" + srcStr); -// System.out.println("转换后:" + encodeStr); -// System.out.println("还原后:" + decodeStr); -// } -} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/HttpParamsUtils.java b/DevLibUtils/src/main/java/dev/utils/common/HttpParamsUtils.java deleted file mode 100644 index 4e5077863e..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/HttpParamsUtils.java +++ /dev/null @@ -1,260 +0,0 @@ -package dev.utils.common; - -import java.net.URLEncoder; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import dev.utils.JCLogUtils; -import dev.utils.LogPrintUtils; - -/** - * detail: Http 参数工具类 - * Created by Ttt - */ -public final class HttpParamsUtils { - - private HttpParamsUtils() { - } - - // 日志TAG - private static final String TAG = HttpParamsUtils.class.getSimpleName(); - - /** - * 拆分参数 - * @param params - * @return - */ - public static HashMap splitParams(final String params) { - return splitParams(params, false); - } - - /** - * 拆分参数 - * @param params - * @param urlEncode 是否需要编码 - * @return - */ - public static HashMap splitParams(final String params, boolean urlEncode) { - HashMap mapParams = new HashMap<>(); - if (params != null) { - // 拆分数据 - String[] keyValues = params.split("&"); - // 数据长度 - int valLength = 0; - // 进行循环遍历 - for (String val : keyValues){ - // 数据不为null - if (val != null && (valLength = val.length()) != 0){ - // 获取首位 = 索引 - int indexOf = val.indexOf('='); - // 不存在则不处理 - if (indexOf != -1){ - // 获取key - String key = val.substring(0, indexOf); - // 获取value - String value = null; - // 防止资源浪费 - if (indexOf + 1 == valLength){ - value = ""; - } else { - value = val.substring(indexOf + 1, valLength); - } - // 判断是否编码 - if (urlEncode){ - mapParams.put(key, urlEncode(value)); - } else { - mapParams.put(key, value); - } - } - } - } - } - return mapParams; - } - - // - - - /** - * 拼接请求参数 - value => String - * @param mapParams - * @return - */ - public static String joinReqParams(final HashMap mapParams) { - return joinReqParams(mapParams, false); - } - - /** - * 拼接请求参数 - value => String - * @param mapParams - * @param urlEncode 是否需要编码 - * @return - */ - public static String joinReqParams(final HashMap mapParams, boolean urlEncode) { - if (mapParams != null) { - int index = 0; - // -- - StringBuilder sBuilder = new StringBuilder(); - Iterator> iterator = mapParams.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - // -- - if (index > 0) sBuilder.append('&'); - sBuilder.append(entry.getKey()); - sBuilder.append('='); - sBuilder.append(urlEncode ? urlEncode(entry.getValue()) : entry.getValue()); - index++; - } - return sBuilder.toString(); - } - return null; - } - - // - - - /** - * 拼接请求参数 - value => Object - * @param mapParams - * @return - */ - public static String joinReqParamsObj(final HashMap mapParams) { - return joinReqParamsObj(mapParams, false); - } - - /** - * 拼接请求参数 - value => Object - * @param mapParams - * @param urlEncode 是否需要编码 - * @return - */ - public static String joinReqParamsObj(final HashMap mapParams, boolean urlEncode) { - if (mapParams != null) { - int index = 0; - // -- - StringBuilder sBuilder = new StringBuilder(); - Iterator> iterator = mapParams.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - // -- - if (index > 0) sBuilder.append('&'); - sBuilder.append(entry.getKey()); - sBuilder.append('='); - if (urlEncode){ - if (entry.getValue() instanceof String){ - sBuilder.append(urlEncode((String) entry.getValue())); - } - } else { - sBuilder.append(entry.getValue()); - } - index++; - } - return sBuilder.toString(); - } - return null; - } - - // - - - /** - * toString 快捷方法, 拼接打印 String - * @param mapParams - * @return - */ - public static String toStringMap(final HashMap mapParams) { - return toStringMap(mapParams, false); - } - - /** - * toString 快捷方法, 拼接打印 String - * @param mapParams - * @param urlEncode 是否需要编码 - * @return - */ - public static String toStringMap(final HashMap mapParams, boolean urlEncode) { - if (mapParams != null) { - StringBuilder sBuilder = new StringBuilder(); - // -- - Iterator> iterator = mapParams.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - sBuilder.append(entry.getKey()); - sBuilder.append(" => "); - sBuilder.append(urlEncode ? urlEncode(entry.getValue()) : entry.getValue()); - sBuilder.append("\n"); - } - return sBuilder.toString(); - } - return null; - } - - // == 拼接成, 模拟 JavaScript 传递对象数组格式 == - - // 正常数据 - // objStr[key] => sex=男&name=Ttt - // 方法会进行 url编码 - // objStr[key] => sex%3D%E7%94%B7%26name%3DTtt - - /** - * 进行转换对象处理(请求发送对象) - * @param mapParams - * @param objStr - * @param key - * @param value - */ - public static void toConvertObjToMS(HashMap mapParams, String objStr, String key, String value){ - if (mapParams != null) { - String data = null; - try { - data = URLEncoder.encode(value, "UTF-8"); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toConvertObjToMS"); - } - mapParams.put(objStr + "[" + key + "]", data); - } - } - - /** - * 进行转换对象处理(请求发送对象) - * @param mapParams - * @param objStr - * @param key - * @param value - */ - public static void toConvertObjToMO(HashMap mapParams, String objStr, String key, Object value){ - if (mapParams != null) { - Object data = null; - try { - data = URLEncoder.encode(value.toString(), "UTF-8"); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toConvertObjToMO"); - } - mapParams.put(objStr + "[" + key + "]", data); - } - } - - // = - - /** - * url编码 - utf-8 - * @param input The input. - * @return the urlencoded string - */ - public static String urlEncode(final String input) { - return urlEncode(input, "UTF-8"); - } - - /** - * url编码 - * @param input The input. - * @param charsetName The name of charset. - * @return the urlencoded string - */ - public static String urlEncode(final String input, final String charsetName) { - try { - return URLEncoder.encode(input, charsetName); - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "urlEncode"); - return input; - } - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/ObjectUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ObjectUtils.java deleted file mode 100644 index 10da8a01a8..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/ObjectUtils.java +++ /dev/null @@ -1,115 +0,0 @@ -package dev.utils.common; - -import java.lang.reflect.Array; -import java.util.Collection; -import java.util.Map; - -import dev.utils.JCLogUtils; - -/** - * detail: 对象相关工具类 - * Created by Ttt - */ -public final class ObjectUtils { - - private ObjectUtils() { - } - - // 日志TAG - private static final String TAG = ObjectUtils.class.getSimpleName(); - - /** - * 判断对象是否为空 - * @param obj 对象 - * @return true : 为空, false : 不为空 - */ - public static boolean isEmpty(final Object obj) { - if (obj == null) { - return true; - } - if (obj instanceof CharSequence && obj.toString().length() == 0) { - return true; - } - if (obj.getClass().isArray() && Array.getLength(obj) == 0) { - return true; - } - if (obj instanceof Collection && ((Collection) obj).isEmpty()) { - return true; - } - if (obj instanceof Map && ((Map) obj).isEmpty()) { - return true; - } - return false; - } - - /** - * 判断对象是否非空 - * @param obj 对象 - * @return true : 非空, false : 空 - */ - public static boolean isNotEmpty(final Object obj) { - return !isEmpty(obj); - } - - /** - * 判断对象是否相等 - * @param o1 对象1 - * @param o2 对象2 - * @return true : 相等, false : 不相等 - */ - public static boolean equals(Object o1, Object o2) { - return o1 == o2 || (o1 != null && o1.equals(o2)); - } - - /** - * 检查对象非空 - * @param object 对象 - * @param message 报错 - * @param 范型 - * @return 非空对象 - */ - public static T requireNonNull(T object, String message) { - if (object == null) { - throw new NullPointerException(message); - } - return object; - } - - /** - * 获取非空或默认对象 - * @param object 对象 - * @param defaultObject 默认值 - * @param 范型 - * @return 非空或默认对象 - */ - public static T getOrDefault(T object, T defaultObject) { - if (object == null) { - return defaultObject; - } - return object; - } - - /** - * 获取对象哈希值 - * @param o 对象 - * @return 哈希值 - */ - public static int hashCode(Object o) { - return o != null ? o.hashCode() : 0; - } - - /** - * 获取转换对象 - * @param obj 对象 - * @param 范型 - * @return 非空或默认对象 - */ - public static T converObj(Object obj) { - try { - return (T) obj; - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "converObj"); - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/QuickCommonUtils.java b/DevLibUtils/src/main/java/dev/utils/common/QuickCommonUtils.java deleted file mode 100644 index c44bcfeea5..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/QuickCommonUtils.java +++ /dev/null @@ -1,45 +0,0 @@ -package dev.utils.common; - -/** - * detail: 快捷通用 - * Created by Ttt - */ -public final class QuickCommonUtils { - - private QuickCommonUtils(){ - } - - /** - * 转换手机号 - * @param phone - */ - public static String converHideMobile(String phone){ - return converHideMobile(phone, "*"); - } - - /** - * 转换手机号 - * @param phone - * @param symbol 符号 - */ - public static String converHideMobile(String phone, String symbol){ - return DevCommonUtils.converSymbolHide(3, phone, symbol); - } - - /** - * 耗时时间记录 - * @param buffer - * @param title 标题 - * @param sTime 开始时间 - * @param eTime 结束时间 - */ - public static void timeRecord(StringBuffer buffer, String title, long sTime, long eTime) { - // 使用时间 - long uTime = eTime - sTime; - // 计算时间 - buffer.append("\n" + title); - buffer.append("\n开始时间:" + sTime); - buffer.append("\n结束时间:" + eTime); - buffer.append("\n所用时间:" + uTime); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/RandomUtils.java b/DevLibUtils/src/main/java/dev/utils/common/RandomUtils.java deleted file mode 100644 index 8dec43e263..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/RandomUtils.java +++ /dev/null @@ -1,274 +0,0 @@ -package dev.utils.common; - -import java.util.Random; - -/** - * detail: 随机生成工具类 - * Created by Ttt - */ -public final class RandomUtils { - - private RandomUtils() { - } - - // 0123456789 - public static final char[] NUMBERS = new char[] { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 }; - - // abcdefghijklmnopqrstuvwxyz - public static final char[] LOWER_CASE_LETTERS = new char[] { 97, 98, 99, - 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - 113, 114, 115, 116, 117, 118, 119, 120, 121, 122 }; - - // ABCDEFGHIJKLMNOPQRSTUVWXYZ - public static final char[] CAPITAL_LETTERS = new char[] { 65, 66, 67, 68, - 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, - 86, 87, 88, 89, 90 }; - - // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ - public static final char[] LETTERS = new char[] { 97, 98, 99, 100, 101, - 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, - 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, - 89, 90 }; - - // 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ - public static final char[] NUMBERS_AND_LETTERS = new char[] { 48, 49, 50, - 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, - 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90 }; - - // = - - public static boolean nextBoolean(Random random) { - return random.nextBoolean(); - } - - public static byte[] nextBytes(Random random, byte[] buf) { - try { - random.nextBytes(buf); - } catch (Exception e) { - } - return buf; - } - - public static double nextDouble(Random random) { - return random.nextDouble(); - } - - public synchronized double nextGaussian(Random random) { - return random.nextGaussian(); - } - - public static float nextFloat(Random random) { - return random.nextFloat(); - } - - public static int nextInt(Random random) { - return random.nextInt(); - } - - public static int nextInt(Random random, int n) { - return random.nextInt(n); - } - - public static long nextLong(Random random) { - return random.nextLong(); - } - - // == ----------------------------------------- == - - public static boolean nextBoolean() { - return new Random().nextBoolean(); - } - - public static byte[] nextBytes(byte[] buf) { - return nextBytes(new Random(), buf); - } - - public static double nextDouble() { - return Math.random(); - } - - public synchronized double nextGaussian() { - return new Random().nextGaussian(); - } - - public static float nextFloat() { - return new Random().nextFloat(); - } - - public static int nextInt() { - return new Random().nextInt(); - } - - public static int nextInt(int n) { - return new Random().nextInt(n); - } - - public static long nextLong() { - return new Random().nextLong(); - } - - // == ----------------------------------------- == - - /** - * 获取数字自定义长度的随机数 - * @param length 长度 - * @return 随机数字符串 - */ - public static String getRandomNumbers(int length) { - return getRandom(NUMBERS, length); - } - - /** - * 获取小写字母自定义长度的随机数 - * @param length 长度 - * @return 随机字符串 - */ - public static String getRandomLowerCaseLetters(int length) { - return getRandom(LOWER_CASE_LETTERS, length); - } - - /** - * 获取大写字母自定义长度的随机数 - * @param length 长度 - * @return 随机字符串 - */ - public static String getRandomCapitalLetters(int length) { - return getRandom(CAPITAL_LETTERS, length); - } - - /** - * 获取大小写字母自定义长度的随机数 - * @param length 长度 - * @return 随机字符串 - */ - public static String getRandomLetters(int length) { - return getRandom(LETTERS, length); - } - - /** - * 获取数字、大小写字母自定义长度的随机数 - * @param length 长度 - * @return 随机字符串 - */ - public static String getRandomNumbersAndLetters(int length) { - return getRandom(NUMBERS_AND_LETTERS, length); - } - - /** - * 获取自定义数据自定义长度的随机数 - * @param length 长度 - * @return 随机字符串 - */ - public static String getRandom(String source, int length) { - return getRandom(source.toCharArray(), length); - } - - /** - * 获取char[]内的随机数 - * @param chars 随机的数据源 - * @param length 需要最终长度 - * @return - */ - public static String getRandom(char[] chars, int length) { - if(length > 0 && chars != null && chars.length != 0) { - StringBuilder str = new StringBuilder(length); - Random random = new Random(); - for (int i = 0; i < length; i++) { - str.append(chars[random.nextInt(chars.length)]); - } - return str.toString(); - } - return null; - } - - /** - * 获取 0 - 最大随机数之间的随机数 - * @param max 最大随机数 - */ - public static int getRandom(int max) { - return getRandom(0, max); - } - - /** - * 获取两个数之间的随机数(不含最大随机数,需要 + 1) - * @param min 最小随机数 - * @param max 最大随机数 - */ - public static int getRandom(int min, int max) { - if (min > max) { - return 0; - } else if (min == max) { - return min; - } - return min + new Random().nextInt(max - min); - } - - /** - * 洗牌算法,随机置换指定的数组使用的默认源的随机性 - * @param objArray - * @return - */ - public static boolean shuffle(Object[] objArray) { - if (objArray == null) { - return false; - } - return shuffle(objArray, getRandom(1,objArray.length)); - } - - /** - * 洗牌算法,随机置换指定的数组 - * @param objArray - * @param shuffleCount - * @return - */ - public static boolean shuffle(Object[] objArray, int shuffleCount) { - int length; - if(shuffleCount > 0 && objArray != null && (length = objArray.length) >= shuffleCount) { - for (int i = 1; i <= shuffleCount; i++) { - int random = getRandom(0,length - i); - Object temp = objArray[length - i]; - objArray[length - i] = objArray[random]; - objArray[random] = temp; - } - return true; - } - return false; - } - - /** - * 洗牌算法,随机置换指定数组的使用随机的默认源 - * @param intArray - * @return - */ - public static int[] shuffle(int[] intArray) { - if (intArray == null) { - return null; - } - return shuffle(intArray, getRandom(1,intArray.length)); - } - - /** - * 洗牌算法,随机置换指定的数组 - * @param intArray - * @param shuffleCount - * @return - */ - public static int[] shuffle(int[] intArray, int shuffleCount) { - int length; - if(shuffleCount > 0 && intArray != null && (length = intArray.length) >= shuffleCount) { - int[] out = new int[shuffleCount]; - for (int i = 1; i <= shuffleCount; i++) { - int random = getRandom(0,length - i); - out[i - 1] = intArray[random]; - int temp = intArray[length - i]; - intArray[length - i] = intArray[random]; - intArray[random] = temp; - } - return out; - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/Reflect2Utils.java b/DevLibUtils/src/main/java/dev/utils/common/Reflect2Utils.java deleted file mode 100644 index 4f4553f2e0..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/Reflect2Utils.java +++ /dev/null @@ -1,249 +0,0 @@ -package dev.utils.common; - -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -import dev.utils.JCLogUtils; - -/** - * detail: 反射相关工具类 - * Created by Ttt - */ -public final class Reflect2Utils { - - private Reflect2Utils(){ - } - - // 日志TAG - private static final String TAG = Reflect2Utils.class.getSimpleName(); - - /** - * 获取某个对象的公共属性 - * @param owner, fieldName - * @return 该属性对象 - */ - public static Object getProperty(Object owner, String fieldName) { - try { - Class ownerClass = owner.getClass(); - Field field = ownerClass.getField(fieldName); - Object property = field.get(owner); - return property; - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "getProperty"); - } - return null; - } - - /** - * 获取某类的静态公共属性 - * @param className 类名 - * @param fieldName 属性名 - * @return 该属性对象 - */ - public static Object getStaticProperty(String className, String fieldName) { - try { - Class ownerClass = Class.forName(className); - Field field = ownerClass.getField(fieldName); - Object property = field.get(ownerClass); - return property; - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "getStaticProperty"); - } - return null; - } - - - /** - * 执行某对象方法 - * @param owner 对象 - * @param methodName 方法名 - * @param args 参数 - * @return 方法返回值 - */ - public static Object invokeMethod(Object owner, String methodName, Object[] args) { - try { - Class ownerClass = owner.getClass(); - Class[] argsClass = new Class[args.length]; - for (int i = 0, j = args.length; i < j; i++) { - argsClass[i] = args[i].getClass(); - } - Method method = ownerClass.getMethod(methodName, argsClass); - return method.invoke(owner, args); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "invokeMethod"); - } - return null; - } - - - /** - * 执行某类的静态方法 - * @param className 类名 - * @param methodName 方法名 - * @param args 参数数组 - * @return 执行方法返回的结果 - */ - public static Object invokeStaticMethod(String className, String methodName, Object[] args) { - try { - Class ownerClass = Class.forName(className); - Class[] argsClass = new Class[args.length]; - for (int i = 0, j = args.length; i < j; i++) { - argsClass[i] = args[i].getClass(); - } - Method method = ownerClass.getMethod(methodName, argsClass); - return method.invoke(null, args); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "invokeStaticMethod"); - } - return null; - } - - - /** - * 新建实例 - * @param className 类名 - * @param args 构造函数的参数 如果无构造参数,args 填写为 null - * @return 新建的实例 - */ - public static Object newInstance(String className, Object[] args, Class[] argsType) { - try { - Class newoneClass = Class.forName(className); - if (args == null) { - return newoneClass.newInstance(); - } else { - // Class[] argsClass = new Class[args.length]; - // for (int i = 0, j = args.length; i < j; i++) { - // argsClass[i] = args[i].getClass(); - // } - Constructor cons = newoneClass.getConstructor(argsType); - return cons.newInstance(args); - } - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "newInstance"); - } - return null; - } - - /** - * 是不是某个类的实例 - * @param obj 实例 - * @param cls 类 - * @return 如果 obj 是此类的实例,则返回 true - */ - public static boolean isInstance(Object obj, Class cls) { - return cls.isInstance(obj); - } - - /** - * 获取数组中的某个元素 - * @param array 数组 - * @param index 索引 - * @return 返回指定数组对象中索引组件的值 - */ - public static Object getByArray(Object array, int index) { - return Array.get(array, index); - } - - public static Class GetClassListByPackage(String pPackage) { - Package _Package = Package.getPackage(pPackage); - Class _List = _Package.getClass(); - return _List; - } - - // = - - /** - * 通过反射获取全部字段 - * 如: (ListView) getDeclaredField(对象, "私有属性") - * @param obj - * @param name - * @return - */ - public static Object getDeclaredField(Object obj, String name) throws Exception { - Field f = obj.getClass().getDeclaredField(name); - f.setAccessible(true); - Object out = f.get(obj); - return out; - } - - /** - * 获取父类中的变量对象 - * @param object : 子类对象 - * @param fieldName : 父类中的属性名 - * @return 父类中的变量对象 - */ - public static Object getDeclaredFieldParentObj(Object object, String fieldName){ - try { - Field field = getDeclaredFieldParent(object, fieldName); - field.setAccessible(true); - return field.get(object); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "getDeclaredFieldParentObj"); - } - return null; - } - - /** - * 循环向上转型, 获取对象的 DeclaredField - * @param object : 子类对象 - * @param fieldName : 父类中的属性名 - * @return 父类中的变量对象 - */ - public static Field getDeclaredFieldParent(Object object, String fieldName){ - Field field = null ; - Class clazz = object.getClass() ; - for(; clazz != Object.class ; clazz = clazz.getSuperclass()) { - try { - field = clazz.getDeclaredField(fieldName) ; - return field ; - } catch (Exception e) { - //这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。 - //如果这里的异常打印或者往外抛,则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了 - } - } - return null; - } - - /** - * 设置反射的方法 - * @param object - * @param name 方法名 - * @param args 方法需要的参数 - */ - public static boolean setFieldMethod(Object object, final String name, final Object... args){ - try { - Method method = object.getClass().getDeclaredMethod(name); - method.setAccessible(true); - // 如果不为null, 则不放参数 - if (args != null && args.length != 0){ - method.invoke(object, args); - } else { - method.invoke(object); - } - return true; - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "setFieldMethod"); - } - return false; - } - - /** - * 设置反射的字段 - * @param obj - * @param name 字段名 - * @param value 字段值 - */ - public static boolean setFieldValue(Object obj, String name, Object value) { - try { - Field field = obj.getClass().getDeclaredField(name); - field.setAccessible(true); - field.set(obj, value); - return true; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "setFieldValue"); - } - return false; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/ScaleUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ScaleUtils.java deleted file mode 100644 index 1c81a5781f..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/ScaleUtils.java +++ /dev/null @@ -1,138 +0,0 @@ -package dev.utils.common; - -import dev.utils.JCLogUtils; - -/** - * detail: 计算比例方法 - * Created by Ttt - */ -public final class ScaleUtils { - - private ScaleUtils() { - } - - // 日志TAG - private static final String TAG = ScaleUtils.class.getSimpleName(); - - /** - * 计算缩放比例 - 根据宽度比例转换高度 - * @param width 需要的最终宽度 - * @param cWidth 当前宽度 - * @param cHeight 当前高度 - * @return [0] = 宽度, [1] = 高度 - */ - public static int[] calcScaleToWidth(int width, int cWidth, int cHeight){ - try { - if (cWidth == 0){ - return new int[] { 0, 0 }; - } - // 计算比例 - float scale = ((float) width) / ((float) cWidth); - // 计算缩放后的高度 - int sHeight = (int) (scale * (float) cHeight); - // 返回对应的数据 - return new int[] { width, sHeight }; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "calcScaleToWidth"); - } - return null; - } - - /** - * 计算缩放比例 - 根据高度比例转换宽度 - * @param height 需要的最终高度 - * @param cWidth 当前宽度 - * @param cHeight 当前高度 - * @return [0] = 宽度, [1] = 高度 - */ - public static int[] calcScaleToHeight(int height, int cWidth, int cHeight){ - try { - if (cHeight == 0){ - return new int[] { 0, 0 }; - } - // 计算比例 - float scale = ((float) height) / ((float) cHeight); - // 计算缩放后的宽度 - int sWidth = (int) (scale * (float) cWidth); - // 返回对应的数据 - return new int[] { sWidth, height }; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "calcScaleToHeight"); - } - return null; - } - - /** - * 通过宽度,高度,根据对应的比例 -> 转换成对应的比例宽度高度 - 智能转换 - * @param width - * @param height - * @param wScale - * @param hScale - * @return - */ - public static int[] calcWidthHeightToScale(int width, int height, float wScale, float hScale){ - try { - // 如果宽度的比例,大于等于高度比例 - if (wScale >= hScale){ // 以宽度为基准 - // 设置宽度 -> 以宽度为基准 - int sWidth = width; - // 计算宽度 - int sHeight = (int) (((float) sWidth) * (hScale / wScale)); - // 返回对应的比例 - return new int[] { sWidth , sHeight }; - } else { // 以高度为基准 - // 设置高度 - int sHeight = height; - // 同步缩放比例 - int sWidth = (int) (((float) sHeight) * (wScale / hScale)); - // 返回对应的比例 - return new int[] { sWidth , sHeight }; - } - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "calcWidthHeightToScale"); - } - return null; - } - - /** - * 以宽度为基准 -> 转换对应比例的高度 - * @param width - * @param wScale - * @param hScale - * @return - */ - public static int[] calcWidthToScale(int width, float wScale, float hScale){ - try { - // 设置宽度 - int sWidth = width; - // 计算高度 - int sHeight = (int) (((float) sWidth) * (hScale / wScale)); - // 返回对应的比例 - return new int[] { sWidth , sHeight }; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "calcWidthToScale"); - } - return null; - } - - /** - * 以高度为基准 -> 转换对应比例的宽度 - * @param height - * @param wScale - * @param hScale - * @return - */ - public static int[] calcHeightToScale(int height, float wScale, float hScale){ - try { - // 设置高度 - int sHeight = height; - // 计算宽度 - int sWidth = (int) (((float) sHeight) * (wScale / hScale)); - // 返回对应的比例 - return new int[] { sWidth , sHeight }; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "calcHeightToScale"); - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/SingletonUtils.java b/DevLibUtils/src/main/java/dev/utils/common/SingletonUtils.java deleted file mode 100644 index 9f1b25852a..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/SingletonUtils.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.utils.common; - -/** - * detail: 单例工具类 - * Created by Ttt - */ -public abstract class SingletonUtils { - - private T instance; - - protected abstract T newInstance(); - - public final T getInstance() { - if (instance == null) { - synchronized (SingletonUtils.class) { - if (instance == null) { - instance = newInstance(); - } - } - } - return instance; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/StreamUtils.java b/DevLibUtils/src/main/java/dev/utils/common/StreamUtils.java deleted file mode 100644 index 10c3eb2564..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/StreamUtils.java +++ /dev/null @@ -1,185 +0,0 @@ -package dev.utils.common; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.OutputStream; - -import dev.utils.JCLogUtils; - -/** - * detail: 流操作工具类 - * Created by Ttt - */ -public final class StreamUtils { - - private StreamUtils(){ - } - - // 日志TAG - private static final String TAG = StreamUtils.class.getSimpleName(); - - /** - * Input stream to output stream. - * @param is The input stream. - * @return output stream - */ - public static ByteArrayOutputStream input2OutputStream(final InputStream is) { - if (is == null) return null; - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - byte[] b = new byte[1024]; - int len; - while ((len = is.read(b, 0, 1024)) != -1) { - os.write(b, 0, len); - } - return os; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "input2OutputStream"); - return null; - } finally { - CloseUtils.closeIO(is); - } - } - - /** - * Output stream to input stream. - * @param out The output stream. - * @return input stream - */ - public ByteArrayInputStream output2InputStream(final OutputStream out) { - if (out == null) return null; - return new ByteArrayInputStream(((ByteArrayOutputStream) out).toByteArray()); - } - - /** - * Input stream to bytes. - * @param is The input stream. - * @return bytes - */ - public static byte[] inputStream2Bytes(final InputStream is) { - if (is == null) return null; - return input2OutputStream(is).toByteArray(); - } - - /** - * Bytes to input stream. - * @param bytes The bytes. - * @return input stream - */ - public static InputStream bytes2InputStream(final byte[] bytes) { - if (bytes == null || bytes.length <= 0) return null; - return new ByteArrayInputStream(bytes); - } - - /** - * Output stream to bytes. - * @param out The output stream. - * @return bytes - */ - public static byte[] outputStream2Bytes(final OutputStream out) { - if (out == null) return null; - return ((ByteArrayOutputStream) out).toByteArray(); - } - - /** - * Bytes to output stream. - * @param bytes The bytes. - * @return output stream - */ - public static OutputStream bytes2OutputStream(final byte[] bytes) { - if (bytes == null || bytes.length <= 0) return null; - ByteArrayOutputStream os = null; - try { - os = new ByteArrayOutputStream(); - os.write(bytes); - return os; - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "bytes2OutputStream"); - return null; - } finally { - CloseUtils.closeIO(os); - } - } - - /** - * Input stream to string. - * @param is The input stream. - * @param charsetName The name of charset. - * @return string - */ - public static String inputStream2String(final InputStream is, final String charsetName) { - if (is == null || isSpace(charsetName)) return null; - try { - return new String(inputStream2Bytes(is), charsetName); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "inputStream2String"); - return null; - } - } - - /** - * String to input stream. - * @param string The string. - * @param charsetName The name of charset. - * @return input stream - */ - public static InputStream string2InputStream(final String string, final String charsetName) { - if (string == null || isSpace(charsetName)) return null; - try { - return new ByteArrayInputStream(string.getBytes(charsetName)); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "string2InputStream"); - return null; - } - } - - /** - * Output stream to string. - * @param out The output stream. - * @param charsetName The name of charset. - * @return string - */ - public static String outputStream2String(final OutputStream out, final String charsetName) { - if (out == null || isSpace(charsetName)) return null; - try { - return new String(outputStream2Bytes(out), charsetName); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "outputStream2String"); - return null; - } - } - - /** - * String to output stream. - * @param string The string. - * @param charsetName The name of charset. - * @return output stream - */ - public static OutputStream string2OutputStream(final String string, final String charsetName) { - if (string == null || isSpace(charsetName)) return null; - try { - return bytes2OutputStream(string.getBytes(charsetName)); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "string2OutputStream"); - return null; - } - } - - // = - - /** - * 判断字符串是否为 null 或全为空白字符 - * @param str 待校验字符串 - * @return - */ - private static boolean isSpace(final String str) { - if (str == null) return true; - for (int i = 0, len = str.length(); i < len; ++i) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/StringUtils.java b/DevLibUtils/src/main/java/dev/utils/common/StringUtils.java deleted file mode 100644 index f4c8d14112..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/StringUtils.java +++ /dev/null @@ -1,471 +0,0 @@ -package dev.utils.common; - -import java.net.URLEncoder; -import java.util.Locale; - -import dev.utils.JCLogUtils; - -/** - * detail: 字符串工具类 - * Created by Ttt - */ -public final class StringUtils { - - private StringUtils() { - } - - // 日志TAG - private static final String TAG = StringUtils.class.getSimpleName(); - /** 换行字符串 */ - public static final String NEW_LINE_STR = System.getProperty("line.separator"); - /** 换行字符串 - 两行 */ - public static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; - - /** - * 获取长度,如果字符串为null,则返回0 - * @param str - * @return - */ - public static int length(String str) { - return str == null ? 0 : str.length(); - } - - /** - * 判断是否为null - * @param str - * @return - */ - public static boolean isEmpty(String str) { - return (str == null || str.length() == 0); - } - - /** - * 判断字符串是否为 null 或全为空白字符 - * @param str 待校验字符串 - * @return - */ - private static boolean isSpace(final String str) { - if (str == null) return true; - for (int i = 0, len = str.length(); i < len; ++i) { - if (!Character.isWhitespace(str.charAt(i))) { - return false; - } - } - return true; - } - - /** - * 清空全部空格,并返回处理后的字符串 - * @param str - * @return - */ - public static String toClearSpace(String str) { - if (isEmpty(str)) { - return str; - } - return str.replaceAll(" ", ""); - } - - /** - * 清空前后空格,并返回处理后的字符串 - * @param str - * @return - */ - public static String toClearSpaceTrim(String str) { - if (isEmpty(str)) return str; - // 如果前面或者后面都是 空格开头,就一直进行处理 - while(str.startsWith(" ") || str.endsWith(" ")) { - str = str.trim(); - } - return str; - } - - // == - - /** - * 字符串进行 GBK 编码 - * @param str - * @return - */ - public static String toGBKEncode(String str){ - return toStrEncode(str, "GBK"); - } - - /** - * 字符串进行 GBK2312 编码 - * @param str - * @return - */ - public static String toGBK2312Encode(String str){ - return toStrEncode(str, "GBK-2312"); - } - - /** - * 字符串进行 UTF-8 编码 - * @param str - * @return - */ - public static String toUTF8Encode(String str){ - return toStrEncode(str, "UTF-8"); - } - - /** - * 进行字符串编码 - * @param str - * @param enc - * @return - */ - public static String toStrEncode(String str, String enc){ - try { - return new String(str.getBytes(), enc); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toStrEncode"); - } - return str; - } - - // == - - /** - * 进行 URL 编码,默认UTF-8 - * @param str - * @return - */ - public static String toUrlEncode(String str){ - return toUrlEncode(str, "UTF-8"); - } - - /** - * 进行 URL 编码 - * @param str - * @param enc - * @return - */ - public static String toUrlEncode(String str, String enc){ - try { - return URLEncoder.encode(str, enc); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "toUrlEncode"); - // 如果URL编码失败,则直接进行字符串编码 - return toStrEncode(str, enc); - } - } - - // = - - /** - * 将字符串转移为ASCII码 - * @param str 字符串 - * @return 字符串ASCII码 - */ - public static String toASCII(String str) { - if (isEmpty(str)) return str; - StringBuffer strBuf = new StringBuffer(); - byte[] bytes = str.getBytes(); - for (int i = 0, len = bytes.length; i < len; i++) { - strBuf.append(Integer.toHexString(bytes[i] & 0xff)); - } - return strBuf.toString(); - } - - /** - * 将字符串转移为Unicode码 - * @param str 字符串 - * @return - */ - public static String toUnicode(String str) { - if (isEmpty(str)) return str; - StringBuffer strBuf = new StringBuffer(); - char[] chars = str.toCharArray(); - for (int i = 0, len = chars.length; i < len; i++) { - strBuf.append("\\u").append(Integer.toHexString(chars[i])); - } - return strBuf.toString(); - } - - /** - * 将字符串转移为Unicode码 - * @param chars 字符数组 - * @return - */ - public static String toUnicodeString(char[] chars) { - if (chars == null) return null; - StringBuffer strBuf = new StringBuffer(); - for (int i = 0, len = chars.length; i < len; i++) { - strBuf.append("\\u").append(Integer.toHexString(chars[i])); - } - return strBuf.toString(); - } - - /** - * 转化为半角字符 - * @param str 待转字符串 - * @return 半角字符串 - */ - public static String toDBC(final String str) { - if (isEmpty(str)) return str; - char[] chars = str.toCharArray(); - for (int i = 0, len = chars.length; i < len; i++) { - if (chars[i] == 12288) { - chars[i] = ' '; - } else if (65281 <= chars[i] && chars[i] <= 65374) { - chars[i] = (char) (chars[i] - 65248); - } else { - chars[i] = chars[i]; - } - } - return new String(chars); - } - - /** - * 转化为全角字符 如: a => a A => A - * @param str 待转字符串 - * @return 全角字符串 - */ - public static String toSBC(final String str) { - if (isEmpty(str)) return str; - char[] chars = str.toCharArray(); - for (int i = 0, len = chars.length; i < len; i++) { - if (chars[i] == ' ') { - chars[i] = (char) 12288; - } else if (33 <= chars[i] && chars[i] <= 126) { - chars[i] = (char) (chars[i] + 65248); - } else { - chars[i] = chars[i]; - } - } - return new String(chars); - } - - // == - - /** - * byte[]数组转换为16进制的字符串 - * @param data 要转换的字节数组 - * @return 转换后的结果 - */ - public static final String byteArrayToHexString(byte[] data) { - if (data == null) return null; - StringBuilder sBuilder = new StringBuilder(data.length * 2); - for (byte b : data) { - int v = b & 0xff; - if (v < 16) { - sBuilder.append('0'); - } - sBuilder.append(Integer.toHexString(v)); - } - return sBuilder.toString().toUpperCase(Locale.getDefault()); - } - - /** - * 进行转换 - * @param bData - * @param hexDigits - * @return - */ - public static String toHexString(byte[] bData, char[] hexDigits) { - if (bData == null || hexDigits == null) return null; - StringBuilder sBuilder = new StringBuilder(bData.length * 2); - for (int i = 0; i < bData.length; i++) { - sBuilder.append(hexDigits[(bData[i] & 0xf0) >>> 4]); - sBuilder.append(hexDigits[bData[i] & 0x0f]); - } - return sBuilder.toString(); - } - - /** - * 16进制表示的字符串转换为字节数组 - * @param str 16进制表示的字符串 - * @return byte[] 字节数组 - */ - public static byte[] hexStringToByteArray(String str) { - if (isEmpty(str)) return null; - int len = str.length(); - byte[] d = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - // 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个进制字节 - d[i / 2] = (byte) ((Character.digit(str.charAt(i), 16) << 4) + Character.digit(str.charAt(i + 1), 16)); - } - return d; - } - - /** - * 检测String是否全是中文 - * @param str - * @return - */ - public static boolean checkCheseToString(String str) { - if (isEmpty(str)) return false; - boolean result = true; - char[] chars = str.toCharArray(); - for (int i = 0, len = chars.length; i < len; i++) { - if (!isChinese(chars[i])) { - result = false; - break; - } - } - return result; - } - - /** - * 判定输入汉字 - * @param c - * @return - */ - public static boolean isChinese(char c) { - Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); - if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS - || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS - || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A - || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION - || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION - || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { - return true; - } - return false; - } - - // == 字符串处理方法 == - - /** - * 首字母大写 - * @param str 待转字符串 - * @return 首字母大写字符串 - */ - public static String upperFirstLetter(final String str) { - if (isEmpty(str) || !Character.isLowerCase(str.charAt(0))) return str; - try { - return String.valueOf((char) (str.charAt(0) - 32)) + str.substring(1); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "upperFirstLetter"); - return str; - } - } - - /** - * 首字母小写 - * @param str 待转字符串 - * @return 首字母小写字符串 - */ - public static String lowerFirstLetter(final String str) { - if (isEmpty(str) || !Character.isUpperCase(str.charAt(0))) return str; - try { - return String.valueOf((char) (str.charAt(0) + 32)) + str.substring(1); - } catch (Exception e){ - JCLogUtils.eTag(TAG, e, "lowerFirstLetter"); - return str; - } - } - - /** - * 反转字符串 - * @param str 待反转字符串 - * @return 反转字符串 - */ - public static String reverse(final String str) { - int len = length(str); - if (len <= 1) return str; - int mid = len >> 1; - char[] chars = str.toCharArray(); - char c; - for (int i = 0; i < mid; ++i) { - c = chars[i]; - chars[i] = chars[len - i - 1]; - chars[len - i - 1] = c; - } - return new String(chars); - } - - /** - * 字符串连接,将参数列表拼接为一个字符串 - * @param more 追加 - * @return 返回拼接后的字符串 - */ - public static String concat(Object... more) { - return concatSpiltWith("", more); - } - - /** - * 字符串连接,将参数列表拼接为一个字符串 - * @param split - * @param more - * @return 回拼接后的字符串 - */ - public static String concatSpiltWith(String split, Object... more) { - if (more == null) return null; - StringBuilder buf = new StringBuilder(); - for (int i = 0, len = more.length; i < len; i++) { - if (i != 0) buf.append(split); - buf.append(more[i]); - } - return buf.toString(); - } - - /** - * 下划线命名转为驼峰命名 - * @param str 下划线命名格式 - * @return 驼峰命名格式 - */ - public static final String underScoreCase2CamelCase(String str) { - if (isEmpty(str)) return str; - if (!str.contains("_")) return str; - StringBuilder sb = new StringBuilder(); - char[] chars = str.toCharArray(); - boolean hitUnderScore = false; - sb.append(chars[0]); - for (int i = 1, len = chars.length; i < len; i++) { - char c = chars[i]; - if (c == '_') { - hitUnderScore = true; - } else { - if (hitUnderScore) { - sb.append(Character.toUpperCase(c)); - hitUnderScore = false; - } else { - sb.append(c); - } - } - } - return sb.toString(); - } - - /** - * 驼峰命名法转为下划线命名 - * @param str 驼峰命名格式 - * @return 下划线命名格式 - */ - public static final String camelCase2UnderScoreCase(String str) { - if (isEmpty(str)) return str; - StringBuilder sb = new StringBuilder(); - char[] chars = str.toCharArray(); - for (int i = 0, len = chars.length; i < len; i++) { - char c = chars[i]; - if (Character.isUpperCase(c)) { - sb.append("_").append(Character.toLowerCase(c)); - } else { - sb.append(c); - } - } - return sb.toString(); - } - - /** - * 数据库字符转义 - * @param keyWord - * @return - */ - public static String sqliteEscape(String keyWord){ - if (isEmpty(keyWord)) return keyWord; - // 替换关键字 - keyWord = keyWord.replace("/", "//"); - keyWord = keyWord.replace("'", "''"); - keyWord = keyWord.replace("[", "/["); - keyWord = keyWord.replace("]", "/]"); - keyWord = keyWord.replace("%", "/%"); - keyWord = keyWord.replace("&","/&"); - keyWord = keyWord.replace("_", "/_"); - keyWord = keyWord.replace("(", "/("); - keyWord = keyWord.replace(")", "/)"); - return keyWord; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/assist/Averager.java b/DevLibUtils/src/main/java/dev/utils/common/assist/Averager.java deleted file mode 100644 index 1c8d3cfa62..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/assist/Averager.java +++ /dev/null @@ -1,60 +0,0 @@ -package dev.utils.common.assist; - -import java.util.ArrayList; - -/** - * detail: 用以统计平均数 - * @author MaTianyu - */ -public class Averager { - - private ArrayList numList = new ArrayList(); - - /** - * 添加一个数字 - * @param num - */ - public synchronized void add(Number num) { - numList.add(num); - } - - /** - * 清除全部 - */ - public void clear() { - numList.clear(); - } - - /** - * 返回参与均值计算的数字个数 - * @return - */ - public Number size() { - return numList.size(); - } - - /** - * 获取平均数 - * @return - */ - public Number getAverage() { - if (numList.size() == 0) { - return 0; - } else { - Float sum = 0f; - for (int i = 0, size = numList.size(); i < size; i++) { - sum = sum.floatValue() + numList.get(i).floatValue(); - } - return sum / numList.size(); - } - } - - /** - * 打印数字列 - * @return - */ - public String print() { - return "PrintList(" + size() + "): " + numList; - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/assist/TimeAverager.java b/DevLibUtils/src/main/java/dev/utils/common/assist/TimeAverager.java deleted file mode 100644 index 54d55ee6c5..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/assist/TimeAverager.java +++ /dev/null @@ -1,60 +0,0 @@ -package dev.utils.common.assist; - -/** - * detail: 时间均值计算器, 只能用于单线程计时。 - * @author MaTianyu - */ -public class TimeAverager { - - /** 计时器 */ - private TimeCounter tc = new TimeCounter(); - - /** 均值器 */ - private Averager av = new Averager(); - - /** - * 一个计时开始 - */ - public long start() { - return tc.start(); - } - - /** - * 一个计时结束 - */ - public long end() { - long time = tc.duration(); - av.add(time); - return time; - } - - /** - * 一个计时结束,并且启动下次计时。 - */ - public long endAndRestart() { - long time = tc.durationRestart(); - av.add(time); - return time; - } - - /** - * 求全部计时均值 - */ - public Number average() { - return av.getAverage(); - } - - /** - * 打印全部时间值 - */ - public void print() { - av.print(); - } - - /** - * 清除数据 - */ - public void clear() { - av.clear(); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/assist/TimeCounter.java b/DevLibUtils/src/main/java/dev/utils/common/assist/TimeCounter.java deleted file mode 100644 index 0bbf7fd11b..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/assist/TimeCounter.java +++ /dev/null @@ -1,39 +0,0 @@ -package dev.utils.common.assist; - -/** - * detail: 时间计时器 - * @author MaTianyu - */ -public class TimeCounter { - - private long t; - - public TimeCounter() { - start(); - } - - /** - * Count start. - */ - public long start() { - t = System.currentTimeMillis(); - return t; - } - - /** - * Get duration and restart. - */ - public long durationRestart() { - long now = System.currentTimeMillis(); - long d = now - t; - t = now; - return d; - } - - /** - * Get duration. - */ - public long duration() { - return System.currentTimeMillis() - t; - } -} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/assist/TimeKeeper.java b/DevLibUtils/src/main/java/dev/utils/common/assist/TimeKeeper.java deleted file mode 100644 index 7549c4e62d..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/assist/TimeKeeper.java +++ /dev/null @@ -1,75 +0,0 @@ -package dev.utils.common.assist; - -import android.os.SystemClock; - -/** - * detail: 时间堵塞保留 - * @author 氢一 - */ -public class TimeKeeper { - - // 预计堵塞时间 - private long keepTimeMillis; - // 开始计时时间 - private long startMillis; - - /** - * 构造函数 - * @param keepTimeMillis - */ - public TimeKeeper(long keepTimeMillis) { - this.keepTimeMillis = keepTimeMillis; - } - - /** - * 获取预计堵塞时间 - * @return - */ - public long getKeepTimeMillis() { - return keepTimeMillis; - } - - /** - * 设置预计堵塞时间 - * @param keepTimeMillis - * @return - */ - public TimeKeeper setKeepTimeMillis(long keepTimeMillis) { - this.keepTimeMillis = keepTimeMillis; - return this; - } - - /** - * 开始计时 - * @return - */ - public TimeKeeper startNow() { - startMillis = SystemClock.elapsedRealtime(); - return this; - } - - public TimeKeeper waitForEnd(OnEndCallback endCallback) { - long costMillis = SystemClock.elapsedRealtime() - startMillis; - long leftMillis = keepTimeMillis - costMillis; - if (leftMillis > 0) { - SystemClock.sleep(leftMillis); - endCallback.onEnd(costMillis, leftMillis); - } else { - endCallback.onEnd(costMillis, leftMillis); - } - return this; - } - - /** - * 结束通知回调 - */ - public interface OnEndCallback { - - /** - * 结束触发通知方法 - * @param costTime 使用 -> 花费时间 - * @param leftTime 堵塞时间 - */ - void onEnd(long costTime, long leftTime); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/cipher/Base64Cipher.java b/DevLibUtils/src/main/java/dev/utils/common/cipher/Base64Cipher.java deleted file mode 100644 index dd454cb488..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/cipher/Base64Cipher.java +++ /dev/null @@ -1,47 +0,0 @@ -package dev.utils.common.cipher; - -import dev.utils.common.assist.Base64; - -/** - * detail: Baes64 编解码, 进行加密 - * @author MaTianyu - */ -public class Base64Cipher implements Cipher { - - // 中间加密层 - private Cipher cipher; - - public Base64Cipher() { - } - - public Base64Cipher(Cipher cipher) { - this.cipher = cipher; - } - - /** - * 解码 - * @param res - * @return - */ - @Override - public byte[] decrypt(byte[] res) { - res = Base64.decode(res, Base64.DEFAULT); - if (cipher != null) { - res = cipher.decrypt(res); - } - return res; - } - - /** - * 编码 - * @param res - * @return - */ - @Override - public byte[] encrypt(byte[] res) { - if (cipher != null) { - res = cipher.encrypt(res); - } - return Base64.encode(res, Base64.DEFAULT); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/cipher/CipherUtils.java b/DevLibUtils/src/main/java/dev/utils/common/cipher/CipherUtils.java deleted file mode 100644 index 6ebe62a684..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/cipher/CipherUtils.java +++ /dev/null @@ -1,78 +0,0 @@ -package dev.utils.common.cipher; - -import dev.utils.common.ByteUtils; -import dev.utils.common.HexUtils; - -/** - * detail: 加密工具类 - * Created by Ttt - */ -public class CipherUtils { - - private CipherUtils(){ - } - - // 保存加密: 如SP - // 保存到SP,可以通过 SP.put(key, CipherUtils.encrypt(obj, cipher)); - // 获取 Object obj = CipherUtils.decrypt(SP.get(key), cipher); - - /** - * 加密工具类 - * @param obj - * @return - */ - public static String encrypt(Object obj){ - return encrypt(obj, null); - } - - /** - * 加密工具类 - * @param obj - * @param cipher - * @return - */ - public static String encrypt(Object obj, Cipher cipher){ - if (obj == null) return null; - byte[] bytes = ByteUtils.objectToByte(obj); - if (cipher != null) bytes = cipher.encrypt(bytes); - return HexUtils.encodeHexStr(bytes); - } - - // - - - /** - * 解密方法 - * @param hex - * @return - */ - public static Object decrypt (String hex){ - return decrypt(hex, null); - } - - /** - * 解密方法 - * @param hex - * @param cipher - * @return - */ - public static Object decrypt (String hex, Cipher cipher){ - if (hex == null) return null; - byte[] bytes = HexUtils.decodeHex(hex.toCharArray()); - if (cipher != null) bytes = cipher.decrypt(bytes); - Object obj = ByteUtils.byteToObject(bytes); - return obj; - } - - -// private static Cipher cipher = new Cipher() { -// @Override -// public byte[] decrypt(byte[] res) { -// return res; -// } -// -// @Override -// public byte[] encrypt(byte[] res) { -// return res; -// } -// }; -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/cipher/Decrypt.java b/DevLibUtils/src/main/java/dev/utils/common/cipher/Decrypt.java deleted file mode 100644 index e56917739e..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/cipher/Decrypt.java +++ /dev/null @@ -1,11 +0,0 @@ -package dev.utils.common.cipher; - -/** - * detail: 解密/解码接口 - * @author MaTianyu - */ -public interface Decrypt { - - byte[] decrypt(byte[] res); - -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/cipher/Encrypt.java b/DevLibUtils/src/main/java/dev/utils/common/cipher/Encrypt.java deleted file mode 100644 index c95b3f7a62..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/cipher/Encrypt.java +++ /dev/null @@ -1,11 +0,0 @@ -package dev.utils.common.cipher; - -/** - * detail: 加密/编码接口 - * @author MaTianyu - */ -public interface Encrypt { - - byte[] encrypt(byte[] res); - -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/CRCUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/CRCUtils.java deleted file mode 100644 index ecc931776a..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/CRCUtils.java +++ /dev/null @@ -1,73 +0,0 @@ -package dev.utils.common.encrypt; - -import java.io.FileInputStream; -import java.io.InputStream; -import java.util.zip.CRC32; - -import dev.utils.JCLogUtils; - -/** - * detail: CRC 工具类 - * Created by Ttt - */ -public final class CRCUtils { - - private CRCUtils() { - } - - // 日志TAG - private static final String TAG = CRCUtils.class.getSimpleName(); - - /** - * 获取 CRC32 值(返回Long,一定几率上唯一) - * @param str - * @return - */ - public static long getCRC32(String str){ - try { - CRC32 crc32 = new CRC32(); - crc32.update(str.getBytes()); - return crc32.getValue(); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "getCRC32"); - } - return -1l; - } - - /** - * 获取 CRC32 值(做了处理,返回String) - * @param str - * @return - */ - public static String getCRC32Str(String str){ - try { - CRC32 crc32 = new CRC32(); - crc32.update(str.getBytes()); - return Long.toHexString(crc32.getValue()); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "getCRC32Str"); - } - return null; - } - - /** - * 获取文件CRC32 值 - * @return - */ - public static String getFileCrc32(String fPath){ - try { - InputStream fis = new FileInputStream(fPath); - byte[] buffer = new byte[1024]; - CRC32 crc32 = new CRC32(); - int numRead = 0; - while ((numRead = fis.read(buffer)) > 0) { - crc32.update(buffer, 0, numRead); - } - fis.close(); - return Long.toHexString(crc32.getValue()); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "getFileCrc32"); - } - return null; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/EncryptUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/EncryptUtils.java deleted file mode 100644 index b47a4bba4a..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/EncryptUtils.java +++ /dev/null @@ -1,985 +0,0 @@ -package dev.utils.common.encrypt; - -import android.util.Base64; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.security.DigestInputStream; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.spec.AlgorithmParameterSpec; - -import javax.crypto.Cipher; -import javax.crypto.Mac; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import dev.utils.JCLogUtils; -import dev.utils.common.CloseUtils; - -/** - * detail: 加密工具类 - * Created by Ttt - */ -public final class EncryptUtils { - - private EncryptUtils() { - } - - // 日志TAG - private static final String TAG = EncryptUtils.class.getSimpleName(); - - /////////////////////////////////////////////////////////////////////////// - // hash encryption - /////////////////////////////////////////////////////////////////////////// - - /** - * Return the hex string of MD2 encryption. - * @param data The data. - * @return the hex string of MD2 encryption - */ - public static String encryptMD2ToString(final String data) { - return encryptMD2ToString(data.getBytes()); - } - - /** - * Return the hex string of MD2 encryption. - * - * @param data The data. - * @return the hex string of MD2 encryption - */ - public static String encryptMD2ToString(final byte[] data) { - return bytes2HexString(encryptMD2(data)); - } - - /** - * Return the bytes of MD2 encryption. - * - * @param data The data. - * @return the bytes of MD2 encryption - */ - public static byte[] encryptMD2(final byte[] data) { - return hashTemplate(data, "MD2"); - } - - /** - * Return the hex string of MD5 encryption. - * - * @param data The data. - * @return the hex string of MD5 encryption - */ - public static String encryptMD5ToString(final String data) { - return encryptMD5ToString(data.getBytes()); - } - - /** - * Return the hex string of MD5 encryption. - * - * @param data The data. - * @param salt The salt. - * @return the hex string of MD5 encryption - */ - public static String encryptMD5ToString(final String data, final String salt) { - return bytes2HexString(encryptMD5((data + salt).getBytes())); - } - - /** - * Return the hex string of MD5 encryption. - * - * @param data The data. - * @return the hex string of MD5 encryption - */ - public static String encryptMD5ToString(final byte[] data) { - return bytes2HexString(encryptMD5(data)); - } - - /** - * Return the hex string of MD5 encryption. - * - * @param data The data. - * @param salt The salt. - * @return the hex string of MD5 encryption - */ - public static String encryptMD5ToString(final byte[] data, final byte[] salt) { - if (data == null || salt == null) return null; - byte[] dataSalt = new byte[data.length + salt.length]; - System.arraycopy(data, 0, dataSalt, 0, data.length); - System.arraycopy(salt, 0, dataSalt, data.length, salt.length); - return bytes2HexString(encryptMD5(dataSalt)); - } - - /** - * Return the bytes of MD5 encryption. - * - * @param data The data. - * @return the bytes of MD5 encryption - */ - public static byte[] encryptMD5(final byte[] data) { - return hashTemplate(data, "MD5"); - } - - /** - * Return the hex string of file's MD5 encryption. - * - * @param filePath The path of file. - * @return the hex string of file's MD5 encryption - */ - public static String encryptMD5File2String(final String filePath) { - File file = isSpace(filePath) ? null : new File(filePath); - return encryptMD5File2String(file); - } - - /** - * Return the bytes of file's MD5 encryption. - * - * @param filePath The path of file. - * @return the bytes of file's MD5 encryption - */ - public static byte[] encryptMD5File(final String filePath) { - File file = isSpace(filePath) ? null : new File(filePath); - return encryptMD5File(file); - } - - /** - * Return the hex string of file's MD5 encryption. - * - * @param file The file. - * @return the hex string of file's MD5 encryption - */ - public static String encryptMD5File2String(final File file) { - return bytes2HexString(encryptMD5File(file)); - } - - /** - * Return the bytes of file's MD5 encryption. - * - * @param file The file. - * @return the bytes of file's MD5 encryption - */ - public static byte[] encryptMD5File(final File file) { - if (file == null) return null; - FileInputStream fis = null; - DigestInputStream digestInputStream; - try { - fis = new FileInputStream(file); - MessageDigest md = MessageDigest.getInstance("MD5"); - digestInputStream = new DigestInputStream(fis, md); - byte[] buffer = new byte[256 * 1024]; - while (true) { - if (!(digestInputStream.read(buffer) > 0)) break; - } - md = digestInputStream.getMessageDigest(); - return md.digest(); - } catch (NoSuchAlgorithmException | IOException e) { - JCLogUtils.eTag(TAG, e, "encryptMD5File"); - return null; - } finally { - CloseUtils.closeIO(fis); - } - } - - /** - * Return the hex string of SHA1 encryption. - * - * @param data The data. - * @return the hex string of SHA1 encryption - */ - public static String encryptSHA1ToString(final String data) { - return encryptSHA1ToString(data.getBytes()); - } - - /** - * Return the hex string of SHA1 encryption. - * - * @param data The data. - * @return the hex string of SHA1 encryption - */ - public static String encryptSHA1ToString(final byte[] data) { - return bytes2HexString(encryptSHA1(data)); - } - - /** - * Return the bytes of SHA1 encryption. - * - * @param data The data. - * @return the bytes of SHA1 encryption - */ - public static byte[] encryptSHA1(final byte[] data) { - return hashTemplate(data, "SHA1"); - } - - /** - * Return the hex string of SHA224 encryption. - * - * @param data The data. - * @return the hex string of SHA224 encryption - */ - public static String encryptSHA224ToString(final String data) { - return encryptSHA224ToString(data.getBytes()); - } - - /** - * Return the hex string of SHA224 encryption. - * - * @param data The data. - * @return the hex string of SHA224 encryption - */ - public static String encryptSHA224ToString(final byte[] data) { - return bytes2HexString(encryptSHA224(data)); - } - - /** - * Return the bytes of SHA224 encryption. - * - * @param data The data. - * @return the bytes of SHA224 encryption - */ - public static byte[] encryptSHA224(final byte[] data) { - return hashTemplate(data, "SHA224"); - } - - /** - * Return the hex string of SHA256 encryption. - * - * @param data The data. - * @return the hex string of SHA256 encryption - */ - public static String encryptSHA256ToString(final String data) { - return encryptSHA256ToString(data.getBytes()); - } - - /** - * Return the hex string of SHA256 encryption. - * - * @param data The data. - * @return the hex string of SHA256 encryption - */ - public static String encryptSHA256ToString(final byte[] data) { - return bytes2HexString(encryptSHA256(data)); - } - - /** - * Return the bytes of SHA256 encryption. - * - * @param data The data. - * @return the bytes of SHA256 encryption - */ - public static byte[] encryptSHA256(final byte[] data) { - return hashTemplate(data, "SHA256"); - } - - /** - * Return the hex string of SHA384 encryption. - * - * @param data The data. - * @return the hex string of SHA384 encryption - */ - public static String encryptSHA384ToString(final String data) { - return encryptSHA384ToString(data.getBytes()); - } - - /** - * Return the hex string of SHA384 encryption. - * - * @param data The data. - * @return the hex string of SHA384 encryption - */ - public static String encryptSHA384ToString(final byte[] data) { - return bytes2HexString(encryptSHA384(data)); - } - - /** - * Return the bytes of SHA384 encryption. - * - * @param data The data. - * @return the bytes of SHA384 encryption - */ - public static byte[] encryptSHA384(final byte[] data) { - return hashTemplate(data, "SHA384"); - } - - /** - * Return the hex string of SHA512 encryption. - * - * @param data The data. - * @return the hex string of SHA512 encryption - */ - public static String encryptSHA512ToString(final String data) { - return encryptSHA512ToString(data.getBytes()); - } - - /** - * Return the hex string of SHA512 encryption. - * - * @param data The data. - * @return the hex string of SHA512 encryption - */ - public static String encryptSHA512ToString(final byte[] data) { - return bytes2HexString(encryptSHA512(data)); - } - - /** - * Return the bytes of SHA512 encryption. - * - * @param data The data. - * @return the bytes of SHA512 encryption - */ - public static byte[] encryptSHA512(final byte[] data) { - return hashTemplate(data, "SHA512"); - } - - /** - * Return the bytes of hash encryption. - * - * @param data The data. - * @param algorithm The name of hash encryption. - * @return the bytes of hash encryption - */ - private static byte[] hashTemplate(final byte[] data, final String algorithm) { - if (data == null || data.length <= 0) return null; - try { - MessageDigest md = MessageDigest.getInstance(algorithm); - md.update(data); - return md.digest(); - } catch (NoSuchAlgorithmException e) { - JCLogUtils.eTag(TAG, e, "hashTemplate"); - return null; - } - } - - /////////////////////////////////////////////////////////////////////////// - // hmac encryption - /////////////////////////////////////////////////////////////////////////// - - /** - * Return the hex string of HmacMD5 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacMD5 encryption - */ - public static String encryptHmacMD5ToString(final String data, final String key) { - return encryptHmacMD5ToString(data.getBytes(), key.getBytes()); - } - - /** - * Return the hex string of HmacMD5 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacMD5 encryption - */ - public static String encryptHmacMD5ToString(final byte[] data, final byte[] key) { - return bytes2HexString(encryptHmacMD5(data, key)); - } - - /** - * Return the bytes of HmacMD5 encryption. - * - * @param data The data. - * @param key The key. - * @return the bytes of HmacMD5 encryption - */ - public static byte[] encryptHmacMD5(final byte[] data, final byte[] key) { - return hmacTemplate(data, key, "HmacMD5"); - } - - /** - * Return the hex string of HmacSHA1 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacSHA1 encryption - */ - public static String encryptHmacSHA1ToString(final String data, final String key) { - return encryptHmacSHA1ToString(data.getBytes(), key.getBytes()); - } - - /** - * Return the hex string of HmacSHA1 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacSHA1 encryption - */ - public static String encryptHmacSHA1ToString(final byte[] data, final byte[] key) { - return bytes2HexString(encryptHmacSHA1(data, key)); - } - - /** - * Return the bytes of HmacSHA1 encryption. - * - * @param data The data. - * @param key The key. - * @return the bytes of HmacSHA1 encryption - */ - public static byte[] encryptHmacSHA1(final byte[] data, final byte[] key) { - return hmacTemplate(data, key, "HmacSHA1"); - } - - /** - * Return the hex string of HmacSHA224 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacSHA224 encryption - */ - public static String encryptHmacSHA224ToString(final String data, final String key) { - return encryptHmacSHA224ToString(data.getBytes(), key.getBytes()); - } - - /** - * Return the hex string of HmacSHA224 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacSHA224 encryption - */ - public static String encryptHmacSHA224ToString(final byte[] data, final byte[] key) { - return bytes2HexString(encryptHmacSHA224(data, key)); - } - - /** - * Return the bytes of HmacSHA224 encryption. - * - * @param data The data. - * @param key The key. - * @return the bytes of HmacSHA224 encryption - */ - public static byte[] encryptHmacSHA224(final byte[] data, final byte[] key) { - return hmacTemplate(data, key, "HmacSHA224"); - } - - /** - * Return the hex string of HmacSHA256 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacSHA256 encryption - */ - public static String encryptHmacSHA256ToString(final String data, final String key) { - return encryptHmacSHA256ToString(data.getBytes(), key.getBytes()); - } - - /** - * Return the hex string of HmacSHA256 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacSHA256 encryption - */ - public static String encryptHmacSHA256ToString(final byte[] data, final byte[] key) { - return bytes2HexString(encryptHmacSHA256(data, key)); - } - - /** - * Return the bytes of HmacSHA256 encryption. - * - * @param data The data. - * @param key The key. - * @return the bytes of HmacSHA256 encryption - */ - public static byte[] encryptHmacSHA256(final byte[] data, final byte[] key) { - return hmacTemplate(data, key, "HmacSHA256"); - } - - /** - * Return the hex string of HmacSHA384 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacSHA384 encryption - */ - public static String encryptHmacSHA384ToString(final String data, final String key) { - return encryptHmacSHA384ToString(data.getBytes(), key.getBytes()); - } - - /** - * Return the hex string of HmacSHA384 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacSHA384 encryption - */ - public static String encryptHmacSHA384ToString(final byte[] data, final byte[] key) { - return bytes2HexString(encryptHmacSHA384(data, key)); - } - - /** - * Return the bytes of HmacSHA384 encryption. - * - * @param data The data. - * @param key The key. - * @return the bytes of HmacSHA384 encryption - */ - public static byte[] encryptHmacSHA384(final byte[] data, final byte[] key) { - return hmacTemplate(data, key, "HmacSHA384"); - } - - /** - * Return the hex string of HmacSHA512 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacSHA512 encryption - */ - public static String encryptHmacSHA512ToString(final String data, final String key) { - return encryptHmacSHA512ToString(data.getBytes(), key.getBytes()); - } - - /** - * Return the hex string of HmacSHA512 encryption. - * - * @param data The data. - * @param key The key. - * @return the hex string of HmacSHA512 encryption - */ - public static String encryptHmacSHA512ToString(final byte[] data, final byte[] key) { - return bytes2HexString(encryptHmacSHA512(data, key)); - } - - /** - * Return the bytes of HmacSHA512 encryption. - * - * @param data The data. - * @param key The key. - * @return the bytes of HmacSHA512 encryption - */ - public static byte[] encryptHmacSHA512(final byte[] data, final byte[] key) { - return hmacTemplate(data, key, "HmacSHA512"); - } - - /** - * Return the bytes of hmac encryption. - * - * @param data The data. - * @param key The key. - * @param algorithm The name of hmac encryption. - * @return the bytes of hmac encryption - */ - private static byte[] hmacTemplate(final byte[] data, - final byte[] key, - final String algorithm) { - if (data == null || data.length == 0 || key == null || key.length == 0) return null; - try { - SecretKeySpec secretKey = new SecretKeySpec(key, algorithm); - Mac mac = Mac.getInstance(algorithm); - mac.init(secretKey); - return mac.doFinal(data); - } catch (InvalidKeyException | NoSuchAlgorithmException e) { - JCLogUtils.eTag(TAG, e, "hmacTemplate"); - return null; - } - } - - /////////////////////////////////////////////////////////////////////////// - // DES encryption - /////////////////////////////////////////////////////////////////////////// - - /** - * Return the Base64-encode bytes of DES encryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the Base64-encode bytes of DES encryption - */ - public static byte[] encryptDES2Base64(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return base64Encode(encryptDES(data, key, transformation, iv)); - } - - /** - * Return the hex string of DES encryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the hex string of DES encryption - */ - public static String encryptDES2HexString(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return bytes2HexString(encryptDES(data, key, transformation, iv)); - } - - /** - * Return the bytes of DES encryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of DES encryption - */ - public static byte[] encryptDES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return symmetricTemplate(data, key, "DES", transformation, iv, true); - } - - /** - * Return the bytes of DES decryption for Base64-encode bytes. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of DES decryption for Base64-encode bytes - */ - public static byte[] decryptBase64DES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return decryptDES(base64Decode(data), key, transformation, iv); - } - - /** - * Return the bytes of DES decryption for hex string. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of DES decryption for hex string - */ - public static byte[] decryptHexStringDES(final String data, - final byte[] key, - final String transformation, - final byte[] iv) { - return decryptDES(hexString2Bytes(data), key, transformation, iv); - } - - /** - * Return the bytes of DES decryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of DES decryption - */ - public static byte[] decryptDES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return symmetricTemplate(data, key, "DES", transformation, iv, false); - } - - /////////////////////////////////////////////////////////////////////////// - // 3DES encryption - /////////////////////////////////////////////////////////////////////////// - - /** - * Return the Base64-encode bytes of 3DES encryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the Base64-encode bytes of 3DES encryption - */ - public static byte[] encrypt3DES2Base64(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return base64Encode(encrypt3DES(data, key, transformation, iv)); - } - - /** - * Return the hex string of 3DES encryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the hex string of 3DES encryption - */ - public static String encrypt3DES2HexString(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return bytes2HexString(encrypt3DES(data, key, transformation, iv)); - } - - /** - * Return the bytes of 3DES encryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of 3DES encryption - */ - public static byte[] encrypt3DES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return symmetricTemplate(data, key, "DESede", transformation, iv, true); - } - - /** - * Return the bytes of 3DES decryption for Base64-encode bytes. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of 3DES decryption for Base64-encode bytes - */ - public static byte[] decryptBase64_3DES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return decrypt3DES(base64Decode(data), key, transformation, iv); - } - - /** - * Return the bytes of 3DES decryption for hex string. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of 3DES decryption for hex string - */ - public static byte[] decryptHexString3DES(final String data, - final byte[] key, - final String transformation, - final byte[] iv) { - return decrypt3DES(hexString2Bytes(data), key, transformation, iv); - } - - /** - * Return the bytes of 3DES decryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of 3DES decryption - */ - public static byte[] decrypt3DES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return symmetricTemplate(data, key, "DESede", transformation, iv, false); - } - - /////////////////////////////////////////////////////////////////////////// - // AES encryption - /////////////////////////////////////////////////////////////////////////// - - /** - * Return the Base64-encode bytes of AES encryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the Base64-encode bytes of AES encryption - */ - public static byte[] encryptAES2Base64(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return base64Encode(encryptAES(data, key, transformation, iv)); - } - - /** - * Return the hex string of AES encryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the hex string of AES encryption - */ - public static String encryptAES2HexString(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return bytes2HexString(encryptAES(data, key, transformation, iv)); - } - - /** - * Return the bytes of AES encryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of AES encryption - */ - public static byte[] encryptAES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return symmetricTemplate(data, key, "AES", transformation, iv, true); - } - - /** - * Return the bytes of AES decryption for Base64-encode bytes. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of AES decryption for Base64-encode bytes - */ - public static byte[] decryptBase64AES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return decryptAES(base64Decode(data), key, transformation, iv); - } - - /** - * Return the bytes of AES decryption for hex string. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of AES decryption for hex string - */ - public static byte[] decryptHexStringAES(final String data, - final byte[] key, - final String transformation, - final byte[] iv) { - return decryptAES(hexString2Bytes(data), key, transformation, iv); - } - - /** - * Return the bytes of AES decryption. - * - * @param data The data. - * @param key The key. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param iv The buffer with the IV. The contents of the - * buffer are copied to protect against subsequent modification. - * @return the bytes of AES decryption - */ - public static byte[] decryptAES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { - return symmetricTemplate(data, key, "AES", transformation, iv, false); - } - - /** - * Return the bytes of symmetric encryption or decryption. - * - * @param data The data. - * @param key The key. - * @param algorithm The name of algorithm. - * @param transformation The name of the transformation, e.g., DES/CBC/PKCS5Padding. - * @param isEncrypt True to encrypt, false otherwise. - * @return the bytes of symmetric encryption or decryption - */ - private static byte[] symmetricTemplate(final byte[] data, - final byte[] key, - final String algorithm, - final String transformation, - final byte[] iv, - final boolean isEncrypt) { - if (data == null || data.length == 0 || key == null || key.length == 0) return null; - try { - SecretKeySpec keySpec = new SecretKeySpec(key, algorithm); - Cipher cipher = Cipher.getInstance(transformation); - if (iv == null || iv.length == 0) { - cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, keySpec); - } else { - AlgorithmParameterSpec params = new IvParameterSpec(iv); - cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, keySpec, params); - } - return cipher.doFinal(data); - } catch (Throwable e) { - JCLogUtils.eTag(TAG, e, "symmetricTemplate"); - return null; - } - } - - private static final char HEX_DIGITS[] = - {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - - private static String bytes2HexString(final byte[] bytes) { - if (bytes == null) return null; - int len = bytes.length; - if (len <= 0) return null; - char[] ret = new char[len << 1]; - for (int i = 0, j = 0; i < len; i++) { - ret[j++] = HEX_DIGITS[bytes[i] >>> 4 & 0x0f]; - ret[j++] = HEX_DIGITS[bytes[i] & 0x0f]; - } - return new String(ret); - } - - private static byte[] hexString2Bytes(String hexString) { - if (isSpace(hexString)) return null; - int len = hexString.length(); - if (len % 2 != 0) { - hexString = "0" + hexString; - len = len + 1; - } - char[] hexBytes = hexString.toUpperCase().toCharArray(); - byte[] ret = new byte[len >> 1]; - for (int i = 0; i < len; i += 2) { - ret[i >> 1] = (byte) (hex2Dec(hexBytes[i]) << 4 | hex2Dec(hexBytes[i + 1])); - } - return ret; - } - - private static int hex2Dec(final char hexChar) { - if (hexChar >= '0' && hexChar <= '9') { - return hexChar - '0'; - } else if (hexChar >= 'A' && hexChar <= 'F') { - return hexChar - 'A' + 10; - } else { - throw new IllegalArgumentException(); - } - } - - private static byte[] base64Encode(final byte[] input) { - return Base64.encode(input, Base64.NO_WRAP); - } - - private static byte[] base64Decode(final byte[] input) { - return Base64.decode(input, Base64.NO_WRAP); - } - - private static boolean isSpace(final String s) { - if (s == null) return true; - for (int i = 0, len = s.length(); i < len; ++i) { - if (!Character.isWhitespace(s.charAt(i))) { - return false; - } - } - return true; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/EscapeUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/EscapeUtils.java deleted file mode 100644 index 76a6995e3f..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/EscapeUtils.java +++ /dev/null @@ -1,153 +0,0 @@ -package dev.utils.common.encrypt; - -/** - * detail: 解码,编码 - * Created by Ttt - */ -public final class EscapeUtils { - - private EscapeUtils(){ - } - - /** 十六进制 - 0-255 */ - private final static String[] hex = { "00", "01", "02", "03", "04", "05", - "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", "10", - "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", - "1C", "1D", "1E", "1F", "20", "21", "22", "23", "24", "25", "26", - "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", "30", "31", - "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", - "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47", - "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", - "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", - "5E", "5F", "60", "61", "62", "63", "64", "65", "66", "67", "68", - "69", "6A", "6B", "6C", "6D", "6E", "6F", "70", "71", "72", "73", - "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", - "7F", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", - "8A", "8B", "8C", "8D", "8E", "8F", "90", "91", "92", "93", "94", - "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F", - "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", - "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3", "B4", "B5", - "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF", "C0", - "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", - "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", - "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", - "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", - "ED", "EE", "EF", "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", - "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF" }; - - private final static byte[] val = { 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F }; - - /** - * 编码 - * @param str - * @return - */ - public static String escape(String str) { - if (str == null){ - return null; - } - StringBuffer sbuf = new StringBuffer(); - for (int i = 0, len = str.length(); i < len; i++) { - int ch = str.charAt(i); - if ('A' <= ch && ch <= 'Z') { - sbuf.append((char) ch); - } else if ('a' <= ch && ch <= 'z') { - sbuf.append((char) ch); - } else if ('0' <= ch && ch <= '9') { - sbuf.append((char) ch); - } else if (ch == '-' || ch == '_' || ch == '.' || ch == '!' - || ch == '~' || ch == '*' || ch == '\'' || ch == '(' - || ch == ')') { - sbuf.append((char) ch); - } else if (ch <= 0x007F) { - sbuf.append('%'); - sbuf.append(hex[ch]); - } else { - sbuf.append('%'); - sbuf.append('u'); - sbuf.append(hex[(ch >>> 8)]); - sbuf.append(hex[(0x00FF & ch)]); - } - } - return sbuf.toString(); - } - - /** - * 解码 说明:本方法保证 不论参数s是否经过escape()编码,均能获取正确的“解码”结果 - * @param str - * @return - */ - public static String unescape(String str) { - if (str == null){ - return null; - } - StringBuffer sbuf = new StringBuffer(); - int i = 0; - int len = str.length(); - while (i < len) { - int ch = str.charAt(i); - if ('A' <= ch && ch <= 'Z') { - sbuf.append((char) ch); - } else if ('a' <= ch && ch <= 'z') { - sbuf.append((char) ch); - } else if ('0' <= ch && ch <= '9') { - sbuf.append((char) ch); - } else if (ch == '-' || ch == '_' || ch == '.' || ch == '!' - || ch == '~' || ch == '*' || ch == '\'' || ch == '(' - || ch == ')') { - sbuf.append((char) ch); - } else if (ch == '%') { - int cint = 0; - if ('u' != str.charAt(i + 1)) { - cint = (cint << 4) | val[str.charAt(i + 1)]; - cint = (cint << 4) | val[str.charAt(i + 2)]; - i += 2; - } else { - cint = (cint << 4) | val[str.charAt(i + 2)]; - cint = (cint << 4) | val[str.charAt(i + 3)]; - cint = (cint << 4) | val[str.charAt(i + 4)]; - cint = (cint << 4) | val[str.charAt(i + 5)]; - i += 5; - } - sbuf.append((char) cint); - } else { - sbuf.append((char) ch); - } - i++; - } - return sbuf.toString(); - } - -// public static void main(String[] args) { -// // 需要编码的数据 -// String stest = "需要编码的数据"; -// System.out.println(stest); -// // 编码 -// System.out.println(escape(stest)); -// // 解码 -// System.out.println(unescape(escape(stest))); -// } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/MD5Utils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/MD5Utils.java deleted file mode 100644 index f876109bf3..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/MD5Utils.java +++ /dev/null @@ -1,163 +0,0 @@ -package dev.utils.common.encrypt; - -import java.io.FileInputStream; -import java.io.InputStream; -import java.security.MessageDigest; - -import dev.utils.JCLogUtils; - -/** - * detail: MD5加密 不可逆(Message Digest,消息摘要算法) - * Created by Ttt - */ -public final class MD5Utils { - - private MD5Utils() { - } - - // 日志TAG - private static final String TAG = MD5Utils.class.getSimpleName(); - - /** 小写 */ - public static final char HEX_DIGITS[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; - /** 大写 */ - public static final char HEX_DIGITS_UPPER[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - - /** - * 加密内容 - 32位大小MD5 - 小写 - * @param str 加密内容 - * @return - */ - public final static String md5(String str) { - try { - return md5(str.getBytes()); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "MD5"); - } - return null; - } - - /** - * 加密内容 - 32位大小MD5 - 小写 - * @param bytes - * @return - */ - public final static String md5(byte[] bytes) { - try { - // 获得MD5摘要算法的 MessageDigest 对象 - MessageDigest mdInst = MessageDigest.getInstance("MD5"); - // 使用指定的字节更新摘要 - mdInst.update(bytes); - // 获得密文 - byte[] md = mdInst.digest(); - return toHexString(md, HEX_DIGITS); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "MD5"); - } - return null; - } - - /** - * 加密内容 - 32位大小MD5 - 大写 - * @param str 加密内容 - * @return - */ - public final static String md5Upper(String str) { - try { - return md5Upper(str.getBytes()); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "MD5Upper"); - } - return null; - } - - /** - * 加密内容 - 32位大小MD5 - 大写 - * @param bytes - * @return - */ - public final static String md5Upper(byte[] bytes) { - try { - // 获得MD5摘要算法的 MessageDigest 对象 - MessageDigest mdInst = MessageDigest.getInstance("MD5"); - // 使用指定的字节更新摘要 - mdInst.update(bytes); - // 获得密文 - byte[] md = mdInst.digest(); - return toHexString(md, HEX_DIGITS_UPPER); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "MD5Upper"); - } - return null; - } - - /** - * 进行转换 - * @param bData - * @return - */ - public static String toHexString(byte[] bData) { - return toHexString(bData, HEX_DIGITS); - } - - /** - * 进行转换 - * @param bData - * @param hexDigits - * @return - */ - public static String toHexString(byte[] bData, char[] hexDigits) { - if (bData == null || hexDigits == null){ - return null; - } - StringBuilder sBuilder = new StringBuilder(bData.length * 2); - for (int i = 0, len = bData.length; i < len; i++) { - sBuilder.append(hexDigits[(bData[i] & 0xf0) >>> 4]); - sBuilder.append(hexDigits[bData[i] & 0x0f]); - } - return sBuilder.toString(); - } - - /** - * 获取文件MD5值 - 小写 - * @param fPath 文件地址 - * @return - */ - public static String getFileMD5(String fPath) { - try { - InputStream fis = new FileInputStream(fPath); - byte[] buffer = new byte[1024]; - MessageDigest md5 = MessageDigest.getInstance("MD5"); - int numRead = 0; - while ((numRead = fis.read(buffer)) > 0) { - md5.update(buffer, 0, numRead); - } - fis.close(); - return toHexString(md5.digest(), HEX_DIGITS); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "getFileMD5"); - } - return null; - } - -// /** -// * MD5加密(小写) -// * @param str 源字符串 -// * @return 加密后的字符串 -// */ -// public static String md5(String str) { -// try { -// byte[] hash = MessageDigest.getInstance("MD5").digest(str.getBytes("UTF-8")); -// StringBuilder hex = new StringBuilder(hash.length * 2); -// for (byte b : hash) { -// if ((b & 0xFF) < 0x10) -// hex.append("0"); -// hex.append(Integer.toHexString(b & 0xFF)); -// } -// return hex.toString(); -// } catch (Exception e) { -// JCLogUtils.eTag(TAG, e, "md5"); -// } -// return null; -// } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/SHAUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/SHAUtils.java deleted file mode 100644 index 6853eda43f..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/SHAUtils.java +++ /dev/null @@ -1,149 +0,0 @@ -package dev.utils.common.encrypt; - -import java.io.FileInputStream; -import java.io.InputStream; -import java.security.MessageDigest; - -import dev.utils.JCLogUtils; - -/** - * detail: SHA 加密工具类 - * Created by Ttt - */ -public final class SHAUtils { - - private SHAUtils() { - } - - // 日志TAG - private static final String TAG = SHAUtils.class.getSimpleName(); - - /** 小写 */ - private static final char HEX_DIGITS[]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; - - /** - * 加密内容 SHA1 - * @param str 加密内容 - * @return - */ - public final static String sha1(String str) { - return shaHex(str, "SHA-1"); - } - - /** - * 加密内容 SHA224 - * @param str 加密内容 - * @return - */ - public final static String sha224(String str) { - return shaHex(str, "SHA-224"); - } - - /** - * 加密内容 SHA256 - * @param str 加密内容 - * @return - */ - public final static String sha256(String str) { - return shaHex(str, "SHA-256"); - } - - /** - * 加密内容 SHA384 - * @param str 加密内容 - * @return - */ - public final static String sha384(String str) { - return shaHex(str, "SHA-384"); - } - - /** - * 加密内容 SHA512 - * @param str 加密内容 - * @return - */ - public final static String sha512(String str) { - return shaHex(str, "SHA-512"); - } - - // ======= - - /** - * 加密内容 SHA - * @param str 加密内容 - * @param sha 加密算法 - * @return - */ - final static String shaHex(String str, String sha) { - try { - byte[] btInput = str.getBytes(); - // 获得 SHA-1 摘要算法的 MessageDigest 对象 - MessageDigest mdInst = MessageDigest.getInstance(sha); - // 使用指定的字节更新摘要 - mdInst.update(btInput); - // 获得密文 - byte[] md = mdInst.digest(); - return toHexString(md, HEX_DIGITS); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "shaHex"); - } - return null; - } - - /** - * 获取文件 Sha 值 - * @param fPath 文件地址 - * @return - */ - final static String getFileSHA(String fPath, String sha) { - try { - InputStream fis = new FileInputStream(fPath); - byte[] buffer = new byte[1024]; - MessageDigest md5 = MessageDigest.getInstance(sha); - int numRead = 0; - while ((numRead = fis.read(buffer)) > 0) { - md5.update(buffer, 0, numRead); - } - fis.close(); - return toHexString(md5.digest(), HEX_DIGITS); - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "getFileSHA"); - } - return null; - } - - // ======= - - /** - * 进行转换 - * @param bData - * @param hexDigits - * @return - */ - static String toHexString(byte[] bData, char[] hexDigits) { - StringBuilder sBuilder = new StringBuilder(bData.length * 2); - for (int i = 0, len = bData.length; i < len; i++) { - sBuilder.append(hexDigits[(bData[i] & 0xf0) >>> 4]); - sBuilder.append(hexDigits[bData[i] & 0x0f]); - } - return sBuilder.toString(); - } - - /** - * 获取文件 Sha1 值 - * @param fPath 文件地址 - * @return - */ - public static String getFileSHA1(String fPath) { - return getFileSHA(fPath, "SHA-1"); - } - - /** - * 获取文件 Sha256 值 - * @param fPath 文件地址 - * @return - */ - public static String getFileSHA256(String fPath) { - return getFileSHA(fPath, "SHA-256"); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadManager.java b/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadManager.java deleted file mode 100644 index 9a430696a6..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadManager.java +++ /dev/null @@ -1,102 +0,0 @@ -package dev.utils.common.thread; - -import java.util.HashMap; -import java.util.LinkedHashMap; - -/** - * detail: 线程池管理 - 开发类 - * Created by Ttt - */ -public final class DevThreadManager { - - /** 默认通用线程池 = 通过CPU自动处理 */ - private static final DevThreadPool sDevThreadPool = new DevThreadPool(DevThreadPool.DevThreadPoolType.CALC_CPU); - /** 线程池数据 */ - private static final LinkedHashMap mapThreads = new LinkedHashMap<>(); - /** 配置数据 */ - private static final HashMap mapConfig = new HashMap<>(); - - /** 禁止构造对象 */ - private DevThreadManager() { - } - - /** - * 获取 DevThreadManager 实例 ,单例模式 - * @param nThreads - * @return - */ - public static synchronized DevThreadPool getInstance(int nThreads) { - // 初始化key - String key = "n_" + nThreads; - // 如果不为null, 则直接返回 - DevThreadPool devThreadPool = mapThreads.get(key); - if (devThreadPool != null){ - return devThreadPool; - } - devThreadPool = new DevThreadPool(nThreads); - mapThreads.put(key, devThreadPool); - return devThreadPool; - } - - /** - * 获取 DevThreadManager 实例 ,单例模式 - * @param key - * @return - */ - public static synchronized DevThreadPool getInstance(String key) { - // 如果不为null, 则直接返回 - DevThreadPool devThreadPool = mapThreads.get(key); - if (devThreadPool != null){ - return devThreadPool; - } - Object obj = mapConfig.get(key); - if (obj != null){ - try { - // 判断是否属于线程池类型 - if (obj instanceof DevThreadPool.DevThreadPoolType){ - devThreadPool = new DevThreadPool((DevThreadPool.DevThreadPoolType) obj); - } else if (obj instanceof Integer){ - devThreadPool = new DevThreadPool((Integer) obj); - } else { // 其他类型, 统一转换 Integer - devThreadPool = new DevThreadPool(Integer.parseInt((String) obj)); - } - if (devThreadPool != null) { - mapThreads.put(key, devThreadPool); - return devThreadPool; - } - } catch (Exception e){ - return sDevThreadPool; - } - } - return sDevThreadPool; - } - - // === - - /** - * 初始化配置信息 - * @param mapConfig - */ - public static void initConfig(HashMap mapConfig){ - if (mapConfig != null){ - mapConfig.putAll(mapConfig); - } - } - - /** - * 添加配置信息 - * @param key - * @param val - */ - public static void putConfig(String key, Object val){ - mapConfig.put(key, val); - } - - /** - * 移除配置信息 - * @param key - */ - public static void removeConfig(String key){ - mapConfig.remove(key); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadPool.java b/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadPool.java deleted file mode 100644 index b995c02a2e..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadPool.java +++ /dev/null @@ -1,415 +0,0 @@ -package dev.utils.common.thread; - -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * detail: 线程池 - 开发类 - * Created by Ttt - */ -public final class DevThreadPool { - -// // https://www.jianshu.com/p/4d4634c92253 -// // 创建线程池 -// 1. 线程池里面管理多少个线程 -// 2. 如果排队满了, 额外的开的线程数 -// 3. 如果线程池没有要执行的任务 存活多久 -// 4. 时间的单位 -// 5. 如果 线程池里管理的线程都已经用了,剩下的任务 临时存到LinkedBlockingQueue对象中 排队 -// public ThreadPoolExecutor(int corePoolSize, -// int maximumPoolSize, -// long keepAliveTime, -// TimeUnit unit, -// BlockingQueue workQueue) { -// this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, -// Executors.defaultThreadFactory(), defaultHandler); -// } - - // ====== - - /** 线程池对象 */ - private final ExecutorService threadPool; - /** 定时任务线程池 */ - private ScheduledExecutorService scheduleExec; - - /** - * 构造函数 - * @param nThreads - */ - public DevThreadPool(int nThreads){ - // 如果小于等于 0, 则默认使用 1 - if (nThreads <= 0){ - nThreads = 1; - } - this.threadPool = Executors.newFixedThreadPool(nThreads); - // 初始化定时器任务 - this.scheduleExec = Executors.newScheduledThreadPool(nThreads); - } - - /** - * 构造函数 - * @param threadPool - */ - public DevThreadPool(ExecutorService threadPool){ - this.threadPool = threadPool; - } - - public DevThreadPool(DevThreadPoolType devThreadPoolType){ - // 初始化定时器任务 - this.scheduleExec = Executors.newScheduledThreadPool(getThreads()); - // = - if (devThreadPoolType != null){ - // = - switch (devThreadPoolType) { - case SINGLE: - threadPool = Executors.newSingleThreadExecutor(); - // 初始化定时器任务 - this.scheduleExec = Executors.newScheduledThreadPool(1); - break; -// case AUTO_CPU: -// //threadPool = Executors.newWorkStealingPool(); -// break; - case CALC_CPU: - threadPool = Executors.newFixedThreadPool(getThreads()); - break; - case CACHE: - threadPool = Executors.newCachedThreadPool(); - break; - default: - threadPool = Executors.newFixedThreadPool(getThreads()); - break; - } - } else { - threadPool = Executors.newFixedThreadPool(getThreads()); - } - } - - public enum DevThreadPoolType { - - // http://blog.csdn.net/a369414641/article/details/48342253 - - // http://blog.csdn.net/vking_wang/article/details/9619137 - - // http://ifeve.com/java8-concurrency-tutorial-thread-executor-examples/ - - // http://blog.csdn.net/sadfishsc/article/details/16980213 - - // 如果当前线程意外终止,会创建一个新线程继续执行任务,这和我们直接创建线程不同,也和newFixedThreadPool(1)不同。 - SINGLE, // newSingleThreadExecutor 获取的是一个单个的线程,这个线程会保证你的任务执行完成。 - - AUTO_CPU, // 根据CPU来创建(自定义创建) - - CALC_CPU, // 手动计算CPU来创建 - - CACHE, // 可缓存线程池 - -// 1 newCachedThreadPool :创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 -// 2 newFixedThreadPool:创建一个固定数目的、可重用的线程池。 -// 3 newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。 -// 4 newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 -// 5 newSingleThreadScheduledExcutor:创建一个单例线程池,定期或延时执行任务。 -// 6 newWorkStealingPool:创建持有足够线程的线程池来支持给定的并行级别,并通过使用多个队列,减少竞争,它需要穿一个并行级别的参数,如果不传,则被设定为默认的CPU数量。 -// 7 ForkJoinPool:支持大任务分解成小任务的线程池,这是Java8新增线程池,通常配合ForkJoinTask接口的子类RecursiveAction或RecursiveTask使用。 - } - - // == - - /** - * 获取线程数 - * @return - */ - private final int getThreads() { - // 使用计算过后的 - return getCaclThreads(); - } - - /** - * 获取线程数 - * @return - */ - private final int getCaclThreads() { - // 获取CPU核心数 - int cNumber = Runtime.getRuntime().availableProcessors(); - // 如果小于等于5,则返回5 - if (cNumber <= 5) { - return 5; - } else { // 大于5的情况 - if (cNumber * 2 + 1 >= 10) { // 防止线程数量过大,当大于10 的时候,返回 10 - return 10; - } else { // 不大于10的时候,默认返回 支持的数量 * 2 + 1 - return cNumber * 2 + 1; - } - } - } - - // == - - /** - * 加入到线程池任务队列 - * @param runnable - */ - public void execute(Runnable runnable) { - if (threadPool != null) { - threadPool.execute(runnable); - } - } - - /** - * 加入到线程池任务队列 - * @param runnables - */ - public void execute(final List runnables) { - if (threadPool == null || runnables == null){ - return; - } - for (Runnable command : runnables) { - if (command != null) { - threadPool.execute(command); - } - } - } - - - /** - * 通过反射,调用某个类的方法 - * @param method - * @param _class - */ - public void execute(final Method method, final Object _class) { - if (threadPool != null){ - threadPool.execute(new Runnable() { - @Override - public void run() { - try { - method.invoke(_class); - } catch (Exception ignore) { - } - } - }); - } - } - - // == - - /** shutdown 会等待所有提交的任务执行完成,不管是正在执行还是保存在任务队列中的已提交任务 - * 待以前提交的任务执行完毕后关闭线程池 - * 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 - * 如果已经关闭,则调用没有作用。 - */ - public void shutdown() { - if (threadPool != null){ - threadPool.shutdown(); - } - } - - /** - * shutdownNow会尝试中断正在执行的任务(其主要是中断一些指定方法如sleep方法),并且停止执行等待队列中提交的任务。 - * 试图停止所有正在执行的活动任务 - * 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。 - * 无法保证能够停止正在处理的活动执行任务,但是会尽力尝试。 - * @return - */ - public List shutdownNow() { - if (threadPool != null){ - return threadPool.shutdownNow(); - } - return null; - } - - /** - * 判断线程池是否已关闭 = isShutDown当调用shutdown()方法后返回为true。 - * @return - */ - public boolean isShutdown() { - if (threadPool != null){ - return threadPool.isShutdown(); - } - return false; - } - - /** - * 若关闭后所有任务都已完成,则返回true. - * 注意除非首先调用shutdown或shutdownNow, 否则isTerminated 永不为true. - * // -- - * isTerminated当调用shutdown()方法后,并且所有提交的任务完成后返回为true - * @return - */ - public boolean isTerminated() { - if (threadPool != null){ - return threadPool.isTerminated(); - } - return false; - } - - /** - * 请求关闭、发生超时或者当前线程中断 - * 无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行。 - * @param timeout 最长等待时间 - * @param unit 时间单位 - * @return true : 请求成功, false : 请求超时 - * @throws InterruptedException 终端异常 - */ - public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException { - return threadPool.awaitTermination(timeout, unit); - } - - /** - * 提交一个Callable任务用于执行 - * 如果想立即阻塞任务的等待,则可以使用{@code result = threadPool.submit(aCallable).get();}形式的构造。 - * @param task 任务 - * @param 泛型 - * @return 表示任务等待完成的Future, 该Future的{@code get}方法在成功完成时将会返回该任务的结果。 - */ - public Future submit(final Callable task) { - return threadPool.submit(task); - } - - /** - * 提交一个Runnable任务用于执行 - * @param task 任务 - * @param result 返回的结果 - * @param 泛型 - * @return 表示任务等待完成的Future, 该Future的{@code get}方法在成功完成时将会返回该任务的结果。 - */ - public Future submit(final Runnable task, final T result) { - return threadPool.submit(task, result); - } - - /** - * 提交一个Runnable任务用于执行 - * @param task 任务 - * @return 表示任务等待完成的Future, 该Future的{@code get}方法在成功完成时将会返回null结果。 - */ - public Future submit(final Runnable task) { - return threadPool.submit(task); - } - - /** - * 执行给定的任务 - * 当所有任务完成时,返回保持任务状态和结果的Future列表。 - * 返回列表的所有元素的{@link Future#isDone}为{@code true}。 - * 注意,可以正常地或通过抛出异常来终止已完成任务。 - * 如果正在进行此操作时修改了给定的 collection,则此方法的结果是不确定的。 - * @param tasks 任务集合 - * @param 泛型 - * @return 表示任务的 Future 列表,列表顺序与给定任务列表的迭代器所生成的顺序相同,每个任务都已完成。 - * @throws InterruptedException 如果等待时发生中断,在这种情况下取消尚未完成的任务。 - */ - public List> invokeAll(final Collection> tasks) throws InterruptedException { - return threadPool.invokeAll(tasks); - } - - /** - * 执行给定的任务 - * 当所有任务完成或超时期满时(无论哪个首先发生),返回保持任务状态和结果的Future列表。 - * 返回列表的所有元素的{@link Future#isDone}为{@code true}。 - * 一旦返回后,即取消尚未完成的任务。 - * 注意,可以正常地或通过抛出异常来终止已完成任务。 - * 如果此操作正在进行时修改了给定的 collection,则此方法的结果是不确定的。 - * @param tasks 任务集合 - * @param timeout 最长等待时间 - * @param unit 时间单位 - * @param 泛型 - * @return 表示任务的 Future 列表,列表顺序与给定任务列表的迭代器所生成的顺序相同。 - * 如果操作未超时,则已完成所有任务。如果确实超时了,则某些任务尚未完成。 - * @throws InterruptedException 如果等待时发生中断,在这种情况下取消尚未完成的任务 - */ - public List> invokeAll(final Collection> tasks, final long timeout, final TimeUnit unit) throws InterruptedException { - return threadPool.invokeAll(tasks, timeout, unit); - } - - /** - * 执行给定的任务 - * 如果某个任务已成功完成(也就是未抛出异常),则返回其结果。 - * 一旦正常或异常返回后,则取消尚未完成的任务。 - * 如果此操作正在进行时修改了给定的collection,则此方法的结果是不确定的。 - * @param tasks 任务集合 - * @param 泛型 - * @return 某个任务返回的结果 - * @throws InterruptedException 如果等待时发生中断 - * @throws ExecutionException 如果没有任务成功完成 - */ - public T invokeAny(final Collection> tasks) throws InterruptedException, ExecutionException { - return threadPool.invokeAny(tasks); - } - - /** - * 执行给定的任务 - * 如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果。 - * 一旦正常或异常返回后,则取消尚未完成的任务。 - * 如果此操作正在进行时修改了给定的collection,则此方法的结果是不确定的。 - * @param tasks 任务集合 - * @param timeout 最长等待时间 - * @param unit 时间单位 - * @param 泛型 - * @return 某个任务返回的结果 - * @throws InterruptedException 如果等待时发生中断 - * @throws ExecutionException 如果没有任务成功完成 - * @throws TimeoutException 如果在所有任务成功完成之前给定的超时期满 - */ - public T invokeAny(final Collection> tasks, final long timeout, final TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return threadPool.invokeAny(tasks, timeout, unit); - } - - // == - - - - /** - * 延迟执行Runnable命令 - * @param command 命令 - * @param delay 延迟时间 - * @param unit 单位 - * @return 表示挂起任务完成的ScheduledFuture,并且其{@code get()}方法在完成后将返回{@code null} - */ - public ScheduledFuture schedule(final Runnable command, final long delay, final TimeUnit unit) { - return scheduleExec.schedule(command, delay, unit); - } - - /** - * 延迟执行Callable命令 - * @param callable 命令 - * @param delay 延迟时间 - * @param unit 时间单位 - * @param 泛型 - * @return 可用于提取结果或取消的ScheduledFuture - */ - public ScheduledFuture schedule(final Callable callable, final long delay, final TimeUnit unit) { - return scheduleExec.schedule(callable, delay, unit); - } - - /** - * 延迟并循环执行命令 - * @param command 命令 - * @param initialDelay 首次执行的延迟时间 - * @param period 连续执行之间的周期 - * @param unit 时间单位 - * @return 表示挂起任务完成的ScheduledFuture,并且其{@code get()}方法在取消后将抛出异常 - */ - public ScheduledFuture scheduleWithFixedRate(final Runnable command, final long initialDelay, final long period, final TimeUnit unit) { - return scheduleExec.scheduleAtFixedRate(command, initialDelay, period, unit); - } - - /** - * 延迟并以固定休息时间循环执行命令 - * @param command 命令 - * @param initialDelay 首次执行的延迟时间 - * @param delay 每一次执行终止和下一次执行开始之间的延迟 - * @param unit 时间单位 - * @return 表示挂起任务完成的ScheduledFuture,并且其{@code get()}方法在取消后将抛出异常 - */ - public ScheduledFuture scheduleWithFixedDelay(final Runnable command, final long initialDelay, final long delay, final TimeUnit unit) { - return scheduleExec.scheduleWithFixedDelay(command, initialDelay, delay, unit); - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/validator/BankCheckUtils.java b/DevLibUtils/src/main/java/dev/utils/common/validator/BankCheckUtils.java deleted file mode 100644 index 792cc2d256..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/validator/BankCheckUtils.java +++ /dev/null @@ -1,718 +0,0 @@ -package dev.utils.common.validator; - -/** - * detail: 银行卡管理 - * @Description:主要功能:银行卡管理 - * @Prject: CommonUtilLibrary - * @Package: com.jingewenku.abrahamcaijin.commonutil - * @author: AbrahamCaiJin - * @date: 2017年05月15日 11:14 - * @Copyright: 个人版权所有 - * @Company: - * @version: 1.0.0 - */ -/* - 当你输入信用卡号码的时候,有没有担心输错了而造成损失呢?其实可以不必这么担心, - 因为并不是一个随便的信用卡号码都是合法的,它必须通过Luhn算法来验证通过。 - 该校验的过程: - 1、从卡号最后一位数字开始,逆向将奇数位(1、3、5等等)相加。 - 2、从卡号最后一位数字开始,逆向将偶数位数字,先乘以2(如果乘积为两位数,则将其减去9),再求和。 - 3、将奇数位总和加上偶数位总和,结果应该可以被10整除。 - 例如,卡号是:5432123456788881 - 则奇数、偶数位(用红色标出)分布:5432123456788881 - 奇数位和=35 - 偶数位乘以2(有些要减去9)的结果:1 6 2 6 1 5 7 7,求和=35。 - 最后35+35=70 可以被10整除,认定校验通过。 - */ -public final class BankCheckUtils { - /* - * 银行卡是由”发卡行标识代码 + 自定义 + 校验码 “等部分组成的 BIN号 银联标准卡与以往发行的银行卡最直接的区别就是其卡号前6位数字的不同。 - * 银行卡卡号的前6位是用来表示发卡银行或机构的,称为“发卡行识别码”(Bank Identification Number,缩写为“BIN”)。 - * 银联标准卡是由国内各家商业银行 - * (含邮储、信用社)共同发行、符合银联业务规范和技术标准、卡正面右下角带有“银联”标识(目前,新发行的银联标准卡一定带有国际化的银联新标识 - * ,新发的非银联标准卡使用旧的联网通用银联标识)、 卡号前6位为622126至622925之一的银行卡,是中国银行卡产业共有的民族品牌。 - */ - // BIN号 - private final static String[] BANKBIN = { "621098", "622150", "622151", - "622181", "622188", "955100", "621095", "620062", "621285", - "621798", "621799", "621797", "620529", "622199", "621096", - "621622", "623219", "621674", "623218", "621599", "370246", - "370248", "370249", "427010", "427018", "427019", "427020", - "427029", "427030", "427039", "370247", "438125", "438126", - "451804", "451810", "451811", "458071", "489734", "489735", - "489736", "510529", "427062", "524091", "427064", "530970", - "530990", "558360", "620200", "620302", "620402", "620403", - "620404", "524047", "620406", "620407", "525498", "620409", - "620410", "620411", "620412", "620502", "620503", "620405", - "620408", "620512", "620602", "620604", "620607", "620611", - "620612", "620704", "620706", "620707", "620708", "620709", - "620710", "620609", "620712", "620713", "620714", "620802", - "620711", "620904", "620905", "621001", "620902", "621103", - "621105", "621106", "621107", "621102", "621203", "621204", - "621205", "621206", "621207", "621208", "621209", "621210", - "621302", "621303", "621202", "621305", "621306", "621307", - "621309", "621311", "621313", "621211", "621315", "621304", - "621402", "621404", "621405", "621406", "621407", "621408", - "621409", "621410", "621502", "621317", "621511", "621602", - "621603", "621604", "621605", "621608", "621609", "621610", - "621611", "621612", "621613", "621614", "621615", "621616", - "621617", "621607", "621606", "621804", "621807", "621813", - "621814", "621817", "621901", "621904", "621905", "621906", - "621907", "621908", "621909", "621910", "621911", "621912", - "621913", "621915", "622002", "621903", "622004", "622005", - "622006", "622007", "622008", "622010", "622011", "622012", - "621914", "622015", "622016", "622003", "622018", "622019", - "622020", "622102", "622103", "622104", "622105", "622013", - "622111", "622114", "622200", "622017", "622202", "622203", - "622208", "622210", "622211", "622212", "622213", "622214", - "622110", "622220", "622223", "622225", "622229", "622230", - "622231", "622232", "622233", "622234", "622235", "622237", - "622215", "622239", "622240", "622245", "622224", "622303", - "622304", "622305", "622306", "622307", "622308", "622309", - "622238", "622314", "622315", "622317", "622302", "622402", - "622403", "622404", "622313", "622504", "622505", "622509", - "622513", "622517", "622502", "622604", "622605", "622606", - "622510", "622703", "622715", "622806", "622902", "622903", - "622706", "623002", "623006", "623008", "623011", "623012", - "622904", "623015", "623100", "623202", "623301", "623400", - "623500", "623602", "623803", "623901", "623014", "624100", - "624200", "624301", "624402", "62451804", "62451810", "62451811", - "62458071", "623700", "628288", "624000", "628286", "622206", - "621225", "526836", "513685", "543098", "458441", "620058", - "621281", "622246", "900000", "544210", "548943", "370267", - "621558", "621559", "621722", "621723", "620086", "621226", - "402791", "427028", "427038", "548259", "356879", "356880", - "356881", "356882", "528856", "621618", "620516", "621227", - "621721", "900010", "625330", "625331", "625332", "623062", - "622236", "621670", "524374", "550213", "374738", "374739", - "621288", "625708", "625709", "622597", "622599", "360883", - "360884", "625865", "625866", "625899", "621376", "620054", - "620142", "621428", "625939", "621434", "625987", "621761", - "621749", "620184", "621300", "621378", "625114", "622159", - "621720", "625021", "625022", "621379", "620114", "620146", - "621724", "625918", "621371", "620143", "620149", "621414", - "625914", "621375", "620187", "621433", "625986", "621370", - "625925", "622926", "622927", "622928", "622929", "622930", - "622931", "620124", "620183", "620561", "625116", "622227", - "621372", "621464", "625942", "622158", "625917", "621765", - "620094", "620186", "621719", "621719", "621750", "621377", - "620148", "620185", "621374", "621731", "621781", "552599", - "623206", "621671", "620059", "403361", "404117", "404118", - "404119", "404120", "404121", "463758", "514027", "519412", - "519413", "520082", "520083", "558730", "621282", "621336", - "621619", "622821", "622822", "622823", "622824", "622825", - "622826", "622827", "622828", "622836", "622837", "622840", - "622841", "622843", "622844", "622845", "622846", "622847", - "622848", "622849", "623018", "625996", "625997", "625998", - "628268", "625826", "625827", "548478", "544243", "622820", - "622830", "622838", "625336", "628269", "620501", "621660", - "621661", "621662", "621663", "621665", "621667", "621668", - "621669", "621666", "625908", "625910", "625909", "356833", - "356835", "409665", "409666", "409668", "409669", "409670", - "409671", "409672", "456351", "512315", "512316", "512411", - "512412", "514957", "409667", "518378", "518379", "518474", - "518475", "518476", "438088", "524865", "525745", "525746", - "547766", "552742", "553131", "558868", "514958", "622752", - "622753", "622755", "524864", "622757", "622758", "622759", - "622760", "622761", "622762", "622763", "601382", "622756", - "628388", "621256", "621212", "620514", "622754", "622764", - "518377", "622765", "622788", "621283", "620061", "621725", - "620040", "558869", "621330", "621331", "621332", "621333", - "621297", "377677", "621568", "621569", "625905", "625906", - "625907", "628313", "625333", "628312", "623208", "621620", - "621756", "621757", "621758", "621759", "621785", "621786", - "621787", "621788", "621789", "621790", "621672", "625337", - "625338", "625568", "621648", "621248", "621249", "622750", - "622751", "622771", "622772", "622770", "625145", "620531", - "620210", "620211", "622479", "622480", "622273", "622274", - "621231", "621638", "621334", "625140", "621395", "622725", - "622728", "621284", "421349", "434061", "434062", "436728", - "436742", "453242", "491031", "524094", "526410", "544033", - "552245", "589970", "620060", "621080", "621081", "621466", - "621467", "621488", "621499", "621598", "621621", "621700", - "622280", "622700", "622707", "622966", "622988", "625955", - "625956", "553242", "621082", "621673", "623211", "356896", - "356899", "356895", "436718", "436738", "436745", "436748", - "489592", "531693", "532450", "532458", "544887", "552801", - "557080", "558895", "559051", "622166", "622168", "622708", - "625964", "625965", "625966", "628266", "628366", "625362", - "625363", "628316", "628317", "620021", "620521", "405512", - "601428", "405512", "434910", "458123", "458124", "520169", - "522964", "552853", "601428", "622250", "622251", "521899", - "622254", "622255", "622256", "622257", "622258", "622259", - "622253", "622261", "622284", "622656", "628216", "622252", - "66405512", "622260", "66601428", "955590", "955591", "955592", - "955593", "628218", "622262", "621069", "620013", "625028", - "625029", "621436", "621002", "621335", "433670", "433680", - "442729", "442730", "620082", "622690", "622691", "622692", - "622696", "622698", "622998", "622999", "433671", "968807", - "968808", "968809", "621771", "621767", "621768", "621770", - "621772", "621773", "620527", "356837", "356838", "486497", - "622660", "622662", "622663", "622664", "622665", "622666", - "622667", "622669", "622670", "622671", "622672", "622668", - "622661", "622674", "622673", "620518", "621489", "621492", - "620535", "623156", "621490", "621491", "620085", "623155", - "623157", "623158", "623159", "999999", "621222", "623020", - "623021", "623022", "623023", "622630", "622631", "622632", - "622633", "622615", "622616", "622618", "622622", "622617", - "622619", "415599", "421393", "421865", "427570", "427571", - "472067", "472068", "622620", "621691", "545392", "545393", - "545431", "545447", "356859", "356857", "407405", "421869", - "421870", "421871", "512466", "356856", "528948", "552288", - "622600", "622601", "622602", "517636", "622621", "628258", - "556610", "622603", "464580", "464581", "523952", "545217", - "553161", "356858", "622623", "625911", "377152", "377153", - "377158", "377155", "625912", "625913", "356885", "356886", - "356887", "356888", "356890", "402658", "410062", "439188", - "439227", "468203", "479228", "479229", "512425", "521302", - "524011", "356889", "545620", "545621", "545947", "545948", - "552534", "552587", "622575", "622576", "622577", "622579", - "622580", "545619", "622581", "622582", "622588", "622598", - "622609", "690755", "690755", "545623", "621286", "620520", - "621483", "621485", "621486", "628290", "622578", "370285", - "370286", "370287", "370289", "439225", "518710", "518718", - "628362", "439226", "628262", "625802", "625803", "621299", - "966666", "622909", "622908", "438588", "438589", "461982", - "486493", "486494", "486861", "523036", "451289", "527414", - "528057", "622901", "622902", "622922", "628212", "451290", - "524070", "625084", "625085", "625086", "625087", "548738", - "549633", "552398", "625082", "625083", "625960", "625961", - "625962", "625963", "356851", "356852", "404738", "404739", - "456418", "498451", "515672", "356850", "517650", "525998", - "622177", "622277", "622516", "622517", "622518", "622520", - "622521", "622522", "622523", "628222", "628221", "984301", - "984303", "622176", "622276", "622228", "621352", "621351", - "621390", "621792", "625957", "625958", "621791", "620530", - "625993", "622519", "621793", "621795", "621796", "622500", - "623078", "622384", "940034", "940015", "622886", "622391", - "940072", "622359", "940066", "622857", "940065", "621019", - "622309", "621268", "622884", "621453", "622684", "621016", - "621015", "622950", "622951", "621072", "623183", "623185", - "621005", "622172", "622985", "622987", "622267", "622278", - "622279", "622468", "622892", "940021", "621050", "620522", - "356827", "356828", "356830", "402673", "402674", "438600", - "486466", "519498", "520131", "524031", "548838", "622148", - "622149", "622268", "356829", "622300", "628230", "622269", - "625099", "625953", "625350", "625351", "625352", "519961", - "625839", "421317", "602969", "621030", "621420", "621468", - "623111", "422160", "422161", "622865", "940012", "623131", - "622178", "622179", "628358", "622394", "940025", "621279", - "622281", "622316", "940022", "621418", "512431", "520194", - "621626", "623058", "602907", "622986", "622989", "622298", - "622338", "940032", "623205", "621977", "990027", "622325", - "623029", "623105", "621244", "623081", "623108", "566666", - "622455", "940039", "622466", "628285", "622420", "940041", - "623118", "603708", "622993", "623070", "623069", "623172", - "623173", "622383", "622385", "628299", "603506", "603367", - "622878", "623061", "623209", "628242", "622595", "622303", - "622305", "621259", "622596", "622333", "940050", "621439", - "623010", "621751", "628278", "625502", "625503", "625135", - "622476", "621754", "622143", "940001", "623026", "623086", - "628291", "621532", "621482", "622135", "622152", "622153", - "622154", "622996", "622997", "940027", "623099", "623007", - "940055", "622397", "622398", "940054", "622331", "622426", - "625995", "621452", "628205", "628214", "625529", "622428", - "621529", "622429", "621417", "623089", "623200", "940057", - "622311", "623119", "622877", "622879", "621775", "623203", - "603601", "622137", "622327", "622340", "622366", "622134", - "940018", "623016", "623096", "940049", "622425", "622425", - "621577", "622485", "623098", "628329", "621538", "940006", - "621269", "622275", "621216", "622465", "940031", "621252", - "622146", "940061", "621419", "623170", "622440", "940047", - "940017", "622418", "623077", "622413", "940002", "623188", - "622310", "940068", "622321", "625001", "622427", "940069", - "623039", "628273", "622370", "683970", "940074", "621437", - "628319", "990871", "622308", "621415", "623166", "622132", - "621340", "621341", "622140", "623073", "622147", "621633", - "622301", "623171", "621422", "622335", "622336", "622165", - "622315", "628295", "625950", "621760", "622337", "622411", - "623102", "622342", "623048", "622367", "622392", "623085", - "622395", "622441", "622448", "621413", "622856", "621037", - "621097", "621588", "623032", "622644", "623518", "622870", - "622866", "623072", "622897", "628279", "622864", "621403", - "622561", "622562", "622563", "622167", "622777", "621497", - "622868", "622899", "628255", "625988", "622566", "622567", - "622625", "622626", "625946", "628200", "621076", "504923", - "622173", "622422", "622447", "622131", "940076", "621579", - "622876", "622873", "622962", "622936", "623060", "622937", - "623101", "621460", "622939", "622960", "623523", "621591", - "622961", "628210", "622283", "625902", "621010", "622980", - "623135", "621726", "621088", "620517", "622740", "625036", - "621014", "621004", "622972", "623196", "621028", "623083", - "628250", "623121", "621070", "628253", "622979", "621035", - "621038", "621086", "621498", "621296", "621448", "622945", - "621755", "622940", "623120", "628355", "621089", "623161", - "628339", "621074", "621515", "623030", "621345", "621090", - "623178", "621091", "623168", "621057", "623199", "621075", - "623037", "628303", "621233", "621235", "621223", "621780", - "621221", "623138", "628389", "621239", "623068", "621271", - "628315", "621272", "621738", "621273", "623079", "621263", - "621325", "623084", "621327", "621753", "628331", "623160", - "621366", "621388", "621348", "621359", "621360", "621217", - "622959", "621270", "622396", "622511", "623076", "621391", - "621339", "621469", "621625", "623688", "623113", "621601", - "621655", "621636", "623182", "623087", "621696", "622955", - "622478", "940013", "621495", "621688", "623162", "622462", - "628272", "625101", "622323", "623071", "603694", "622128", - "622129", "623035", "623186", "621522", "622271", "940037", - "940038", "985262", "622322", "628381", "622481", "622341", - "940058", "623115", "621258", "621465", "621528", "622328", - "940062", "625288", "623038", "625888", "622332", "940063", - "623123", "622138", "621066", "621560", "621068", "620088", - "621067", "622531", "622329", "623103", "622339", "620500", - "621024", "622289", "622389", "628300", "625516", "621516", - "622859", "622869", "623075", "622895", "623125", "622947", - "621561", "623095", "621073", "623109", "621361", "623033", - "623207", "622891", "621363", "623189", "623510", "622995", - "621053", "621230", "621229", "622218", "628267", "621392", - "621481", "621310", "621396", "623251", "628351" }; - // "发卡行.卡种名称", - private static final String[] BANKNAME = { "邮储银行·绿卡通", "邮储银行·绿卡银联标准卡", - "邮储银行·绿卡银联标准卡", "邮储银行·绿卡专用卡", "邮储银行·绿卡银联标准卡", "邮储银行·绿卡(银联卡)", - "邮储银行·绿卡VIP卡", "邮储银行·银联标准卡", "邮储银行·中职学生资助卡", "邮政储蓄银行·IC绿卡通VIP卡", - "邮政储蓄银行·IC绿卡通", "邮政储蓄银行·IC联名卡", "邮政储蓄银行·IC预付费卡", "邮储银行·绿卡银联标准卡", - "邮储银行·绿卡通", "邮政储蓄银行·武警军人保障卡", "邮政储蓄银行·中国旅游卡(金卡)", - "邮政储蓄银行·普通高中学生资助卡", "邮政储蓄银行·中国旅游卡(普卡)", "邮政储蓄银行·福农卡", - "工商银行·牡丹运通卡金卡", "工商银行·牡丹运通卡金卡", "工商银行·牡丹运通卡金卡", - "工商银行·牡丹VISA卡(单位卡)", "工商银行·牡丹VISA信用卡", "工商银行·牡丹VISA卡(单位卡)", - "工商银行·牡丹VISA信用卡", "工商银行·牡丹VISA信用卡", "工商银行·牡丹VISA信用卡", - "工商银行·牡丹VISA信用卡", "工商银行·牡丹运通卡普通卡", "工商银行·牡丹VISA信用卡", - "工商银行·牡丹VISA白金卡", "工商银行·牡丹贷记卡(银联卡)", "工商银行·牡丹贷记卡(银联卡)", - "工商银行·牡丹贷记卡(银联卡)", "工商银行·牡丹贷记卡(银联卡)", "工商银行·牡丹欧元卡", "工商银行·牡丹欧元卡", - "工商银行·牡丹欧元卡", "工商银行·牡丹万事达国际借记卡", "工商银行·牡丹VISA信用卡", "工商银行·海航信用卡", - "工商银行·牡丹VISA信用卡", "工商银行·牡丹万事达信用卡", "工商银行·牡丹万事达信用卡", - "工商银行·牡丹万事达信用卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹万事达白金卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·海航信用卡个人普卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·灵通卡", "工商银行·牡丹灵通卡", "工商银行·E时代卡", "工商银行·E时代卡", - "工商银行·理财金卡", "工商银行·准贷记卡(个普)", "工商银行·准贷记卡(个普)", "工商银行·准贷记卡(个普)", - "工商银行·准贷记卡(个普)", "工商银行·准贷记卡(个普)", "工商银行·牡丹灵通卡", "工商银行·准贷记卡(商普)", - "工商银行·牡丹卡(商务卡)", "工商银行·准贷记卡(商金)", "工商银行·牡丹卡(商务卡)", "工商银行·贷记卡(个普)", - "工商银行·牡丹卡(个人卡)", "工商银行·牡丹卡(个人卡)", "工商银行·牡丹卡(个人卡)", "工商银行·牡丹卡(个人卡)", - "工商银行·贷记卡(个金)", "工商银行·牡丹交通卡", "工商银行·准贷记卡(个金)", "工商银行·牡丹交通卡", - "工商银行·贷记卡(商普)", "工商银行·贷记卡(商金)", "工商银行·牡丹卡(商务卡)", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹交通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", - "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹贷记卡", - "工商银行·牡丹贷记卡", "工商银行·牡丹贷记卡", "工商银行·牡丹贷记卡", "工商银行·牡丹灵通卡", - "工商银行·中央预算单位公务卡", "工商银行·牡丹灵通卡", "工商银行·财政预算单位公务卡", "工商银行·牡丹卡白金卡", - "工商银行·牡丹卡普卡", "工商银行·国航知音牡丹信用卡", "工商银行·国航知音牡丹信用卡", "工商银行·国航知音牡丹信用卡", - "工商银行·国航知音牡丹信用卡", "工商银行·银联标准卡", "工商银行·中职学生资助卡", "工商银行·专用信用消费卡", - "工商银行·牡丹社会保障卡", "中国工商银行·牡丹东航联名卡", "中国工商银行·牡丹东航联名卡", - "中国工商银行·牡丹运通白金卡", "中国工商银行·福农灵通卡", "中国工商银行·福农灵通卡", "工商银行·灵通卡", - "工商银行·灵通卡", "中国工商银行·中国旅行卡", "工商银行·牡丹卡普卡", "工商银行·国际借记卡", - "工商银行·国际借记卡", "工商银行·国际借记卡", "工商银行·国际借记卡", "中国工商银行·牡丹JCB信用卡", - "中国工商银行·牡丹JCB信用卡", "中国工商银行·牡丹JCB信用卡", "中国工商银行·牡丹JCB信用卡", - "中国工商银行·牡丹多币种卡", "中国工商银行·武警军人保障卡", "工商银行·预付芯片卡", "工商银行·理财金账户金卡", - "工商银行·灵通卡", "工商银行·牡丹宁波市民卡", "中国工商银行·中国旅游卡", "中国工商银行·中国旅游卡", - "中国工商银行·中国旅游卡", "中国工商银行·借记卡", "中国工商银行·借贷合一卡", "中国工商银行·普通高中学生资助卡", - "中国工商银行·牡丹多币种卡", "中国工商银行·牡丹多币种卡", "中国工商银行·牡丹百夫长信用卡", - "中国工商银行·牡丹百夫长信用卡", "工商银行·工银财富卡", "中国工商银行·中小商户采购卡", - "中国工商银行·中小商户采购卡", "中国工商银行·环球旅行金卡", "中国工商银行·环球旅行白金卡", - "中国工商银行·牡丹工银大来卡", "中国工商银行·牡丹工银大莱卡", "中国工商银行·IC金卡", "中国工商银行·IC白金卡", - "中国工商银行·工行IC卡(红卡)", "中国工商银行布鲁塞尔分行·借记卡", "中国工商银行布鲁塞尔分行·预付卡", - "中国工商银行布鲁塞尔分行·预付卡", "中国工商银行金边分行·借记卡", "中国工商银行金边分行·信用卡", - "中国工商银行金边分行·借记卡", "中国工商银行金边分行·信用卡", "中国工商银行加拿大分行·借记卡", - "中国工商银行加拿大分行·借记卡", "中国工商银行加拿大分行·预付卡", "中国工商银行巴黎分行·借记卡", - "中国工商银行巴黎分行·借记卡", "中国工商银行巴黎分行·贷记卡", "中国工商银行法兰克福分行·贷记卡", - "中国工商银行法兰克福分行·借记卡", "中国工商银行法兰克福分行·贷记卡", "中国工商银行法兰克福分行·贷记卡", - "中国工商银行法兰克福分行·借记卡", "中国工商银行法兰克福分行·预付卡", "中国工商银行法兰克福分行·预付卡", - "中国工商银行印尼分行·借记卡", "中国工商银行印尼分行·信用卡", "中国工商银行米兰分行·借记卡", - "中国工商银行米兰分行·预付卡", "中国工商银行米兰分行·预付卡", "中国工商银行阿拉木图子行·借记卡", - "中国工商银行阿拉木图子行·贷记卡", "中国工商银行阿拉木图子行·借记卡", "中国工商银行阿拉木图子行·预付卡", - "中国工商银行万象分行·借记卡", "中国工商银行万象分行·贷记卡", "中国工商银行卢森堡分行·借记卡", - "中国工商银行卢森堡分行·贷记卡", "中国工商银行澳门分行·E时代卡", "中国工商银行澳门分行·E时代卡", - "中国工商银行澳门分行·E时代卡", "中国工商银行澳门分行·理财金账户", "中国工商银行澳门分行·理财金账户", - "中国工商银行澳门分行·理财金账户", "中国工商银行澳门分行·预付卡", "中国工商银行澳门分行·预付卡", - "中国工商银行澳门分行·工银闪付预付卡", "中国工商银行澳门分行·工银银联公司卡", "中国工商银行澳门分行·Diamond", - "中国工商银行阿姆斯特丹·借记卡", "中国工商银行卡拉奇分行·借记卡", "中国工商银行卡拉奇分行·贷记卡", - "中国工商银行新加坡分行·贷记卡", "中国工商银行新加坡分行·贷记卡", "中国工商银行新加坡分行·借记卡", - "中国工商银行新加坡分行·预付卡", "中国工商银行新加坡分行·预付卡", "中国工商银行新加坡分行·借记卡", - "中国工商银行新加坡分行·借记卡", "中国工商银行马德里分行·借记卡", "中国工商银行马德里分行·借记卡", - "中国工商银行马德里分行·预付卡", "中国工商银行马德里分行·预付卡", "中国工商银行伦敦子行·借记卡", - "中国工商银行伦敦子行·工银伦敦借记卡", "中国工商银行伦敦子行·借记卡", "农业银行·金穗贷记卡", "农业银行·中国旅游卡", - "农业银行·普通高中学生资助卡", "农业银行·银联标准卡", "农业银行·金穗贷记卡(银联卡)", - "农业银行·金穗贷记卡(银联卡)", "农业银行·金穗贷记卡(银联卡)", "农业银行·金穗贷记卡(银联卡)", - "农业银行·金穗贷记卡(银联卡)", "农业银行·金穗贷记卡(银联卡)", "农业银行·VISA白金卡", - "农业银行·万事达白金卡", "农业银行·金穗贷记卡(银联卡)", "农业银行·金穗贷记卡(银联卡)", - "农业银行·金穗贷记卡(银联卡)", "农业银行·金穗贷记卡(银联卡)", "农业银行·金穗贷记卡", "农业银行·中职学生资助卡", - "农业银行·专用惠农卡", "农业银行·武警军人保障卡", "农业银行·金穗校园卡(银联卡)", "农业银行·金穗星座卡(银联卡)", - "农业银行·金穗社保卡(银联卡)", "农业银行·金穗旅游卡(银联卡)", "农业银行·金穗青年卡(银联卡)", - "农业银行·复合介质金穗通宝卡", "农业银行·金穗海通卡", "农业银行·退役金卡", "农业银行·金穗贷记卡", - "农业银行·金穗贷记卡", "农业银行·金穗通宝卡(银联卡)", "农业银行·金穗惠农卡", "农业银行·金穗通宝银卡", - "农业银行·金穗通宝卡(银联卡)", "农业银行·金穗通宝卡(银联卡)", "农业银行·金穗通宝卡", - "农业银行·金穗通宝卡(银联卡)", "农业银行·金穗通宝卡(银联卡)", "农业银行·金穗通宝钻石卡", "农业银行·掌尚钱包", - "农业银行·银联IC卡金卡", "农业银行·银联预算单位公务卡金卡", "农业银行·银联IC卡白金卡", "农业银行·金穗公务卡", - "中国农业银行贷记卡·IC普卡", "中国农业银行贷记卡·IC金卡", "中国农业银行贷记卡·澳元卡", - "中国农业银行贷记卡·欧元卡", "中国农业银行贷记卡·金穗通商卡", "中国农业银行贷记卡·金穗通商卡", - "中国农业银行贷记卡·银联白金卡", "中国农业银行贷记卡·中国旅游卡", "中国农业银行贷记卡·银联IC公务卡", - "宁波市农业银行·市民卡B卡", "中国银行·联名卡", "中国银行·个人普卡", "中国银行·个人金卡", "中国银行·员工普卡", - "中国银行·员工金卡", "中国银行·理财普卡", "中国银行·理财金卡", "中国银行·理财银卡", "中国银行·理财白金卡", - "中国银行·中行金融IC卡白金卡", "中国银行·中行金融IC卡普卡", "中国银行·中行金融IC卡金卡", - "中国银行·中银JCB卡金卡", "中国银行·中银JCB卡普卡", "中国银行·员工普卡", "中国银行·个人普卡", - "中国银行·中银威士信用卡员", "中国银行·中银威士信用卡员", "中国银行·个人白金卡", "中国银行·中银威士信用卡", - "中国银行·长城公务卡", "中国银行·长城电子借记卡", "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", - "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", "中国银行·中银威士信用卡员", - "中国银行·长城万事达信用卡", "中国银行·长城万事达信用卡", "中国银行·长城万事达信用卡", "中国银行·长城万事达信用卡", - "中国银行·长城万事达信用卡", "中国银行·中银奥运信用卡", "中国银行·长城信用卡", "中国银行·长城信用卡", - "中国银行·长城信用卡", "中国银行·长城万事达信用卡", "中国银行·长城公务卡", "中国银行·长城公务卡", - "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", "中国银行·长城人民币信用卡", "中国银行·长城人民币信用卡", - "中国银行·长城人民币信用卡", "中国银行·长城信用卡", "中国银行·长城人民币信用卡", "中国银行·长城人民币信用卡", - "中国银行·长城信用卡", "中国银行·银联单币贷记卡", "中国银行·长城信用卡", "中国银行·长城信用卡", - "中国银行·长城信用卡", "中国银行·长城电子借记卡", "中国银行·长城人民币信用卡", "中国银行·银联标准公务卡", - "中国银行·一卡双账户普卡", "中国银行·财互通卡", "中国银行·电子现金卡", "中国银行·长城人民币信用卡", - "中国银行·长城单位信用卡普卡", "中国银行·中银女性主题信用卡", "中国银行·长城单位信用卡金卡", "中国银行·白金卡", - "中国银行·中职学生资助卡", "中国银行·银联标准卡", "中国银行·金融IC卡", "中国银行·长城社会保障卡", - "中国银行·世界卡", "中国银行·社保联名卡", "中国银行·社保联名卡", "中国银行·医保联名卡", "中国银行·医保联名卡", - "中国银行·公司借记卡", "中国银行·银联美运顶级卡", "中国银行·长城福农借记卡金卡", "中国银行·长城福农借记卡普卡", - "中国银行·中行金融IC卡普卡", "中国银行·中行金融IC卡金卡", "中国银行·中行金融IC卡白金卡", - "中国银行·长城银联公务IC卡白金卡", "中国银行·中银旅游信用卡", "中国银行·长城银联公务IC卡金卡", - "中国银行·中国旅游卡", "中国银行·武警军人保障卡", "中国银行·社保联名借记IC卡", "中国银行·社保联名借记IC卡", - "中国银行·医保联名借记IC卡", "中国银行·医保联名借记IC卡", "中国银行·借记IC个人普卡", - "中国银行·借记IC个人金卡", "中国银行·借记IC个人普卡", "中国银行·借记IC白金卡", "中国银行·借记IC钻石卡", - "中国银行·借记IC联名卡", "中国银行·普通高中学生资助卡", "中国银行·长城环球通港澳台旅游金卡", - "中国银行·长城环球通港澳台旅游白金卡", "中国银行·中银福农信用卡", "中国银行金边分行·借记卡", - "中国银行雅加达分行·借记卡", "中国银行首尔分行·借记卡", "中国银行澳门分行·人民币信用卡", - "中国银行澳门分行·人民币信用卡", "中国银行澳门分行·中银卡", "中国银行澳门分行·中银卡", "中国银行澳门分行·中银卡", - "中国银行澳门分行·中银银联双币商务卡", "中国银行澳门分行·预付卡", "中国银行澳门分行·澳门中国银行银联预付卡", - "中国银行澳门分行·澳门中国银行银联预付卡", "中国银行澳门分行·熊猫卡", "中国银行澳门分行·财富卡", - "中国银行澳门分行·银联港币卡", "中国银行澳门分行·银联澳门币卡", "中国银行马尼拉分行·双币种借记卡", - "中国银行胡志明分行·借记卡", "中国银行曼谷分行·借记卡", "中国银行曼谷分行·长城信用卡环球通", - "中国银行曼谷分行·借记卡", "建设银行·龙卡准贷记卡", "建设银行·龙卡准贷记卡金卡", "建设银行·中职学生资助卡", - "建设银行·乐当家银卡VISA", "建设银行·乐当家金卡VISA", "建设银行·乐当家白金卡", - "建设银行·龙卡普通卡VISA", "建设银行·龙卡储蓄卡", "建设银行·VISA准贷记卡(银联卡)", - "建设银行·VISA准贷记金卡", "建设银行·乐当家", "建设银行·乐当家", "建设银行·准贷记金卡", - "建设银行·乐当家白金卡", "建设银行·金融复合IC卡", "建设银行·银联标准卡", "建设银行·银联理财钻石卡", - "建设银行·金融IC卡", "建设银行·理财白金卡", "建设银行·社保IC卡", "建设银行·财富卡私人银行卡", - "建设银行·理财金卡", "建设银行·福农卡", "建设银行·武警军人保障卡", "建设银行·龙卡通", "建设银行·银联储蓄卡", - "建设银行·龙卡储蓄卡(银联卡)", "建设银行·准贷记卡", "建设银行·理财白金卡", "建设银行·理财金卡", - "建设银行·准贷记卡普卡", "建设银行·准贷记卡金卡", "建设银行·龙卡信用卡", "建设银行·建行陆港通龙卡", - "中国建设银行·普通高中学生资助卡", "中国建设银行·中国旅游卡", "中国建设银行·龙卡JCB金卡", - "中国建设银行·龙卡JCB白金卡", "中国建设银行·龙卡JCB普卡", "中国建设银行·龙卡贷记卡公司卡", - "中国建设银行·龙卡贷记卡", "中国建设银行·龙卡国际普通卡VISA", "中国建设银行·龙卡国际金卡VISA", - "中国建设银行·VISA白金信用卡", "中国建设银行·龙卡国际白金卡", "中国建设银行·龙卡国际普通卡MASTER", - "中国建设银行·龙卡国际金卡MASTER", "中国建设银行·龙卡万事达金卡", "中国建设银行·龙卡贷记卡", - "中国建设银行·龙卡万事达白金卡", "中国建设银行·龙卡贷记卡", "中国建设银行·龙卡万事达信用卡", - "中国建设银行·龙卡人民币信用卡", "中国建设银行·龙卡人民币信用金卡", "中国建设银行·龙卡人民币白金卡", - "中国建设银行·龙卡IC信用卡普卡", "中国建设银行·龙卡IC信用卡金卡", "中国建设银行·龙卡IC信用卡白金卡", - "中国建设银行·龙卡银联公务卡普卡", "中国建设银行·龙卡银联公务卡金卡", "中国建设银行·中国旅游卡", - "中国建设银行·中国旅游卡", "中国建设银行·龙卡IC公务卡", "中国建设银行·龙卡IC公务卡", "交通银行·交行预付卡", - "交通银行·世博预付IC卡", "交通银行·太平洋互连卡", "交通银行·太平洋万事顺卡", "交通银行·太平洋互连卡(银联卡)", - "交通银行·太平洋白金信用卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋双币贷记卡", - "交通银行·太平洋白金信用卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋万事顺卡", "交通银行·太平洋人民币贷记卡", - "交通银行·太平洋人民币贷记卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋准贷记卡", "交通银行·太平洋准贷记卡", - "交通银行·太平洋准贷记卡", "交通银行·太平洋准贷记卡", "交通银行·太平洋借记卡", "交通银行·太平洋借记卡", - "交通银行·太平洋人民币贷记卡", "交通银行·太平洋借记卡", "交通银行·太平洋MORE卡", "交通银行·白金卡", - "交通银行·交通银行公务卡普卡", "交通银行·太平洋人民币贷记卡", "交通银行·太平洋互连卡", "交通银行·太平洋借记卡", - "交通银行·太平洋万事顺卡", "交通银行·太平洋贷记卡(银联卡)", "交通银行·太平洋贷记卡(银联卡)", - "交通银行·太平洋贷记卡(银联卡)", "交通银行·太平洋贷记卡(银联卡)", "交通银行·交通银行公务卡金卡", - "交通银行·交银IC卡", "交通银行香港分行·交通银行港币借记卡", "交通银行香港分行·港币礼物卡", - "交通银行香港分行·双币种信用卡", "交通银行香港分行·双币种信用卡", "交通银行香港分行·双币卡", - "交通银行香港分行·银联人民币卡", "交通银行澳门分行·银联借记卡", "中信银行·中信借记卡", "中信银行·中信借记卡", - "中信银行·中信国际借记卡", "中信银行·中信国际借记卡", "中信银行·中国旅行卡", "中信银行·中信借记卡(银联卡)", - "中信银行·中信借记卡(银联卡)", "中信银行·中信贵宾卡(银联卡)", "中信银行·中信理财宝金卡", - "中信银行·中信理财宝白金卡", "中信银行·中信钻石卡", "中信银行·中信钻石卡", "中信银行·中信借记卡", - "中信银行·中信理财宝(银联卡)", "中信银行·中信理财宝(银联卡)", "中信银行·中信理财宝(银联卡)", - "中信银行·借记卡", "中信银行·理财宝IC卡", "中信银行·理财宝IC卡", "中信银行·理财宝IC卡", - "中信银行·理财宝IC卡", "中信银行·理财宝IC卡", "中信银行·主账户复合电子现金卡", "光大银行·阳光商旅信用卡", - "光大银行·阳光商旅信用卡", "光大银行·阳光商旅信用卡", "光大银行·阳光卡(银联卡)", "光大银行·阳光卡(银联卡)", - "光大银行·阳光卡(银联卡)", "光大银行·阳光卡(银联卡)", "光大银行·阳光卡(银联卡)", "光大银行·阳光卡(银联卡)", - "光大银行·阳光卡(银联卡)", "光大银行·阳光卡(银联卡)", "光大银行·阳光卡(银联卡)", "光大银行·阳光卡(银联卡)", - "光大银行·阳光卡(银联卡)", "光大银行·阳光卡(银联卡)", "光大银行·阳光卡(银联卡)", "光大银行·阳光卡(银联卡)", - "光大银行·阳光卡(银联卡)", "光大银行·借记卡普卡", "光大银行·社会保障IC卡", "光大银行·IC借记卡普卡", - "光大银行·手机支付卡", "光大银行·联名IC卡普卡", "光大银行·借记IC卡白金卡", "光大银行·借记IC卡金卡", - "光大银行·阳光旅行卡", "光大银行·借记IC卡钻石卡", "光大银行·联名IC卡金卡", "光大银行·联名IC卡白金卡", - "光大银行·联名IC卡钻石卡", "华夏银行·华夏卡(银联卡)", "华夏银行·华夏白金卡", "华夏银行·华夏普卡", - "华夏银行·华夏金卡", "华夏银行·华夏白金卡", "华夏银行·华夏钻石卡", "华夏银行·华夏卡(银联卡)", - "华夏银行·华夏至尊金卡(银联卡)", "华夏银行·华夏丽人卡(银联卡)", "华夏银行·华夏万通卡", - "民生银行·民生借记卡(银联卡)", "民生银行·民生银联借记卡-金卡", "民生银行·钻石卡", - "民生银行·民生借记卡(银联卡)", "民生银行·民生借记卡(银联卡)", "民生银行·民生借记卡(银联卡)", - "民生银行·民生借记卡", "民生银行·民生国际卡", "民生银行·民生国际卡(银卡)", "民生银行·民生国际卡(欧元卡)", - "民生银行·民生国际卡(澳元卡)", "民生银行·民生国际卡", "民生银行·民生国际卡", "民生银行·薪资理财卡", - "民生银行·借记卡普卡", "民生银行·民生MasterCard", "民生银行·民生MasterCard", - "民生银行·民生MasterCard", "民生银行·民生MasterCard", "民生银行·民生JCB信用卡", - "民生银行·民生JCB金卡", "民生银行·民生贷记卡(银联卡)", "民生银行·民生贷记卡(银联卡)", - "民生银行·民生贷记卡(银联卡)", "民生银行·民生贷记卡(银联卡)", "民生银行·民生贷记卡(银联卡)", - "民生银行·民生JCB普卡", "民生银行·民生贷记卡(银联卡)", "民生银行·民生贷记卡(银联卡)", - "民生银行·民生信用卡(银联卡)", "民生银行·民生信用卡(银联卡)", "民生银行·民生银联白金信用卡", - "民生银行·民生贷记卡(银联卡)", "民生银行·民生银联个人白金卡", "民生银行·公务卡金卡", - "民生银行·民生贷记卡(银联卡)", "民生银行·民生银联商务信用卡", "民生银行·民VISA无限卡", - "民生银行·民生VISA商务白金卡", "民生银行·民生万事达钛金卡", "民生银行·民生万事达世界卡", - "民生银行·民生万事达白金公务卡", "民生银行·民生JCB白金卡", "民生银行·银联标准金卡", "民生银行·银联芯片普卡", - "民生银行·民生运通双币信用卡普卡", "民生银行·民生运通双币信用卡金卡", "民生银行·民生运通双币信用卡钻石卡", - "民生银行·民生运通双币标准信用卡白金卡", "民生银行·银联芯片金卡", "民生银行·银联芯片白金卡", - "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", - "招商银行·招商银行信用卡", "招商银行·两地一卡通", "招商银行·招行国际卡(银联卡)", "招商银行·招商银行信用卡", - "招商银行·VISA商务信用卡", "招商银行·招行国际卡(银联卡)", "招商银行·招商银行信用卡", - "招商银行·招商银行信用卡", "招商银行·招行国际卡(银联卡)", "招商银行·世纪金花联名信用卡", - "招商银行·招行国际卡(银联卡)", "招商银行·招商银行信用卡", "招商银行·万事达信用卡", "招商银行·万事达信用卡", - "招商银行·万事达信用卡", "招商银行·万事达信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", - "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", - "招商银行·一卡通(银联卡)", "招商银行·万事达信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", - "招商银行·一卡通(银联卡)", "招商银行·公司卡(银联卡)", "招商银行·金卡", "招商银行·招行一卡通", - "招商银行·招行一卡通", "招商银行·万事达信用卡", "招商银行·金葵花卡", "招商银行·电子现金卡", - "招商银行·银联IC普卡", "招商银行·银联IC金卡", "招商银行·银联金葵花IC卡", "招商银行·IC公务卡", - "招商银行·招商银行信用卡", "招商银行信用卡中心·美国运通绿卡", "招商银行信用卡中心·美国运通金卡", - "招商银行信用卡中心·美国运通商务绿卡", "招商银行信用卡中心·美国运通商务金卡", "招商银行信用卡中心·VISA信用卡", - "招商银行信用卡中心·MASTER信用卡", "招商银行信用卡中心·MASTER信用金卡", - "招商银行信用卡中心·银联标准公务卡(金卡)", "招商银行信用卡中心·VISA信用卡", - "招商银行信用卡中心·银联标准财政公务卡", "招商银行信用卡中心·芯片IC信用卡", "招商银行信用卡中心·芯片IC信用卡", - "招商银行香港分行·香港一卡通", "兴业银行·兴业卡(银联卡)", "兴业银行·兴业卡(银联标准卡)", - "兴业银行·兴业自然人生理财卡", "兴业银行·兴业智能卡(银联卡)", "兴业银行·兴业智能卡", - "兴业银行·visa标准双币个人普卡", "兴业银行·VISA商务普卡", "兴业银行·VISA商务金卡", - "兴业银行·VISA运动白金信用卡", "兴业银行·万事达信用卡(银联卡)", "兴业银行·VISA信用卡(银联卡)", - "兴业银行·加菲猫信用卡", "兴业银行·个人白金卡", "兴业银行·银联信用卡(银联卡)", "兴业银行·银联信用卡(银联卡)", - "兴业银行·银联白金信用卡", "兴业银行·银联标准公务卡", "兴业银行·VISA信用卡(银联卡)", - "兴业银行·万事达信用卡(银联卡)", "兴业银行·银联标准贷记普卡", "兴业银行·银联标准贷记金卡", - "兴业银行·银联标准贷记金卡", "兴业银行·银联标准贷记金卡", "兴业银行·兴业信用卡", "兴业银行·兴业信用卡", - "兴业银行·兴业信用卡", "兴业银行·银联标准贷记普卡", "兴业银行·银联标准贷记普卡", "兴业银行·兴业芯片普卡", - "兴业银行·兴业芯片金卡", "兴业银行·兴业芯片白金卡", "兴业银行·兴业芯片钻石卡", "浦东发展银行·浦发JCB金卡", - "浦东发展银行·浦发JCB白金卡", "浦东发展银行·信用卡VISA普通", "浦东发展银行·信用卡VISA金卡", - "浦东发展银行·浦发银行VISA年青卡", "浦东发展银行·VISA白金信用卡", "浦东发展银行·浦发万事达白金卡", - "浦东发展银行·浦发JCB普卡", "浦东发展银行·浦发万事达金卡", "浦东发展银行·浦发万事达普卡", - "浦东发展银行·浦发单币卡", "浦东发展银行·浦发银联单币麦兜普卡", "浦东发展银行·东方轻松理财卡", - "浦东发展银行·东方-轻松理财卡普卡", "浦东发展银行·东方轻松理财卡", "浦东发展银行·东方轻松理财智业金卡", - "浦东发展银行·东方卡(银联卡)", "浦东发展银行·东方卡(银联卡)", "浦东发展银行·东方卡(银联卡)", - "浦东发展银行·公务卡金卡", "浦东发展银行·公务卡普卡", "浦东发展银行·东方卡", "浦东发展银行·东方卡", - "浦东发展银行·浦发单币卡", "浦东发展银行·浦发联名信用卡", "浦东发展银行·浦发银联白金卡", - "浦东发展银行·轻松理财普卡", "浦东发展银行·移动联名卡", "浦东发展银行·轻松理财消贷易卡", - "浦东发展银行·轻松理财普卡(复合卡)", "浦东发展银行·贷记卡", "浦东发展银行·贷记卡", - "浦东发展银行·东方借记卡(复合卡)", "浦东发展银行·电子现金卡(IC卡)", "浦东发展银行·移动浦发联名卡", - "浦东发展银行·东方-标准准贷记卡", "浦东发展银行·轻松理财金卡(复合卡)", "浦东发展银行·轻松理财白金卡(复合卡)", - "浦东发展银行·轻松理财钻石卡(复合卡)", "浦东发展银行·东方卡", "恒丰银行·九州IC卡", - "恒丰银行·九州借记卡(银联卡)", "恒丰银行·九州借记卡(银联卡)", "天津市商业银行·银联卡(银联卡)", - "烟台商业银行·金通卡", "潍坊银行·鸢都卡(银联卡)", "潍坊银行·鸳都卡(银联卡)", "临沂商业银行·沂蒙卡(银联卡)", - "临沂商业银行·沂蒙卡(银联卡)", "日照市商业银行·黄海卡", "日照市商业银行·黄海卡(银联卡)", "浙商银行·商卡", - "浙商银行·商卡", "渤海银行·浩瀚金卡", "渤海银行·渤海银行借记卡", "渤海银行·金融IC卡", - "渤海银行·渤海银行公司借记卡", "星展银行·星展银行借记卡", "星展银行·星展银行借记卡", "恒生银行·恒生通财卡", - "恒生银行·恒生优越通财卡", "新韩银行·新韩卡", "上海银行·慧通钻石卡", "上海银行·慧通金卡", - "上海银行·私人银行卡", "上海银行·综合保险卡", "上海银行·申卡社保副卡(有折)", "上海银行·申卡社保副卡(无折)", - "上海银行·白金IC借记卡", "上海银行·慧通白金卡(配折)", "上海银行·慧通白金卡(不配折)", - "上海银行·申卡(银联卡)", "上海银行·申卡借记卡", "上海银行·银联申卡(银联卡)", "上海银行·单位借记卡", - "上海银行·首发纪念版IC卡", "上海银行·申卡贷记卡", "上海银行·申卡贷记卡", "上海银行·J分期付款信用卡", - "上海银行·申卡贷记卡", "上海银行·申卡贷记卡", "上海银行·上海申卡IC", "上海银行·申卡贷记卡", - "上海银行·申卡贷记卡普通卡", "上海银行·申卡贷记卡金卡", "上海银行·万事达白金卡", "上海银行·万事达星运卡", - "上海银行·申卡贷记卡金卡", "上海银行·申卡贷记卡普通卡", "上海银行·安融卡", "上海银行·分期付款信用卡", - "上海银行·信用卡", "上海银行·个人公务卡", "上海银行·安融卡", "上海银行·上海银行银联白金卡", - "上海银行·贷记IC卡", "上海银行·中国旅游卡(IC普卡)", "上海银行·中国旅游卡(IC金卡)", - "上海银行·中国旅游卡(IC白金卡)", "上海银行·万事达钻石卡", "上海银行·淘宝IC普卡", "北京银行·京卡借记卡", - "北京银行·京卡(银联卡)", "北京银行·京卡借记卡", "北京银行·京卡", "北京银行·京卡", "北京银行·借记IC卡", - "北京银行·京卡贵宾金卡", "北京银行·京卡贵宾白金卡", "吉林银行·君子兰一卡通(银联卡)", - "吉林银行·君子兰卡(银联卡)", "吉林银行·长白山金融IC卡", "吉林银行·信用卡", "吉林银行·信用卡", - "吉林银行·公务卡", "镇江市商业银行·金山灵通卡(银联卡)", "镇江市商业银行·金山灵通卡(银联卡)", - "宁波银行·银联标准卡", "宁波银行·汇通借记卡", "宁波银行·汇通卡(银联卡)", "宁波银行·明州卡", - "宁波银行·汇通借记卡", "宁波银行·汇通国际卡银联双币卡", "宁波银行·汇通国际卡银联双币卡", "平安银行·新磁条借记卡", - "平安银行·平安银行IC借记卡", "平安银行·万事顺卡", "平安银行·平安银行借记卡", "平安银行·平安银行借记卡", - "平安银行·万事顺借记卡", "焦作市商业银行·月季借记卡(银联卡)", "焦作市商业银行·月季城市通(银联卡)", - "焦作市商业银行·中国旅游卡", "温州银行·金鹿卡", "汉口银行·九通卡(银联卡)", "汉口银行·九通卡", - "汉口银行·借记卡", "汉口银行·借记卡", "盛京银行·玫瑰卡", "盛京银行·玫瑰IC卡", "盛京银行·玫瑰IC卡", - "盛京银行·玫瑰卡", "盛京银行·玫瑰卡", "盛京银行·玫瑰卡(银联卡)", "盛京银行·玫瑰卡(银联卡)", - "盛京银行·盛京银行公务卡", "洛阳银行·都市一卡通(银联卡)", "洛阳银行·都市一卡通(银联卡)", "洛阳银行·--", - "大连银行·北方明珠卡", "大连银行·人民币借记卡", "大连银行·金融IC借记卡", "大连银行·大连市社会保障卡", - "大连银行·借记IC卡", "大连银行·借记IC卡", "大连银行·大连市商业银行贷记卡", "大连银行·大连市商业银行贷记卡", - "大连银行·银联标准公务卡", "苏州市商业银行·姑苏卡", "杭州商业银行·西湖卡", "杭州商业银行·西湖卡", - "杭州商业银行·借记IC卡", "杭州商业银行·", "南京银行·梅花信用卡公务卡", "南京银行·梅花信用卡商务卡", - "南京银行·梅花贷记卡(银联卡)", "南京银行·梅花借记卡(银联卡)", "南京银行·白金卡", "南京银行·商务卡", - "东莞市商业银行·万顺通卡(银联卡)", "东莞市商业银行·万顺通卡(银联卡)", "东莞市商业银行·万顺通借记卡", - "东莞市商业银行·社会保障卡", "乌鲁木齐市商业银行·雪莲借记IC卡", "乌鲁木齐市商业银行·乌鲁木齐市公务卡", - "乌鲁木齐市商业银行·福农卡贷记卡", "乌鲁木齐市商业银行·福农卡准贷记卡", "乌鲁木齐市商业银行·雪莲准贷记卡", - "乌鲁木齐市商业银行·雪莲贷记卡(银联卡)", "乌鲁木齐市商业银行·雪莲借记IC卡", - "乌鲁木齐市商业银行·雪莲借记卡(银联卡)", "乌鲁木齐市商业银行·雪莲卡(银联卡)", "绍兴银行·兰花IC借记卡", - "绍兴银行·社保IC借记卡", "绍兴银行·兰花公务卡", "成都商业银行·芙蓉锦程福农卡", "成都商业银行·芙蓉锦程天府通卡", - "成都商业银行·锦程卡(银联卡)", "成都商业银行·锦程卡金卡", "成都商业银行·锦程卡定活一卡通金卡", - "成都商业银行·锦程卡定活一卡通", "成都商业银行·锦程力诚联名卡", "成都商业银行·锦程力诚联名卡", - "成都商业银行·锦程卡(银联卡)", "抚顺银行·借记IC卡", "临商银行·借记卡", "宜昌市商业银行·三峡卡(银联卡)", - "宜昌市商业银行·信用卡(银联卡)", "葫芦岛市商业银行·一通卡", "葫芦岛市商业银行·一卡通(银联卡)", - "天津市商业银行·津卡", "天津市商业银行·津卡贷记卡(银联卡)", "天津市商业银行·贷记IC卡", "天津市商业银行·--", - "天津银行·商务卡", "宁夏银行·宁夏银行公务卡", "宁夏银行·宁夏银行福农贷记卡", "宁夏银行·如意卡(银联卡)", - "宁夏银行·宁夏银行福农借记卡", "宁夏银行·如意借记卡", "宁夏银行·如意IC卡", "宁夏银行·宁夏银行如意借记卡", - "宁夏银行·中国旅游卡", "齐商银行·金达卡(银联卡)", "齐商银行·金达借记卡(银联卡)", "齐商银行·金达IC卡", - "徽商银行·黄山卡", "徽商银行·黄山卡", "徽商银行·借记卡", "徽商银行·徽商银行中国旅游卡(安徽)", - "徽商银行合肥分行·黄山卡", "徽商银行芜湖分行·黄山卡(银联卡)", "徽商银行马鞍山分行·黄山卡(银联卡)", - "徽商银行淮北分行·黄山卡(银联卡)", "徽商银行安庆分行·黄山卡(银联卡)", "重庆银行·长江卡(银联卡)", - "重庆银行·长江卡(银联卡)", "重庆银行·长江卡", "重庆银行·借记IC卡", "哈尔滨银行·丁香一卡通(银联卡)", - "哈尔滨银行·丁香借记卡(银联卡)", "哈尔滨银行·丁香卡", "哈尔滨银行·福农借记卡", - "无锡市商业银行·太湖金保卡(银联卡)", "丹东银行·借记IC卡", "丹东银行·丹东银行公务卡", "兰州银行·敦煌卡", - "南昌银行·金瑞卡(银联卡)", "南昌银行·南昌银行借记卡", "南昌银行·金瑞卡", "晋商银行·晋龙一卡通", - "晋商银行·晋龙一卡通", "晋商银行·晋龙卡(银联卡)", "青岛银行·金桥通卡", "青岛银行·金桥卡(银联卡)", - "青岛银行·金桥卡(银联卡)", "青岛银行·金桥卡", "青岛银行·借记IC卡", "吉林银行·雾凇卡(银联卡)", - "吉林银行·雾凇卡(银联卡)", "南通商业银行·金桥卡(银联卡)", "南通商业银行·金桥卡(银联卡)", - "日照银行·黄海卡、财富卡借记卡", "鞍山银行·千山卡(银联卡)", "鞍山银行·千山卡(银联卡)", "鞍山银行·千山卡", - "青海银行·三江银行卡(银联卡)", "青海银行·三江卡", "台州银行·大唐贷记卡", "台州银行·大唐准贷记卡", - "台州银行·大唐卡(银联卡)", "台州银行·大唐卡", "台州银行·借记卡", "台州银行·公务卡", - "泉州银行·海峡银联卡(银联卡)", "泉州银行·海峡储蓄卡", "泉州银行·海峡银联卡(银联卡)", "泉州银行·海峡卡", - "泉州银行·公务卡", "昆明商业银行·春城卡(银联卡)", "昆明商业银行·春城卡(银联卡)", - "昆明商业银行·富滇IC卡(复合卡)", "阜新银行·借记IC卡", "嘉兴银行·南湖借记卡(银联卡)", "廊坊银行·白金卡", - "廊坊银行·金卡", "廊坊银行·银星卡(银联卡)", "廊坊银行·龙凤呈祥卡", "内蒙古银行·百灵卡(银联卡)", - "内蒙古银行·成吉思汗卡", "湖州市商业银行·百合卡", "湖州市商业银行·", "沧州银行·狮城卡", - "南宁市商业银行·桂花卡(银联卡)", "包商银行·雄鹰卡(银联卡)", "包商银行·包头市商业银行借记卡", - "包商银行·雄鹰贷记卡", "包商银行·包商银行内蒙古自治区公务卡", "包商银行·贷记卡", "包商银行·借记卡", - "连云港市商业银行·金猴神通借记卡", "威海商业银行·通达卡(银联卡)", "威海市商业银行·通达借记IC卡", - "攀枝花市商业银行·攀枝花卡(银联卡)", "攀枝花市商业银行·攀枝花卡", "绵阳市商业银行·科技城卡(银联卡)", - "泸州市商业银行·酒城卡(银联卡)", "泸州市商业银行·酒城IC卡", "大同市商业银行·云冈卡(银联卡)", - "三门峡银行·天鹅卡(银联卡)", "广东南粤银行·南珠卡(银联卡)", "张家口市商业银行·好运IC借记卡", - "桂林市商业银行·漓江卡(银联卡)", "龙江银行·福农借记卡", "龙江银行·联名借记卡", "龙江银行·福农借记卡", - "龙江银行·龙江IC卡", "龙江银行·社会保障卡", "龙江银行·--", "江苏长江商业银行·长江卡", - "徐州市商业银行·彭城借记卡(银联卡)", "南充市商业银行·借记IC卡", "南充市商业银行·熊猫团团卡", - "莱商银行·银联标准卡", "莱芜银行·金凤卡", "莱商银行·借记IC卡", "德阳银行·锦程卡定活一卡通", - "德阳银行·锦程卡定活一卡通金卡", "德阳银行·锦程卡定活一卡通", "唐山市商业银行·唐山市城通卡", - "曲靖市商业银行·珠江源卡", "曲靖市商业银行·珠江源IC卡", "温州银行·金鹿信用卡", "温州银行·金鹿信用卡", - "温州银行·金鹿公务卡", "温州银行·贷记IC卡", "汉口银行·汉口银行贷记卡", "汉口银行·汉口银行贷记卡", - "汉口银行·九通香港旅游贷记普卡", "汉口银行·九通香港旅游贷记金卡", "汉口银行·贷记卡", "汉口银行·九通公务卡", - "江苏银行·聚宝借记卡", "江苏银行·月季卡", "江苏银行·紫金卡", "江苏银行·绿扬卡(银联卡)", - "江苏银行·月季卡(银联卡)", "江苏银行·九州借记卡(银联卡)", "江苏银行·月季卡(银联卡)", - "江苏银行·聚宝惠民福农卡", "江苏银行·江苏银行聚宝IC借记卡", "江苏银行·聚宝IC借记卡VIP卡", - "长治市商业银行·长治商行银联晋龙卡", "承德市商业银行·热河卡", "承德银行·借记IC卡", "德州银行·长河借记卡", - "德州银行·--", "遵义市商业银行·社保卡", "遵义市商业银行·尊卡", "邯郸市商业银行·邯银卡", - "邯郸市商业银行·邯郸银行贵宾IC借记卡", "安顺市商业银行·黄果树福农卡", "安顺市商业银行·黄果树借记卡", - "江苏银行·紫金信用卡(公务卡)", "江苏银行·紫金信用卡", "江苏银行·天翼联名信用卡", "平凉市商业银行·广成卡", - "玉溪市商业银行·红塔卡", "玉溪市商业银行·红塔卡", "浙江民泰商业银行·金融IC卡", "浙江民泰商业银行·民泰借记卡", - "浙江民泰商业银行·金融IC卡C卡", "浙江民泰商业银行·银联标准普卡金卡", "浙江民泰商业银行·商惠通", - "上饶市商业银行·三清山卡", "东营银行·胜利卡", "泰安市商业银行·岱宗卡", "泰安市商业银行·市民一卡通", - "浙江稠州商业银行·义卡", "浙江稠州商业银行·义卡借记IC卡", "浙江稠州商业银行·公务卡", "自贡市商业银行·借记IC卡", - "自贡市商业银行·锦程卡", "鄂尔多斯银行·天骄公务卡", "鹤壁银行·鹤卡", "许昌银行·连城卡", "铁岭银行·龙凤卡", - "乐山市商业银行·大福卡", "乐山市商业银行·--", "长安银行·长长卡", "长安银行·借记IC卡", - "重庆三峡银行·财富人生卡", "重庆三峡银行·借记卡", "石嘴山银行·麒麟借记卡", "石嘴山银行·麒麟借记卡", - "石嘴山银行·麒麟公务卡", "盘锦市商业银行·鹤卡", "盘锦市商业银行·盘锦市商业银行鹤卡", "平顶山银行·平顶山银行公务卡", - "朝阳银行·鑫鑫通卡", "朝阳银行·朝阳银行福农卡", "朝阳银行·红山卡", "宁波东海银行·绿叶卡", - "遂宁市商业银行·锦程卡", "遂宁是商业银行·金荷卡", "保定银行·直隶卡", "保定银行·直隶卡", - "凉山州商业银行·锦程卡", "凉山州商业银行·金凉山卡", "漯河银行·福卡", "漯河银行·福源卡", "漯河银行·福源公务卡", - "达州市商业银行·锦程卡", "新乡市商业银行·新卡", "晋中银行·九州方圆借记卡", "晋中银行·九州方圆卡", - "驻马店银行·驿站卡", "驻马店银行·驿站卡", "驻马店银行·公务卡", "衡水银行·金鼎卡", "衡水银行·借记IC卡", - "周口银行·如愿卡", "周口银行·公务卡", "阳泉市商业银行·金鼎卡", "阳泉市商业银行·金鼎卡", - "宜宾市商业银行·锦程卡", "宜宾市商业银行·借记IC卡", "库尔勒市商业银行·孔雀胡杨卡", "雅安市商业银行·锦城卡", - "雅安市商业银行·--", "安阳银行·安鼎卡", "信阳银行·信阳卡", "信阳银行·公务卡", "信阳银行·信阳卡", - "华融湘江银行·华融卡", "华融湘江银行·华融卡", "营口沿海银行·祥云借记卡", "景德镇商业银行·瓷都卡", - "哈密市商业银行·瓜香借记卡", "湖北银行·金牛卡", "湖北银行·汉江卡", "湖北银行·借记卡", "湖北银行·三峡卡", - "湖北银行·至尊卡", "湖北银行·金融IC卡", "西藏银行·借记IC卡", "新疆汇和银行·汇和卡", "广东华兴银行·借记卡", - "广东华兴银行·华兴银联公司卡", "广东华兴银行·华兴联名IC卡", "广东华兴银行·华兴金融IC借记卡", "濮阳银行·龙翔卡", - "宁波通商银行·借记卡", "甘肃银行·神舟兴陇借记卡", "甘肃银行·甘肃银行神州兴陇IC卡", "枣庄银行·借记IC卡", - "本溪市商业银行·借记卡", "盛京银行·医保卡", "上海农商银行·如意卡(银联卡)", "上海农商银行·如意卡(银联卡)", - "上海农商银行·鑫通卡", "上海农商银行·国际如意卡", "上海农商银行·借记IC卡", - "常熟市农村商业银行·粒金贷记卡(银联卡)", "常熟市农村商业银行·公务卡", "常熟市农村商业银行·粒金准贷卡", - "常熟农村商业银行·粒金借记卡(银联卡)", "常熟农村商业银行·粒金IC卡", "常熟农村商业银行·粒金卡", - "深圳农村商业银行·信通卡(银联卡)", "深圳农村商业银行·信通商务卡(银联卡)", "深圳农村商业银行·信通卡", - "深圳农村商业银行·信通商务卡", "广州农村商业银行·福农太阳卡", "广东南海农村商业银行·盛通卡", - "广东南海农村商业银行·盛通卡(银联卡)", "佛山顺德农村商业银行·恒通卡(银联卡)", "佛山顺德农村商业银行·恒通卡", - "佛山顺德农村商业银行·恒通卡(银联卡)", "江阴农村商业银行·暨阳公务卡", "江阴市农村商业银行·合作贷记卡(银联卡)", - "江阴农村商业银行·合作借记卡", "江阴农村商业银行·合作卡(银联卡)", "江阴农村商业银行·暨阳卡", - "重庆农村商业银行·江渝借记卡VIP卡", "重庆农村商业银行·江渝IC借记卡", "重庆农村商业银行·江渝乡情福农卡", - "东莞农村商业银行·信通卡(银联卡)", "东莞农村商业银行·信通卡(银联卡)", "东莞农村商业银行·信通信用卡", - "东莞农村商业银行·信通借记卡", "东莞农村商业银行·贷记IC卡", "张家港农村商业银行·一卡通(银联卡)", - "张家港农村商业银行·一卡通(银联卡)", "张家港农村商业银行·", "北京农村商业银行·信通卡", "北京农村商业银行·惠通卡", - "北京农村商业银行·凤凰福农卡", "北京农村商业银行·惠通卡", "北京农村商业银行·中国旅行卡", "北京农村商业银行·凤凰卡", - "天津农村商业银行·吉祥商联IC卡", "天津农村商业银行·信通借记卡(银联卡)", "天津农村商业银行·借记IC卡", - "鄞州农村合作银行·蜜蜂借记卡(银联卡)", "宁波鄞州农村合作银行·蜜蜂电子钱包(IC)", - "宁波鄞州农村合作银行·蜜蜂IC借记卡", "宁波鄞州农村合作银行·蜜蜂贷记IC卡", "宁波鄞州农村合作银行·蜜蜂贷记卡", - "宁波鄞州农村合作银行·公务卡", "成都农村商业银行·福农卡", "成都农村商业银行·福农卡", - "珠海农村商业银行·信通卡(银联卡)", "太仓农村商业银行·郑和卡(银联卡)", "太仓农村商业银行·郑和IC借记卡", - "无锡农村商业银行·金阿福", "无锡农村商业银行·借记IC卡", "黄河农村商业银行·黄河卡", - "黄河农村商业银行·黄河富农卡福农卡", "黄河农村商业银行·借记IC卡", "天津滨海农村商业银行·四海通卡", - "天津滨海农村商业银行·四海通e芯卡", "武汉农村商业银行·汉卡", "武汉农村商业银行·汉卡", - "武汉农村商业银行·中国旅游卡", "江南农村商业银行·阳湖卡(银联卡)", "江南农村商业银行·天天红火卡", - "江南农村商业银行·借记IC卡", "海口联合农村商业银行·海口联合农村商业银行合卡", "湖北嘉鱼吴江村镇银行·垂虹卡", - "福建建瓯石狮村镇银行·玉竹卡", "浙江平湖工银村镇银行·金平卡", "重庆璧山工银村镇银行·翡翠卡", - "重庆农村商业银行·银联标准贷记卡", "重庆农村商业银行·公务卡", "南阳村镇银行·玉都卡", - "晋中市榆次融信村镇银行·魏榆卡", "三水珠江村镇银行·珠江太阳卡", "东营莱商村镇银行·绿洲卡", "建设银行·单位结算卡", - "玉溪市商业银行·红塔卡" }; - - /** - * 校验银行卡卡号 是否合法 - * @param cardId - * @return - */ - public static boolean checkBankCard(String cardId) { - char bit = getBankCardCheckCode(cardId - .substring(0, cardId.length() - 1)); - if (bit == 'N') { - return false; - } - boolean isBankCard = cardId.charAt(cardId.length() - 1) == bit; - return isBankCard; - } - - /** - * 从不含校验位的银行卡卡号采用 Luhm 校验算法获得校验位 - * @param nonCheckCodeCardId - * @return - */ - public static char getBankCardCheckCode(String nonCheckCodeCardId) { - if (nonCheckCodeCardId == null - || nonCheckCodeCardId.trim().length() == 0 - || !nonCheckCodeCardId.matches("\\d+")) { - // 如果传的不是数据返回N - return 'N'; - } - char[] chs = nonCheckCodeCardId.trim().toCharArray(); - int luhmSum = 0; - for (int i = chs.length - 1, j = 0; i >= 0; i--, j++) { - int k = chs[i] - '0'; - if (j % 2 == 0) { - k *= 2; - k = k / 10 + k % 10; - } - luhmSum += k; - } - return (luhmSum % 10 == 0) ? '0' : (char) ((10 - luhmSum % 10) + '0'); - } - - /** - * 通过银行卡 的前六位确定 判断银行开户行及卡种 - * @param cardbin - * @return - */ - public static String getNameOfBank(String cardbin) { - // 通过银行卡的前6位确定 - cardbin = cardbin.substring(0, 6); - int index = -1; - for (int i = 0; i < BANKBIN.length; i++) { - if (cardbin.equals(BANKBIN[i])) { - index = i; - } - } - if (index == -1) { - return ""; - } - return BANKNAME[index]; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/validator/IDCardUtils.java b/DevLibUtils/src/main/java/dev/utils/common/validator/IDCardUtils.java deleted file mode 100644 index f4caf0fcdb..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/validator/IDCardUtils.java +++ /dev/null @@ -1,595 +0,0 @@ -package dev.utils.common.validator; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import dev.utils.JCLogUtils; - -/** - * detail: 居民身份证工具类 - * @Description:主要功能: 居民身份证工具类 - * @Prject: CommonUtilLibrary - * @Package: com.jingewenku.abrahamcaijin.commonutil - * @author: AbrahamCaiJin - * @date: 2017年07月21日 15:08 - * @Copyright: 个人版权所有 - * @Company: - * @version: 1.0.0 - */ - -public final class IDCardUtils { - - // 日志TAG - private static final String TAG = IDCardUtils.class.getSimpleName(); - - public static final int power[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; - private static final int CHINA_ID_MAX_LENGTH = 18; - public static final int CHINA_ID_MIN_LENGTH = 15; - public static Map cityCodes = new HashMap(); - public static Map twFirstCode = new HashMap(); - //台湾身份首字母对应数字 - public static Map hkFirstCode = new HashMap(); - - //香港身份首字母对应数字 - static { - cityCodes.put("11", "北京"); - cityCodes.put("12", "天津"); - cityCodes.put("13", "河北"); - cityCodes.put("14", "山西"); - cityCodes.put("15", "内蒙古"); - cityCodes.put("21", "辽宁"); - cityCodes.put("22", "吉林"); - cityCodes.put("23", "黑龙江"); - cityCodes.put("31", "上海"); - cityCodes.put("32", "江苏"); - cityCodes.put("33", "浙江"); - cityCodes.put("34", "安徽"); - cityCodes.put("35", "福建"); - cityCodes.put("36", "江西"); - cityCodes.put("37", "山东"); - cityCodes.put("41", "河南"); - cityCodes.put("42", "湖北"); - cityCodes.put("43", "湖南"); - cityCodes.put("44", "广东"); - cityCodes.put("45", "广西"); - cityCodes.put("46", "海南"); - cityCodes.put("50", "重庆"); - cityCodes.put("51", "四川"); - cityCodes.put("52", "贵州"); - cityCodes.put("53", "云南"); - cityCodes.put("54", "西藏"); - cityCodes.put("61", "陕西"); - cityCodes.put("62", "甘肃"); - cityCodes.put("63", "青海"); - cityCodes.put("64", "宁夏"); - cityCodes.put("65", "新疆"); - cityCodes.put("71", "台湾"); - cityCodes.put("81", "香港"); - cityCodes.put("82", "澳门"); - cityCodes.put("91", "国外"); - twFirstCode.put("A", 10); - twFirstCode.put("B", 11); - twFirstCode.put("C", 12); - twFirstCode.put("D", 13); - twFirstCode.put("E", 14); - twFirstCode.put("F", 15); - twFirstCode.put("G", 16); - twFirstCode.put("H", 17); - twFirstCode.put("J", 18); - twFirstCode.put("K", 19); - twFirstCode.put("L", 20); - twFirstCode.put("M", 21); - twFirstCode.put("N", 22); - twFirstCode.put("P", 23); - twFirstCode.put("Q", 24); - twFirstCode.put("R", 25); - twFirstCode.put("S", 26); - twFirstCode.put("T", 27); - twFirstCode.put("U", 28); - twFirstCode.put("V", 29); - twFirstCode.put("X", 30); - twFirstCode.put("Y", 31); - twFirstCode.put("W", 32); - twFirstCode.put("Z", 33); - twFirstCode.put("I", 34); - twFirstCode.put("O", 35); - hkFirstCode.put("A", 1); - hkFirstCode.put("B", 2); - hkFirstCode.put("C", 3); - hkFirstCode.put("R", 18); - hkFirstCode.put("U", 21); - hkFirstCode.put("Z", 26); - hkFirstCode.put("X", 24); - hkFirstCode.put("W", 23); - hkFirstCode.put("O", 15); - hkFirstCode.put("N", 14); - } - - /** - * 将身份证的每位和对应位的加权因子相乘之后,再获取和值 - * - * @param iArr int[] - * @return 身份证编码 - */ - public static int getPowerSum(int[] iArr) { - int iSum = 0; - if (power.length == iArr.length) { - for (int i = 0; i < iArr.length; i++) { - for (int j = 0; j < power.length; j++) { - if (i == j) { - iSum = iSum + iArr[i] * power[j]; - } - } - } - } - return iSum; - } - - /** - * 将power和值与1 1取模获得余数进行校验码判断 - * - * @param iSum sum - * @return 校验位 - */ - public static String getCheckCode18(int iSum) { - String sCode = ""; - switch (iSum % 11) { - case 10: - sCode = "2"; - break; - case 9: - sCode = "3"; - break; - case 8: - sCode = "4"; - break; - case 7: - sCode = "5"; - break; - case 6: - sCode = "6"; - break; - case 5: - sCode = "7"; - break; - case 4: - sCode = "8"; - break; - case 3: - sCode = "9"; - break; - case 2: - sCode = "x"; - break; - case 1: - sCode = "0"; - break; - case 0: - sCode = "1"; - break; - } - return sCode; - } - - /** - * 将字符数组转换成数字数组 - * - * @param ca 字符数组 - * @return 数字数组 - */ - public static int[] converCharToInt(char[] ca) { - int len = ca.length; - int[] iArr = new int[len]; - try { - for (int i = 0; i < len; i++) { - iArr[i] = Integer.parseInt(String.valueOf(ca[i])); - } - } catch (NumberFormatException e) { - JCLogUtils.eTag(TAG, e, "converCharToInt"); - } - return iArr; - } - - /** - * 数字验证 - * - * @param val 待验证的字符串 - * @return 是否是数字 - */ - private static boolean isNum(String val) { - return !(val == null || "".equals(val)) && val.matches("^[0-9]*$"); - } - - /** - * 身份证校验规则,验证18位身份编码是否合法 - * - * @param idCard 待验证的字符串 - * @return 校验结果 - */ - public static boolean validateIdCard18(String idCard) { - - boolean bTrue = false; - if (idCard == null) { - return false; - } - if (idCard.length() == CHINA_ID_MAX_LENGTH) { - // 前17位 - String code17 = idCard.substring(0, 17); - // 第18位 - String code18 = idCard.substring(17, CHINA_ID_MAX_LENGTH); - if (isNum(code17)) { - char[] cArr = code17.toCharArray(); - if (cArr != null) { - int[] iCard = converCharToInt(cArr); - int iSum17 = getPowerSum(iCard); - // 获取校验位 - String val = getCheckCode18(iSum17); - if (val.length() > 0) { - if (val.equalsIgnoreCase(code18)) { - bTrue = true; - } - } - } - } - } - return bTrue; - } - - /** - * 身份证校验规则,验证15位身份编码是否合法 - * - * @param idCard 待验证的字符串 - * @return 校验结果 - */ - public static boolean validateIdCard15(String idCard) { - if (idCard.length() != CHINA_ID_MIN_LENGTH) { - return false; - } - if (isNum(idCard)) { - String proCode = idCard.substring(0, 2); - if (cityCodes.get(proCode) == null) { - return false; - } - String birthCode = idCard.substring(6, 12); - Date birthDate = null; - try { - birthDate = new SimpleDateFormat("yy").parse(birthCode.substring(0, 2)); - } catch (ParseException e) { - JCLogUtils.eTag(TAG, e, "validateIdCard15"); - } - Calendar cal = Calendar.getInstance(); - if (birthDate != null) cal.setTime(birthDate); - if (!validateDateSmllerThenNow(cal.get(Calendar.YEAR), Integer.valueOf(birthCode.substring(2, 4)), Integer.valueOf(birthCode.substring(4, 6)))) { - return false; - } - } else { - return false; - } - return true; - } - - /** - * 验证小于当前日期 是否有效 - * - * @param iYear 待验证日期(年) - * @param iMonth 待验证日期(月 1-12) - * @param iDate 待验证日期(日) - * @return 是否有效 - */ - private static boolean validateDateSmllerThenNow(int iYear, int iMonth, int iDate) { - Calendar cal = Calendar.getInstance(); - int year = cal.get(Calendar.YEAR); - int datePerMonth; - int MIN = 1930; - if (iYear < MIN || iYear >= year) { - return false; - } - if (iMonth < 1 || iMonth > 12) { - return false; - } - switch (iMonth) { - case 4: - case 6: - case 9: - case 11: - datePerMonth = 30; - break; - case 2: - boolean dm = ((iYear % 4 == 0 && iYear % 100 != 0) || (iYear % 400 == 0)) && (iYear > MIN && iYear < year); - datePerMonth = dm ? 29 : 28; - break; - default: - datePerMonth = 31; - } - return (iDate >= 1) && (iDate <= datePerMonth); - } - - /** - * 将15位身份证号码转换为18位 - * - * @param idCard 15位身份编码 - * @return 18位身份编码 - */ - public static String convert15CardTo18(String idCard) { - String idCard18 = ""; - if (idCard.length() != CHINA_ID_MIN_LENGTH) { - return null; - } - if (isNum(idCard)) { - // 获取出生年月日 - String birthday = idCard.substring(6, 12); - Date birthDate = null; - try { - birthDate = new SimpleDateFormat("yyMMdd").parse(birthday); - } catch (ParseException e) { - JCLogUtils.eTag(TAG, e, "convert15CardTo18"); - } - Calendar cal = Calendar.getInstance(); - if (birthDate != null) cal.setTime(birthDate); - // 获取出生年(完全表现形式,如:2010) - String sYear = String.valueOf(cal.get(Calendar.YEAR)); - idCard18 = idCard.substring(0, 6) + sYear + idCard.substring(8); - // 转换字符数组 - char[] cArr = idCard18.toCharArray(); - if (cArr != null) { - int[] iCard = converCharToInt(cArr); - int iSum17 = getPowerSum(iCard); - // 获取校验位 - String sVal = getCheckCode18(iSum17); - if (sVal.length() > 0) { - idCard18 += sVal; - } else { - return null; - } - } - } else { - return null; - } - return idCard18; - } - - /** - * 验证台湾身份证号码 - * - * @param idCard 身份证号码 - * @return 是否符合 - */ - public static boolean validateTWCard(String idCard) { - String start = idCard.substring(0, 1); - String mid = idCard.substring(1, 9); - String end = idCard.substring(9, 10); - Integer iStart = twFirstCode.get(start); - Integer sum = iStart / 10 + (iStart % 10) * 9; - char[] chars = mid.toCharArray(); - Integer iflag = 8; - for (char c : chars) { - sum = sum + Integer.valueOf(c + "") * iflag; - iflag--; - } - return (sum % 10 == 0 ? 0 : 10 - sum % 10) == Integer.valueOf(end); - } - - /** - * 验证香港身份证号码(存在Bug,部份特殊身份证无法检查) - * 身份证前2位为英文字符,如果只出现一个英文字符则表示第一位是空格,对应数字58 前2位英文字符A-Z分别对应数字10-35 - * 最后一位校验码为0-9的数字加上字符"A","A"代表10 - * 将身份证号码全部转换为数字,分别对应乘9-1相加的总和,整除11则证件号码有效 - * - * @param idCard 身份证号码 - * @return 验证码是否符合 - */ - public static boolean validateHKCard(String idCard) { - String card = idCard.replaceAll("[\\(|\\)]", ""); - Integer sum = 0; - if (card.length() == 9) { - sum = ((int) card.substring(0, 1).toUpperCase().toCharArray()[0] - 55) * 9 + ((int) card.substring(1, 2).toUpperCase().toCharArray()[0] - 55) * 8; - card = card.substring(1, 9); - } else { - sum = 522 + ((int) card.substring(0, 1).toUpperCase().toCharArray()[0] - 55) * 8; - } - String mid = card.substring(1, 7); - String end = card.substring(7, 8); - char[] chars = mid.toCharArray(); - Integer iflag = 7; - for (char c : chars) { - sum = sum + Integer.valueOf(c + "") * iflag; - iflag--; - } - if (end.toUpperCase().equals("A")) { - sum = sum + 10; - } else { - sum = sum + Integer.valueOf(end); - } - return (sum % 11 == 0); - } - - public static String[] validateIdCard10(String idCard) { - String[] info = new String[3]; - String card = idCard.replaceAll("[\\(|\\)]", ""); - if (card.length() != 8 && card.length() != 9 && idCard.length() != 10) { - return null; - } - if (idCard.matches("^[a-zA-Z][0-9]{9}$")) { // 台湾 - info[0] = "台湾"; - String char2 = idCard.substring(1, 2); - if (char2.equals("1")) { - info[1] = "M"; - } else if (char2.equals("2")) { - info[1] = "F"; - } else { - info[1] = "N"; - info[2] = "false"; - return info; - } - info[2] = validateTWCard(idCard) ? "true" : "false"; - } else if (idCard.matches("^[1|5|7][0-9]{6}\\(?[0-9A-Z]\\)?$")) { // 澳门 - info[0] = "澳门"; - info[1] = "N"; - // TODO - } else if (idCard.matches("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?$")) { // 香港 - info[0] = "香港"; - info[1] = "N"; - info[2] = validateHKCard(idCard) ? "true" : "false"; - } else { - return null; - } - return info; - } - - /** - * 验证身份证是否合法 - * - * @param idCard 待验证的字符串 - * @return 身份证是否合法 - */ - public static boolean validateCard(String idCard) { - String card = idCard.trim(); - if (validateIdCard18(card)) { - return true; - } - if (validateIdCard15(card)) { - return true; - } - String[] cardval = validateIdCard10(card); - if (cardval != null) { - if (cardval[2].equals("true")) { - return true; - } - } - return false; - } - - /** - * 根据身份编号获取年龄 - * - * @param idCard 身份编号 - * @return 年龄 - */ - public static int getAgeByIdCard(String idCard) { - int iAge = 0; - if (idCard.length() == CHINA_ID_MIN_LENGTH) { - idCard = convert15CardTo18(idCard); - } - String year = idCard.substring(6, 10); - Calendar cal = Calendar.getInstance(); - int iCurrYear = cal.get(Calendar.YEAR); - iAge = iCurrYear - Integer.valueOf(year); - return iAge; - } - - /** - * 根据身份编号获取生日 - * - * @param idCard 身份编号 - * @return 生日(yyyyMMdd) - */ - public static String getBirthByIdCard(String idCard) { - Integer len = idCard.length(); - if (len < CHINA_ID_MIN_LENGTH) { - return null; - } else if (len == CHINA_ID_MIN_LENGTH) { - idCard = convert15CardTo18(idCard); - } - return idCard.substring(6, 14); - } - - /** - * 根据身份编号获取生日 - * - * @param idCard 身份编号 - * @return 生日(yyyyMMdd) - */ - public static String getBirthdayByIdCard(String idCard) { - return getBirthByIdCard(idCard).replaceAll("(\\d{4})(\\d{2})(\\d{2})", "$1-$2-$3"); - } - - /** - * 根据身份编号获取生日年 - * - * @param idCard 身份编号 - * @return 生日(yyyy) - */ - public static Short getYearByIdCard(String idCard) { - Integer len = idCard.length(); - if (len < CHINA_ID_MIN_LENGTH) { - return null; - } else if (len == CHINA_ID_MIN_LENGTH) { - idCard = convert15CardTo18(idCard); - } - return Short.valueOf(idCard.substring(6, 10)); - } - - /** - * 根据身份编号获取生日月 - * - * @param idCard 身份编号 - * @return 生日(MM) - */ - public static Short getMonthByIdCard(String idCard) { - Integer len = idCard.length(); - if (len < CHINA_ID_MIN_LENGTH) { - return null; - } else if (len == CHINA_ID_MIN_LENGTH) { - idCard = convert15CardTo18(idCard); - } - return Short.valueOf(idCard.substring(10, 12)); - } - - /** - * 根据身份编号获取生日天 - * - * @param idCard 身份编号 - * @return 生日(dd) - */ - public static Short getDateByIdCard(String idCard) { - Integer len = idCard.length(); - if (len < CHINA_ID_MIN_LENGTH) { - return null; - } else if (len == CHINA_ID_MIN_LENGTH) { - idCard = convert15CardTo18(idCard); - } - return Short.valueOf(idCard.substring(12, 14)); - } - - /** - * 根据身份编号获取性别 - * - * @param idCard 身份编号 - * @return 性别(M-男,F-女,N-未知) - */ - public static String getGenderByIdCard(String idCard) { - String sGender = "N"; - if (idCard.length() == CHINA_ID_MIN_LENGTH) { - idCard = convert15CardTo18(idCard); - } - String sCardNum = idCard.substring(16, 17); - if (Integer.parseInt(sCardNum) % 2 != 0) { - sGender = "M"; - } else { - sGender = "F"; - } - return sGender; - } - - /** - * 根据身份编号获取户籍省份 - * - * @param idCard 身份编码 - * @return 省级编码 - */ - public static String getProvinceByIdCard(String idCard) { - int len = idCard.length(); - String sProvince = null; - String sProvinNum = ""; - if (len == CHINA_ID_MIN_LENGTH || len == CHINA_ID_MAX_LENGTH) { - sProvinNum = idCard.substring(0, 2); - } - sProvince = cityCodes.get(sProvinNum); - return sProvince; - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToIDCardUtils.java b/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToIDCardUtils.java deleted file mode 100644 index 3711066452..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToIDCardUtils.java +++ /dev/null @@ -1,97 +0,0 @@ -package dev.utils.common.validator; - -import java.util.regex.Pattern; - -/** - * detail: 检验身份证工具类 - * Created by Ttt - */ -public final class ValiToIDCardUtils { - - private ValiToIDCardUtils() { - } - - /** 正则表达式:验证身份证 */ - static final String REGEX_ID_CARD = "(^\\d{15}$)|(^\\d{17}([0-9]|X)$)"; - - /** 正则表达式:验证台湾 */ - static final String REGEX_TW_ID_CARD = "/^[a-zA-Z][0-9]{9}$/"; - - /** 正则表达式:验证香港 */ - static final String REGEX_XG_ID_CARD = "[A-Z][0-9]{6}\\([0-9A]\\)"; - - /** 正则表达式:验证澳门 */ - static final String REGEX_AM_ID_CARD = "[157][0-9]{6}\\([0-9]\\)"; - - // ==== 内部方法 ===== - - /** - * 判断是否为null - * @param str - * @return - */ - public static boolean isEmpty(String str) { - return (str == null || str.length() == 0); - } - - /** - * 通用匹配函数 - * @param regex - * @param input - * @return - */ - static boolean match(String regex, String input) { - return Pattern.matches(regex, input); - } - - // == - - /** - * 校验身份证 - * @param idCard - * @return - */ - public static boolean isIDCard(String idCard) { - if (!isEmpty(idCard)){ - return match(REGEX_ID_CARD, idCard); - } - return false; - } - - /** - * 校验身份证 -> 香港 - * @param idCard - * @return - */ - public static boolean isHKIDCard(String idCard){ - if (!isEmpty(idCard)){ - return match(REGEX_XG_ID_CARD, idCard); - } - return false; - } - - /** - * 校验身份证 -> 澳门 - * @param idCard - * @return - */ - public static boolean isAMIDCard(String idCard){ - if (!isEmpty(idCard)){ - return match(REGEX_AM_ID_CARD, idCard); - } - return false; - } - - /** - * 校验身份证 -> 台湾 - * @param idCard - * @return - */ - public static boolean isTWIDCard(String idCard){ - if (!isEmpty(idCard)){ - return match(REGEX_TW_ID_CARD, idCard); - } - return false; - } - -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java b/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java deleted file mode 100644 index 1c24de7e47..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java +++ /dev/null @@ -1,220 +0,0 @@ -package dev.utils.common.validator; - -import java.util.regex.Pattern; - -/** - * detail: 检验联系(手机号,座机)工具类 - * Created by Ttt - * http://blog.csdn.net/linbilin_/article/details/49796617 - * // -- - * http://www.cnblogs.com/zengxiangzhan/p/phone.html - */ -public final class ValiToPhoneUtils { - - private ValiToPhoneUtils() { - } - - // ==== 内部方法 ===== - - /** - * 判断是否为null - * @param str - * @return - */ - public static boolean isEmpty(String str) { - return (str == null || str.length() == 0); - } - - /** - * 通用匹配函数 - * @param regex - * @param input - * @return - */ - static boolean match(String regex, String input) { - return Pattern.matches(regex, input); - } - - // == - - /** - * 中国手机号格式验证,在输入可以调用该方法,点击发送验证码,使用 isPhone - * @param phone - * @return - */ - public static boolean isPhoneCheck(String phone){ - if (!isEmpty(phone)){ - return match(CHAIN_PHONE_FORMAT_CHECK, phone); - } - return false; - } - - /** - * 是否中国手机号 - * @param phone - * @return - */ - public static boolean isPhone(String phone){ - if (!isEmpty(phone)){ - return match(CHINA_PHONE_PATTERN, phone); - } - return false; - } - - /** - * 是否中国电信手机号码 - * @param phone - * @return - */ - public static boolean isPhoneToChinaTelecom(String phone){ - if (!isEmpty(phone)){ - return match(CHINA_TELECOM_PATTERN, phone); - } - return false; - } - - /** - * 是否中国联通手机号码 - * @param phone - * @return - */ - public static boolean isPhoneToChinaUnicom(String phone){ - if (!isEmpty(phone)){ - return match(CHINA_UNICOM_PATTERN, phone); - } - return false; - } - - /** - * 是否中国移动手机号码 - * @param phone - * @return - */ - public static boolean isPhoneToChinaMobile(String phone){ - if (!isEmpty(phone)){ - return match(CHINA_MOBILE_PATTERN, phone); - } - return false; - } - - /** - * 判断是否香港手机号 - * @param phone - * @return - */ - public static boolean isPhoneToHkMobile(String phone){ - if (!isEmpty(phone)){ - return match(HK_PHONE_PATTERN, phone); - } - return false; - } - - /** - * 验证电话号码的格式 - * @param phone - * @return - */ - public static boolean isPhoneCallNum(String phone) { - if (!isEmpty(phone)){ - return match(PHONE_CALL_PATTERN, phone); - } - return false; - } - - // ====================== - - // == 手机号判断 == - - /** 简单手机号码校验 => 校验手机号码的长度和1开头 (是否11位)*/ - static final String CHAIN_PHONE_FORMAT_CHECK = "^(?:\\+86)?1\\d{10}$"; - - // 中国手机号正则 - static final String CHINA_PHONE_PATTERN; - - /** 中国电信号码正则 */ - static final String CHINA_TELECOM_PATTERN; - - /** 中国联通号码正则 */ - static final String CHINA_UNICOM_PATTERN; - - /** 中国移动号码正则 */ - static final String CHINA_MOBILE_PATTERN; - - /* 香港手机号码正则 => 香港手机号码8位数,5|6|8|9开头+7位任意数*/ - static final String HK_PHONE_PATTERN = "^(5|6|8|9)\\d{7}$"; - - // == 座机判断 == - - /** 座机电话格式验证 **/ - static final String PHONE_CALL_PATTERN = "^(?:\\(\\d{3,4}\\)|\\d{3,4}-)?\\d{7,8}(?:-\\d{1,4})?$"; - - static { - // ====== 中国电信 ====== - // 电信:133、153、180、181、189 、177(4G)、149、173、174、199 - // 进行拼接字符串,便于理解,后期修改 - StringBuffer sBuffer = new StringBuffer(); - sBuffer.append("^13[3]{1}\\d{8}$"); // 13开头 - sBuffer.append("|"); // 或 - sBuffer.append("^14[9]{1}\\d{8}$"); // 14开头 - sBuffer.append("|"); - sBuffer.append("^15[3]{1}\\d{8}$"); // 15开头 - sBuffer.append("|"); - sBuffer.append("^17[3,4,7]{1}\\d{8}$"); // 17开头 - sBuffer.append("|"); - sBuffer.append("^18[0,1,9]{1}\\d{8}$"); // 18开头 - sBuffer.append("|"); - sBuffer.append("^19[9]{1}\\d{8}$"); // 19开头 - // 手机正则 - CHINA_TELECOM_PATTERN = sBuffer.toString(); - // ======================= - - // ====== 中国联通 ====== - // 联通:130、131、132、155、156、185、186、176(4G)、145(上网卡)、146、166、171、175 - // 进行拼接字符串,便于理解,后期修改 - sBuffer = new StringBuffer(); - sBuffer.append("^13[0,1,2]{1}\\d{8}$"); // 13开头 - sBuffer.append("|"); // 或 - sBuffer.append("^14[5,6]{1}\\d{8}$"); // 14开头 - sBuffer.append("|"); - sBuffer.append("^15[5,6]{1}\\d{8}$"); // 15开头 - sBuffer.append("|"); - sBuffer.append("^16[6]{1}\\d{8}$"); // 16开头 - sBuffer.append("|"); - sBuffer.append("^17[1,5,6]{1}\\d{8}$"); // 17开头 - sBuffer.append("|"); - sBuffer.append("^18[5,6]{1}\\d{8}$"); // 18开头 - // 手机正则 - CHINA_UNICOM_PATTERN = sBuffer.toString(); - // ======================= - - // ====== 中国移动 ====== - // 移动:134、135、136、137、138、139、150、151、152、157、158、159、182、183、184、187、188、178(4G)、147(上网卡)、148、172、198 - // 进行拼接字符串,便于理解,后期修改 - sBuffer = new StringBuffer(); - sBuffer.append("^13[4,5,6,7,8,9]{1}\\d{8}$"); // 13开头 - sBuffer.append("|"); // 或 - sBuffer.append("^14[7,8]{1}\\d{8}$"); // 14开头 - sBuffer.append("|"); - sBuffer.append("^15[0,1,2,7,8,9]{1}\\d{8}$"); // 15开头 - sBuffer.append("|"); - sBuffer.append("^17[2,8]{1}\\d{8}$"); // 17开头 - sBuffer.append("|"); - sBuffer.append("^18[2,3,4,7,8]{1}\\d{8}$"); // 18开头 - sBuffer.append("|"); - sBuffer.append("^19[8]{1}\\d{8}$"); // 19开头 - // 手机正则 - CHINA_MOBILE_PATTERN = sBuffer.toString(); - // ======================= - - /** - * 验证手机号是否正确 - * 移动:134、135、136、137、138、139、150、151、152、157、158、159、182、183、184、187、188、178(4G)、147(上网卡)、148、172、198 - * 联通:130、131、132、155、156、185、186、176(4G)、145(上网卡)、146、166、171、175 - * 电信:133、153、180、181、189 、177(4G)、149、173、174、199 - * 卫星通信:1349 - * 虚拟运营商:170 - * http://www.cnblogs.com/zengxiangzhan/p/phone.html - */ - CHINA_PHONE_PATTERN = "^13[\\d]{9}$|^14[5,6,7,8,9]{1}\\d{8}$|^15[^4]{1}\\d{8}$|^16[6]{1}\\d{8}$|^17[0,1,2,3,4,5,6,7,8]{1}\\d{8}$|^18[\\d]{9}$|^19[8,9]{1}\\d{8}$"; - } -} diff --git a/DevLibUtils/src/main/java/dev/utils/common/validator/ValidatorUtils.java b/DevLibUtils/src/main/java/dev/utils/common/validator/ValidatorUtils.java deleted file mode 100644 index b4efe18e52..0000000000 --- a/DevLibUtils/src/main/java/dev/utils/common/validator/ValidatorUtils.java +++ /dev/null @@ -1,290 +0,0 @@ -package dev.utils.common.validator; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import dev.utils.JCLogUtils; - -/** - * detail: 校验工具类 - * Created by Ttt - */ -public final class ValidatorUtils { - - private ValidatorUtils() { - } - - // 日志TAG - private static final String TAG = ValidatorUtils.class.getSimpleName(); - - /** 正则表达式:验证数字 */ - static final String REGEX_NUMBER = "^[0-9]*$"; - - /** 正则表达式:不能输入特殊字符 ^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$ */ - static final String REGEX_SPECIAL = "^[\\u4E00-\\u9FA5A-Za-z0-9]+$"; - - /** 正则表达式:验证微信号 ^[a-zA-Z]{1}[-_a-zA-Z0-9]{5,19}+$ */ - static final String REGEX_WX = "^[a-zA-Z\\\\d_]{5,19}$"; - - /** 正则表达式:验证真实姓名 |[\w·•])$ ^[\u4e00-\u9fa5]+(·[\u4e00-\u9fa5]+)*$ */ - static final String REGEX_REALNAME = "^[\\u4e00-\\u9fa5]+(•[\\u4e00-\\u9fa5]*)*$|^[\\u4e00-\\u9fa5]+(·[\\u4e00-\\u9fa5]*)*$"; - - /** 正则表达式:验证昵称 */ - static final String REGEX_NICKNAME = "^[\\u4E00-\\u9FA5A-Za-z0-9_]+$"; - - /** 正则表达式:验证用户名(不包含中文和特殊字符)如果用户名使用手机号码或邮箱 则结合手机号验证和邮箱验证 */ - static final String REGEX_USERNAME = "^[a-zA-Z]\\w{5,17}$"; - - /** 正则表达式:验证密码(不包含特殊字符) */ - static final String REGEX_PASSWORD = "^[a-zA-Z0-9]{6,18}$"; - - /** 正则表达式:验证邮箱 */ - static final String REGEX_EMAIL = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; - - /** 正则表达式:验证URL */ - static final String REGEX_URL = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?"; - - /** 正则表达式:验证IP地址 */ - static final String REGEX_IP_ADDR = "(2[5][0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})"; - - // ==== 内部方法 ===== - - /** - * 判断是否为null - * @param str - * @return - */ - public static boolean isEmpty(String str) { - return (str == null || str.length() == 0); - } - - /** - * 通用匹配函数 - * @param regex - * @param input - * @return - */ - static boolean match(String regex, String input) { - return Pattern.matches(regex, input); - } - - // ====== - - /** - * 检验数字 - * @param str - * @return - */ - public static boolean isNumber(String str) { - if (!isEmpty(str)){ - return match(REGEX_NUMBER, str); - } - return false; - } - - /** - * 判断字符串是不是全是字母 - * @param str - * @return - */ - public static boolean isLetter(String str) { - if (!isEmpty(str)){ - return match("^[A-Za-z]+$", str); - } - return false; - } - - /** - * 判断字符串是不是只含字母和数字 - * @param str - * @return - */ - public static boolean isNumberLetter(String str) { - if (!isEmpty(str)){ - return match("^[A-Za-z0-9]+$", str); - } - return false; - } - - /** - * 检验特殊符号 - * @param str - * @return - */ - public static boolean isSpec(String str) { - if (!isEmpty(str)){ - return match(REGEX_SPECIAL, str); - } - return false; - } - - /** - * 检验微信号 - * @param str - * @return - */ - public static boolean isWx(String str) { - if (!isEmpty(str)){ - return match(REGEX_WX, str); - } - return false; - } - - /** - * 检验真实姓名 - * @param str - * @return - */ - public static boolean isRealName(String str) { - if (!isEmpty(str)){ - return match(REGEX_REALNAME, str); - } - return false; - } - - /** - * 校验昵称 - * @param str - * @return - */ - public static boolean isNickName(String str) { - if (!isEmpty(str)){ - return match(REGEX_NICKNAME, str); - } - return false; - } - - /** - * 校验用户名 - * @param str - * @return - */ - public static boolean isUserName(String str) { - if (!isEmpty(str)){ - return match(REGEX_USERNAME, str); - } - return false; - } - - /** - * 校验密码 - * @param str - * @return - */ - public static boolean isPassword(String str) { - if (!isEmpty(str)){ - return match(REGEX_PASSWORD, str); - } - return false; - } - - /** - * 校验邮箱 - * @param str - * @return - */ - public static boolean isEmail(String str) { - if (!isEmpty(str)){ - return match(REGEX_EMAIL, str); - } - return false; - } - - /** - * 校验URL - * @param str - * @return - */ - public static boolean isUrl(String str) { - if (!isEmpty(str)){ - return match(REGEX_URL, str); - } - return false; - } - - /** - * 校验IP地址 - * @param str - * @return - */ - public static boolean isIPAddress(String str) { - if (!isEmpty(str)){ - return match(REGEX_IP_ADDR, str); - } - return false; - } - - /** - * IP地址校验 - * @param str 待校验是否是IP地址的字符串 - * @return - */ - public static boolean isIP(String str) { - if (!isEmpty(str)){ - Pattern pattern = Pattern.compile("\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b"); - Matcher matcher = pattern.matcher(str); - return matcher.matches(); - } - return false; - } - - // ======= - -// // http://blog.csdn.net/myfuturein/article/details/6885216 -// [\\u0391-\\uFFE5]匹配双字节字符(汉字+符号) -// [\\u4e00-\\u9fa5]注意只匹配汉字,不匹配双字节字符 - - /** 正则表达式:验证汉字 */ - static final String REGEX_CHINESE = "^[\u4e00-\u9fa5]+$"; - /** 正则表达式:验证汉字(含双角符号) */ - static final String REGEX_CHINESE_ALL = "^[\u0391-\uFFE5]+$"; - - /** - * 校验汉字(无符号,纯汉字) - * @param str - * @return - */ - public static boolean isChinese(String str) { - if (!isEmpty(str)){ - return match(REGEX_CHINESE, str); - } - return false; - } - - /** - * 判断字符串是不是全是中文 - * @param str - * @return - */ - public static boolean isChineseAll(String str) { - if (!isEmpty(str)){ - return match(REGEX_CHINESE_ALL, str); - } - return false; - } - - /** - * 判断字符串中包含中文、包括中文字符标点等 - * @param data 可能包含中文的字符串 - * @return 是否包含中文 - */ - public static boolean isContainChinese(String data) { - try { - String chinese = "[\u0391-\uFFE5]"; - int length; - if(data != null && (length = data.length()) != 0) { - char[] dChar = data.toCharArray(); - for (int i = 0; i < length; i++) { - boolean flag = String.valueOf(dChar[i]).matches(chinese); - if (flag) { - return true; - } - } - } - } catch (Exception e) { - JCLogUtils.eTag(TAG, e, "isContainChinese"); - } - return false; - } - -} diff --git a/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_error_outline_white.png b/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_error_outline_white.png deleted file mode 100644 index c1dee40696..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_error_outline_white.png and /dev/null differ diff --git a/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_warning_white.png b/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_warning_white.png deleted file mode 100644 index 59fa4428f3..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_warning_white.png and /dev/null differ diff --git a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_frame.png b/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_frame.png deleted file mode 100644 index 778e4e6765..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_frame.png and /dev/null differ diff --git a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_check_white.png b/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_check_white.png deleted file mode 100644 index 3b2b65d262..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_check_white.png and /dev/null differ diff --git a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_clear_white.png b/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_clear_white.png deleted file mode 100644 index b7c7ffd0e7..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_clear_white.png and /dev/null differ diff --git a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_error_outline_white.png b/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_error_outline_white.png deleted file mode 100644 index 4f519d871f..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_error_outline_white.png and /dev/null differ diff --git a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_info_outline_white.png b/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_info_outline_white.png deleted file mode 100644 index c571b2e3e7..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_info_outline_white.png and /dev/null differ diff --git a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_warning_outline_white.png b/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_warning_outline_white.png deleted file mode 100644 index acffe8dcb9..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_warning_outline_white.png and /dev/null differ diff --git a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_warning_white.png b/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_warning_white.png deleted file mode 100644 index 7ca3842953..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-mdpi/dev_toast_ic_warning_white.png and /dev/null differ diff --git a/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_error_outline_white.png b/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_error_outline_white.png deleted file mode 100644 index b144939b9b..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_error_outline_white.png and /dev/null differ diff --git a/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_warning_white.png b/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_warning_white.png deleted file mode 100644 index 8683a2ea9a..0000000000 Binary files a/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_warning_white.png and /dev/null differ diff --git a/README.md b/README.md index 46a5b38fe2..910f57fcfc 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,56 @@ -# About - -> DevUtils 是一个 Android 工具库, 主要根据不同功能模块,封装快捷使用的工具类及 API 方法调用。 ->

该项目尽可能的便于开发人员,快捷、快速开发安全可靠的项目,以及内置部分常用的资源文件,如color.xml、(toast) layout.xml等 - -# Gradle - -Step 1. Add the JitPack repository to your build file -``` -allprojects { - repositories { - maven { url 'https://jitpack.io' } - } -} -``` - -Step 2. Add the dependency -``` -dependencies { - // 因为内含 res 文件, 使用 aar 方式调用 - implementation 'com.github.afkT:DevUtils:1.0.0@aar' - // implementation 'com.github.afkT:DevUtils:latest.release@aar' -} -``` - -## Documentation - -- [README - API](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/README.md) - - -## Use - -> 只需要在 Application 中调用 DevUtils.init() 进行初始化就行 ->

DevUtils.openLog() 是打开内部工具类 日志输出, 发包则不调用此句 ->

DevLogger => https://github.com/afkT/DevLogger - -```java -/** - * detail: 全局Application - * Created by Ttt - */ -public class BaseApplication extends Application{ - - // 日志TAG - private final String LOG_TAG = BaseApplication.class.getSimpleName(); - - @Override - public void onCreate() { - super.onCreate(); - - // 初始化工具类 - DevUtils.init(this.getApplicationContext()); - // == 初始化日志配置 == - // 设置默认Logger配置 - LogConfig logConfig = new LogConfig(); - logConfig.logLevel = LogLevel.DEBUG; - logConfig.tag = LOG_TAG; - DevLogger.init(logConfig); - // 打开 lib 内部日志 - DevUtils.openLog(); - DevUtils.openDebug(); - } -} -``` - -# Thanks - -> 感谢以下开源项目的作者,本项目中有些功能受你们项目灵感的启发,有些功能也用到你们的代码完成。 - -- [orhanobut/logger](https://github.com/orhanobut/logger) -- [laobie/StatusBarUtil](https://github.com/laobie/StatusBarUtil) -- [GrenderG/Toasty](https://github.com/GrenderG/Toasty) -- [Blankj/AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode) -- [l123456789jy/Lazy](https://github.com/l123456789jy/Lazy) -- [yangfuhai/ASimpleCache](https://github.com/yangfuhai/ASimpleCache) -- [AbrahamCaiJin/CommonUtilLibrary](https://github.com/AbrahamCaiJin/CommonUtilLibrary) -- [litesuits/android-common](https://github.com/litesuits/android-common) \ No newline at end of file + +[![Cocoapods](https://img.shields.io/badge/GitHub-afkT-blue.svg)](https://github.com/afkT) +[![GitHub license](https://img.shields.io/github/license/afkT/DevUtils.svg)](https://github.com/afkT/DevUtils/blob/master/LICENSE) +[![Cocoapods](https://img.shields.io/badge/DevUtils-1.8.2-brightgreen.svg)](https://github.com/afkT/DevUtils) +[![API](https://img.shields.io/badge/API-14%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=14) +[![Cocoapods](https://img.shields.io/badge/utils-100+-ff69b4.svg)](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/README.md) + +# About (持续更新,目前含100+工具类) + +> DevUtils 是一个 Android 工具库,主要根据不同功能模块,封装快捷使用的工具类及 API 方法调用 +> +> 该项目尽可能的便于开发人员,快捷、高效开发安全可靠的项目,以及内置部分常用的资源文件,如 color.xml + +![module](https://raw.githubusercontent.com/afkT/DevUtils/master/file/DevUtils.png) + + +## Documentation - [Lib](https://github.com/afkT/DevUtils/tree/master/lib) + +### DevApp + +- [README - API](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/README.md) + +- [Use and Config](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/USE_CONFIG.md) + +### DevAssist + +- [README - API](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/README.md) + +### DevJava + +- [README - API](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/README.md) + + +## Other + +> [DevOther](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/README.md) 第三方库封装、以及部分特殊工具类等,方便 copy 封装类使用 + +> [JavaDoc API Generate](https://github.com/afkT/JavaDoc) 该工具类 (DevUtils) API 文档,是通过 JavaDoc 项目读取 class 信息生成,并且进行代码、注释间距规范检测,生成效果示范 [DevApp - API](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/README.md) + + +## License + + Copyright 2018 afkT + + 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. + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index fcbac1ab02..500dfdca6b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,30 +1,31 @@ -apply plugin: 'com.android.application' +apply { + from rootProject.file(files.app_gradle) +} android { - - compileSdkVersion 27 - buildToolsVersion "27.0.3" - defaultConfig { applicationId "com.dev" - minSdkVersion 19 - targetSdkVersion 27 - versionCode 1 - versionName "1.0.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } } } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:27.1.1' - implementation 'com.android.support:support-v4:27.1.1' - compile 'com.android.support.constraint:constraint-layout:1.0.2' + implementation project(path: ':DevApp') + implementation project(path: ':DevAssist') + implementation project(path: ':DevOther') - compile project(path: ':DevLibUtils') -} + // ======= + // = lib = + // ======= + + // Gson + implementation deps.lib.gson + // Imageloader + implementation deps.lib.imageloader + // Glide + implementation deps.lib.glide + annotationProcessor deps.lib.glide_compiler + // EventBus + implementation deps.lib.eventbus + // OkHttp3 + implementation deps.lib.okhttp3 +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee9c747be4..db7634dbbe 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,12 +2,18 @@ + + + + + + diff --git a/app/src/main/java/com/dev/BaseApplication.java b/app/src/main/java/com/dev/BaseApplication.java index 1971918cee..170d44f4f8 100644 --- a/app/src/main/java/com/dev/BaseApplication.java +++ b/app/src/main/java/com/dev/BaseApplication.java @@ -9,11 +9,11 @@ /** * detail: 全局Application - * Created by Ttt + * @author Ttt */ -public class BaseApplication extends Application{ +public class BaseApplication extends Application { - // 日志TAG + // 日志 TAG private final String LOG_TAG = BaseApplication.class.getSimpleName(); @Override @@ -22,13 +22,14 @@ public void onCreate() { // 初始化工具类 DevUtils.init(this.getApplicationContext()); - // == 初始化日志配置 == + // = 初始化日志配置 = // 设置默认Logger配置 LogConfig logConfig = new LogConfig(); logConfig.logLevel = LogLevel.DEBUG; logConfig.tag = LOG_TAG; + logConfig.sortLog = true; // 美化日志, 边框包围 DevLogger.init(logConfig); - // 打开 lib 内部日志 + // 打开 lib 内部日志 - 线上环境, 不调用方法就行 DevUtils.openLog(); DevUtils.openDebug(); } diff --git a/app/src/main/java/com/dev/MainActivity.java b/app/src/main/java/com/dev/MainActivity.java index 72698be77a..c48fc6a657 100644 --- a/app/src/main/java/com/dev/MainActivity.java +++ b/app/src/main/java/com/dev/MainActivity.java @@ -1,7 +1,10 @@ package com.dev; -import android.support.v7.app.AppCompatActivity; +import android.Manifest; import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import dev.utils.app.permission.PermissionUtils; public class MainActivity extends AppCompatActivity { @@ -9,5 +12,9 @@ public class MainActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + + // 申请权限 + PermissionUtils.permission(Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE).request(this); } } diff --git a/app/src/main/java/com/dev/utils/Config.java b/app/src/main/java/com/dev/utils/Config.java new file mode 100644 index 0000000000..f2539653dc --- /dev/null +++ b/app/src/main/java/com/dev/utils/Config.java @@ -0,0 +1,51 @@ +package com.dev.utils; + +import java.io.File; + +import dev.utils.app.SDCardUtils; + +/** + * detail: 配置页面 + * @author Ttt + */ +public final class Config { + + // 项目日志 Tag + public static final String LOG_TAG = "DevUtils_Log"; + + // = 项目信息 = + + // 项目名 + public static final String BASE_NAME = "DevUtils"; + // 缩写标识 - 小写 + public static final String BASE_NAME_SHORT = "dev"; + // 缩写标识 - 大写 + public static final String BASE_NAME_SHORT_CAP = "DEV"; + // Bugly 项目标示 + public static final String BUGLY_PRONAME_MARK = "DevUtils_Android"; + // SDCard 路径 + public static final String BASE_SDCARD_PATH = SDCardUtils.getSDCardPath(); + + // = 本地 SDCard 数据 = + + // SDCard Pro 文件统一前缀 SDP_xxx + + // 统一文件夹 + public static final String SDP_PATH = BASE_SDCARD_PATH + File.separator + BASE_NAME + File.separator; + + // 临时存储 + public static final String SDP_TEMP_PATH = SDP_PATH + "Temp" + File.separator; + + // 本地 SDCard 资源缓存地址 + public static final String SDP_CACHE_PATH = SDP_PATH + "Cache" + File.separator; + + // 下载保存路径 + public static final String SDP_DOWN_PATH = SDP_PATH + BASE_NAME_SHORT_CAP + "Download" + File.separator; + + // 下载图片路径 + public static final String SDP_DOWN_IMAGE_PATH = SDP_DOWN_PATH + "Image" + File.separator; + + // 文本存储路径 + public static final String SDP_TEXT_PATH = SDP_DOWN_PATH + "Text" + File.separator; + +} diff --git a/app/src/main/java/com/dev/utils/cache/CacheUse.java b/app/src/main/java/com/dev/utils/cache/CacheUse.java new file mode 100644 index 0000000000..f83f6dcacf --- /dev/null +++ b/app/src/main/java/com/dev/utils/cache/CacheUse.java @@ -0,0 +1,89 @@ +package com.dev.utils.cache; + +import android.content.Context; + +import java.io.File; +import java.io.Serializable; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.app.SDCardUtils; +import dev.utils.app.cache.DevCache; +import dev.utils.app.logger.DevLogger; + +/** + * detail: 缓存使用方法 + * @author Ttt + */ +public final class CacheUse { + + private CacheUse() { + } + + // 日志 TAG + private static final String TAG = CacheUse.class.getSimpleName(); + + /** + * 缓存使用方法 + */ + private void cacheUse() { + final Context mContext = DevUtils.getContext(); + + // 初始化 + CacheVo cacheVo = new CacheVo("测试持久化"); + // 打印信息 + LogPrintUtils.dTag(TAG, "保存前: " + cacheVo.toString()); + // 保存数据 + DevCache.get(mContext).put("ctv", cacheVo); + // 重新获取 + CacheVo ctv = (CacheVo) DevCache.get(mContext).getAsObject("ctv"); + // 打印获取后的数据 + DevLogger.dTag(TAG, "保存后: " + ctv.toString()); + // 设置保存有效时间 5秒 + DevCache.get(mContext).put("ctva", new CacheVo("测试有效时间"), 1); + + // 保存到指定文件夹下 + DevCache.get(new File(SDCardUtils.getSDCardPath(), "Cache")).put("key", "保存数据"); + + // 延迟后 + new Thread(new Runnable() { + @Override + public void run() { + try { + // 延迟 1.5 已经过期再去获取 + Thread.sleep(1500); + // 获取数据 + CacheVo ctva = (CacheVo) DevCache.get(mContext).getAsObject("ctva"); + // 判断是否过期 + DevLogger.dTag(TAG, "是否过期: " + (ctva == null)); + } catch (Exception e) { + } + } + }).start(); + } + + /** + * 缓存实体类 + */ + static class CacheVo implements Serializable { + + String name; + + long time; + + public CacheVo(String name) { + this.name = name; + this.time = System.currentTimeMillis(); + } + + public CacheVo(String name, long time) { + this.name = name; + this.time = time; + } + + @Override + public String toString() { + return "name: " + name + ", time: " + time; + } + } +} diff --git a/app/src/main/java/com/dev/utils/logger/LogTools.java b/app/src/main/java/com/dev/utils/logger/LogTools.java new file mode 100644 index 0000000000..63e13beb14 --- /dev/null +++ b/app/src/main/java/com/dev/utils/logger/LogTools.java @@ -0,0 +1,74 @@ +package com.dev.utils.logger; + +/** + * detail: 日志工具类 + * @author Ttt + *

+ *     这个类的主要作用是把方法封装好, 然后实现传入实体类, 或者几个参数, 返回处理后的String
+ *     不用在代码中, 拼接String, 然后打印日志, 这样代码维护起来方便, 也快捷, 都是1-2句代码实现具体功能调用
+ * 
+ */ +class LogTools { + + /** + * 获取分享信息实体类数据 + * @param sMsgVo 分享实体类对象 + * @return + */ + public static String getShareMsgVoData(TestData.ShareMsgVo sMsgVo) { + StringBuilder builder = new StringBuilder(); + try { + if (sMsgVo != null) { + builder.append("打印分享信息实体类数据"); + builder.append("\n分享标题: " + sMsgVo.sTitle); + builder.append("\n分享文本: " + sMsgVo.sText); + builder.append("\n分享的图片路径: " + sMsgVo.sImagePath); + builder.append("\n标题网络链接: " + sMsgVo.sTitleUrl); + } else { + builder.append("sMsgVo 为 null"); + } + } catch (Exception e) { + } + return builder.toString(); + } + + /** + * 获取用户信息实体类数据 + * @param uInfoVo 用户信息实体类对象 + * @return + */ + public static String getUserInfoVoData(TestData.UserInfoVo uInfoVo) { + StringBuilder builder = new StringBuilder(); + try { + if (uInfoVo != null) { + builder.append("打印用户信息实体类数据"); + builder.append("\n用户名: " + uInfoVo.uName); + builder.append("\n用户密码: " + uInfoVo.uPwd); + builder.append("\n用户年龄: " + uInfoVo.uAge); + } else { + builder.append("uInfoVo 为 null"); + } + } catch (Exception e) { + } + return builder.toString(); + } + + /** + * 获取零散参数数据 + * @param uName 用户名字 + * @param sTitle 分享标题 + * @param uAge 用户年龄 + * @return + */ + public static String getScatteredData(String uName, String sTitle, int uAge) { + StringBuilder builder = new StringBuilder(); + try { + builder.append("打印零散参数数据"); + builder.append("\nuName: " + uName); + builder.append("\nsTitle: " + sTitle); + builder.append("\nuAge: " + uAge); + } catch (Exception e) { + } + return builder.toString(); + } +} diff --git a/app/src/main/java/com/dev/utils/logger/LoggerUse.java b/app/src/main/java/com/dev/utils/logger/LoggerUse.java new file mode 100644 index 0000000000..624092ff7d --- /dev/null +++ b/app/src/main/java/com/dev/utils/logger/LoggerUse.java @@ -0,0 +1,286 @@ +package com.dev.utils.logger; + +import android.util.Log; + +import com.dev.utils.Config; + +import java.io.File; + +import dev.utils.app.logger.DevLogger; +import dev.utils.app.logger.DevLoggerUtils; +import dev.utils.app.logger.LogConfig; +import dev.utils.app.logger.LogLevel; +import dev.utils.common.DevCommonUtils; + +/** + * detail: 日志使用方法 + * @author Ttt + */ +public final class LoggerUse { + + private LoggerUse() { + } + + // 日志 TAG + private static final String LOG_TAG = LoggerUse.class.getSimpleName(); + // 日志文件夹路径 + private static final String LOG_SD_PATH = Config.SDP_PATH + File.separator + "Logger" + File.separator; + + // ======== + // = 配置 = + // ======== + + /** + * 日志配置相关 + */ + private void logConfig() { + // = 在BaseApplication 中调用 = + // 初始化日志配置 + LogConfig logConfig = new LogConfig(); + // 堆栈方法总数(显示经过的方法) + logConfig.methodCount = 3; + // 堆栈方法索引偏移(0 = 最新经过调用的方法信息, 偏移则往上推, 如 1 = 倒数第二条经过调用的方法信息) + logConfig.methodOffset = 0; + // 是否输出全部方法(在特殊情况下, 如想要打印全部经过的方法, 但是不知道经过的总数) + logConfig.outputMethodAll = false; + // 显示日志线程信息(特殊情况, 显示经过的线程信息, 具体情况如上) + logConfig.displayThreadInfo = false; + // 是否排序日志(格式化后) + logConfig.sortLog = false; // 是否美化日志, 边框包围 + // 日志级别 + logConfig.logLevel = LogLevel.DEBUG; + // 设置 TAG (特殊情况使用, 不使用全部的 TAG 时, 如单独输出在某个 TAG 下) + logConfig.tag = "BaseLog"; + // 进行初始化配置, 这样设置后, 默认全部日志都使用改配置, 特殊使用 DevLogger.other(config).d(xxx); + DevLogger.init(logConfig); +// // 进行初始化配置, 在DevUtils.init() 内部调用了 +// DevLoggerUtils.init(mContext); // 日志操作工具类, 快捷获取 LogConfig、以及保存日志到文件中等 + } + + // = 使用 = + + /** + * 日志使用方法 + */ + public static void loggerUse() { + // 测试打印Log所用时间 + testTime(); + + // try, catch 保存异常日志 + exLog(); + + // 正常保存日志 + saveLog(); + + // 使用日志操作 + tempLog(); + } + + /** + * 测试打印Log所用时间 + */ + private static void testTime() { + // 拼接字符串 + StringBuilder builder = new StringBuilder(); + // 日志 TAG + final String tag = "CALC_TIME"; + // = + // 遍历次数 + int count = 1000; + // 设置开始时间 + long sTime = System.currentTimeMillis(); + // 开始遍历 + for (int i = 0; i < count; i++) { + Log.d(tag, "A: " + (i + 1)); + } + // 拼接时间信息 + DevCommonUtils.timeRecord(builder, "正常系统Log耗时记录", sTime, System.currentTimeMillis()); + + // = + // 设置开始时间 + sTime = System.currentTimeMillis(); + // 开始遍历 + for (int i = 0; i < count; i++) { + // DevLogger.d("B: " + (i + 1)); + DevLogger.dTag(tag, "B: " + (i + 1)); + } + // 拼接时间信息 + DevCommonUtils.timeRecord(builder, "Logger耗时记录", sTime, System.currentTimeMillis()); + + // = + // 初始化日志配置 + LogConfig logConfig = new LogConfig(); + // 显示日志线程信息(特殊情况, 显示经过的线程信息, 具体情况如上) + logConfig.displayThreadInfo = true; + // 是否排序日志(格式化后) + logConfig.sortLog = true; + // 日志级别 + logConfig.logLevel = LogLevel.DEBUG; + // 设置开始时间 + sTime = System.currentTimeMillis(); + // 开始遍历 + for (int i = 0; i < count; i++) { + DevLogger.other(logConfig).dTag(tag, "C: " + (i + 1)); + } + // 拼接时间信息 + DevCommonUtils.timeRecord(builder, "Logger耗时记录 - 使用自定义日志配置", sTime, System.currentTimeMillis()); + // 打印时间 + Log.d(LOG_TAG, builder.toString()); + } + + /** + * 打印、保存异常日志 + */ + private static void exLog() { + // = 保存异常日志 = + try { + String s = null; + s.indexOf("c"); + } catch (NullPointerException e) { + // 打印格式化后的日志信息 + DevLogger.other(DevLoggerUtils.getSortLogConfig("LogPro")).e(e, "s = null"); + // 保存的路径 + String fileName = LOG_SD_PATH + System.currentTimeMillis() + ".log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fileName); + // = + // 保存自定义头部、底部信息 + DevLoggerUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log"); + // = + // 保存的路径 + fileName = LOG_SD_PATH + System.currentTimeMillis() + "_orgs.log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fileName); + } + } + + /** + * 保存日志 + */ + private static void saveLog() { + // = 保存日志 = + // 保存文件名 + String fileName = System.currentTimeMillis() + ".log"; + // 保存日志 + DevLoggerUtils.saveLog("保存自定义信息日志", LOG_SD_PATH, fileName); + + // = 保存日志, 包含头部信息、底部信息 = + // 保存文件名 + fileName = System.currentTimeMillis() + ".log"; + // 保存日志 + DevLoggerUtils.saveLog("保存自定义信息日志", "头部", "底部", LOG_SD_PATH, fileName); + } + + /** + * 打印临时日志 + */ + private static void tempLog() { + // = 打印零散数据 = + TestData.ShareMsgVo sMsgVo = new TestData.ShareMsgVo(); + sMsgVo.sTitle = "分享Blog"; + sMsgVo.sText = null; + sMsgVo.sImagePath = "http://t.jpg"; + sMsgVo.sTitleUrl = "http://www.test.com"; + + TestData.UserInfoVo uInfoVo = new TestData.UserInfoVo(); + uInfoVo.uName = "BlogRecord"; + uInfoVo.uPwd = "log_pwd"; + uInfoVo.uAge = 100; + + // 打印分享数据 + DevLogger.d(LogTools.getShareMsgVoData(sMsgVo)); + // 打印用户数据 + DevLogger.d(LogTools.getUserInfoVoData(uInfoVo)); + // 打印零散数据 + DevLogger.d(LogTools.getScatteredData(uInfoVo.uName, sMsgVo.sTitle, uInfoVo.uAge)); + + // = 打印测试数据 = + // 日志 TAG + final String tag = LOG_TAG; + // = 使用 BaseApplication 默认配置 = + // JSON数组 + DevLogger.json("[" + TestData.JSON_WITH_NO_LINE_BREAK + "," + TestData.JSON_WITH_NO_LINE_BREAK + "]"); + // JSON对象 + DevLogger.json(TestData.SMALL_SON_WITH_NO_LINE_BREAK); + // XML数据 + DevLogger.xml(TestData.XML_DATA); + // = 其他 = + DevLogger.v("测试数据 - v"); + DevLogger.d("测试数据 - d"); + DevLogger.i("测试数据 - i"); + DevLogger.w("测试数据 - w"); + DevLogger.e("错误 - e"); + DevLogger.wtf("测试数据 - wtf"); + // = + DevLogger.vTag(tag, "测试数据 - v"); + DevLogger.vTag(tag, "测试数据 - d"); + try { + Class clazz = Class.forName("asdfasd"); + } catch (ClassNotFoundException e) { + DevLogger.e(e, "发生异常"); + } + // 占位符(其他类型, 一样) + DevLogger.d("%s测试占位符数据 - d%s", "1.", " - Format"); + // = + DevLogger.dTag(tag, "%s测试占位符数据 - d%s", "1.", " - Format"); + + // = 使用自定义临时配置 = + // 自定义配置, 如下使用方式 + // DevLogger.other(logConfig).d(message); + // DevLogger.other(logConfig).dTag(tag, message); + // 打印不换行的日志信息 + DevLogger.other(DevLoggerUtils.getDebugLogConfig(tag)).vTag("Temp", "测试数据 - v"); + DevLogger.other(DevLoggerUtils.getDebugLogConfig(tag)).d("测试数据 - d"); + DevLogger.other(DevLoggerUtils.getDebugLogConfig(tag)).i("测试数据 - i"); + DevLogger.other(DevLoggerUtils.getDebugLogConfig(tag)).w("测试数据 - w"); + DevLogger.other(DevLoggerUtils.getDebugLogConfig(tag)).e("错误 - e"); + DevLogger.other(DevLoggerUtils.getDebugLogConfig(tag)).wtf(tag, "测试数据 - wtf"); + // = + DevLogger.other(DevLoggerUtils.getDebugLogConfig(tag, LogLevel.DEBUG)).json(TestData.SMALL_SON_WITH_NO_LINE_BREAK); + + // = + // 初始化日志配置 + LogConfig logConfig = new LogConfig(); + // 堆栈方法总数(显示经过的方法) + logConfig.methodCount = 3; + // 堆栈方法索引偏移(0 = 最新经过调用的方法信息, 偏移则往上推, 如 1 = 倒数第二条经过调用的方法信息) + logConfig.methodOffset = 0; + // 是否输出全部方法(在特殊情况下, 如想要打印全部经过的方法, 但是不知道经过的总数) + logConfig.outputMethodAll = false; + // 显示日志线程信息(特殊情况, 显示经过的线程信息, 具体情况如上) + logConfig.displayThreadInfo = true; + // 是否排序日志(格式化后) + logConfig.sortLog = true; + // 日志级别 + logConfig.logLevel = LogLevel.DEBUG; + // 设置 TAG (特殊情况使用, 不使用全部的 TAG 时, 如单独输出在某个 TAG 下) + logConfig.tag = "SAD"; + // 打印不换行的日志信息 + DevLogger.other(logConfig).e("new Config - e"); + + // = + // 使用方法 + LogConfig tempLogConfig = new LogConfig(); + // 堆栈方法总数(显示经过的方法) + tempLogConfig.methodCount = 10; + // 堆栈方法索引偏移(0 = 最新经过调用的方法信息, 偏移则往上推, 如 1 = 倒数第二条经过调用的方法信息) + tempLogConfig.methodOffset = 0; + // 是否输出全部方法(在特殊情况下, 如想要打印全部经过的方法, 但是不知道经过的总数) + tempLogConfig.outputMethodAll = false; + // 显示日志线程信息(特殊情况, 显示经过的线程信息, 具体情况如上) + tempLogConfig.displayThreadInfo = true; + // 是否排序日志(格式化后) + tempLogConfig.sortLog = true; + // 日志级别 + tempLogConfig.logLevel = LogLevel.DEBUG; + // 设置 TAG (特殊情况使用, 不使用全部的 TAG 时, 如单独输出在某个 TAG 下) + tempLogConfig.tag = "SAD"; + try { + String s = null; + s.indexOf("tempLogConfig"); + } catch (Exception e) { + // 打印不换行的日志信息 + DevLogger.other(tempLogConfig).e(e, "new Config - e"); + } + } +} diff --git a/app/src/main/java/com/dev/utils/logger/TestData.java b/app/src/main/java/com/dev/utils/logger/TestData.java new file mode 100644 index 0000000000..77610d7f12 --- /dev/null +++ b/app/src/main/java/com/dev/utils/logger/TestData.java @@ -0,0 +1,110 @@ +package com.dev.utils.logger; + +/** + * detail: 测试数据 + * @author Ttt + */ +class TestData { + + /** + * 分享信息实体类(测试) + */ + public static class ShareMsgVo { + + public int sPlatform = 0; // 分享平台 + public String sTitle = null; // title标题, 印象笔记、邮箱、信息、微信、人人网和QQ空间使用 + public String sText = null; // text是分享文本, 所有平台都需要这个字段 + public String sImagePath = null; // imagePath是图片的本地路径, Linked-In以外的平台都支持此参数 + public String sImageUrl = null; // 分享图片路径(QQ空间需要) + public String sTitleUrl = null; // titleUrl是标题的网络链接, 仅在人人网和QQ空间使用 + // = 不一定使用 = + public String sUrl = null; // url仅在微信(包括好友和朋友圈)中使用 + public String sComment = null; // 分享的评论, 仅在人人网和QQ空间使用 + public String sSite = null; // site是分享此内容的网站名称, 仅在QQ空间使用 + public String sSiteUrl = null; // siteUrl是分享此内容的网站地址, 仅在QQ空间使用 + // = 微信平台分享类型 = + public int weChatATShareType = 0; // 分享类型shareType(SHARE_IMAGE) ,shareType(SHARE_VIDEO), shareType(SHARE_WEBPAGE) + // 栈索引(用于移除顶部栈 View) + public int sTaskId = -1; // -1 表示不需要移除栈例如单图片分享, 不会添加到栈, 自然也不需要移除 + // 分享模式 + public int sMode = -1; + } + + /** + * 用户信息实体类(测试) + */ + public static class UserInfoVo { + + public String uName = null; // 用户名 + public String uPwd = null; // 用户密码 + public int uAge = -1; // 用户年龄 + } + + // = + + public static final String JSON_WITH_LINE_BREAK = "{\"widget\": {\n" + + " \"debug\": \"on\",\n" + + " \"window\": {\n" + + " \"title\": \"Sample Konfabulator Widget\",\n" + + " \"name\": \"main_window\",\n" + + " \"width\": 500,\n" + + " \"height\": 500\n" + + " },\n" + + " \"t.dev.image\": { \n" + + " \"src\": \"Images/Sun.png\",\n" + + " \"name\": \"sun1\",\n" + + " \"hOffset\": 250,\n" + + " \"vOffset\": 250,\n" + + " \"alignment\": \"center\"\n" + + " },\n" + + " \"t.dev.text\": {\n" + + " \"data\": \"Click Here\",\n" + + " \"size\": 36,\n" + + " \"style\": \"bold\",\n" + + " \"name\": \"text1\",\n" + + " \"hOffset\": 250,\n" + + " \"vOffset\": 100,\n" + + " \"alignment\": \"center\",\n" + + " \"onMouseUp\": \"sun1.opacity = (sun1.opacity / 100) * 90;\"\n" + + " }\n" + + "}} "; + + public static final String JSON_WITH_NO_LINE_BREAK = "{\"widget\": {" + + " \"debug\": \"on\"," + + " \"window\": {" + + " \"title\": \"Sample Konfabulator Widget\"," + + " \"name\": \"main_window\"," + + " \"width\": 500," + + " \"height\": 500" + + " },\n" + + " \"t.dev.image\": { " + + " \"src\": \"Images/Sun.png\"," + + " \"name\": \"sun1\"," + + " \"hOffset\": 250," + + " \"vOffset\": 250," + + " \"alignment\": \"center\"" + + " },\n" + + " \"t.dev.text\": {" + + " \"data\": \"Click Here\"," + + " \"size\": 36," + + " \"style\": \"bold\"," + + " \"name\": \"text1\"," + + " \"hOffset\": 250," + + " \"vOffset\": 100," + + " \"alignment\": \"center\"," + + " \"onMouseUp\": \"sun1.opacity = (sun1.opacity / 100) * 90;\"" + + " }" + + "}} "; + + public static final String SMALL_SON_WITH_NO_LINE_BREAK = "{\"widget\": {" + + " \"debug\": \"on\"," + + " \"window\": {" + + " \"title\": \"Sample Konfabulator Widget\"," + + " \"name\": \"main_window\"," + + " \"width\": 500," + + " \"height\": 500" + + " }" + + "}} "; + + public static final String XML_DATA = "abcd"; +} diff --git a/app/src/main/java/com/dev/utils/media/MediaUse.java b/app/src/main/java/com/dev/utils/media/MediaUse.java new file mode 100644 index 0000000000..394642ae99 --- /dev/null +++ b/app/src/main/java/com/dev/utils/media/MediaUse.java @@ -0,0 +1,123 @@ +package com.dev.utils.media; + +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.view.SurfaceView; + +import com.dev.R; + +import dev.utils.app.SDCardUtils; +import dev.utils.app.player.DevMediaManager; +import dev.utils.app.player.DevVideoPlayerControl; + +/** + * detail: 多媒体使用方法 + * @author Ttt + */ +public final class MediaUse { + + private MediaUse() { + } + + // 日志 TAG + private static final String TAG = MediaUse.class.getSimpleName(); + + /** + * 多媒体使用方法 + */ + private void mediaUse() { + // 设置 TAG, 打印日志使用 + DevMediaManager.getInstance().setTAG(TAG); + // 设置音量 + DevMediaManager.getInstance().setVolume(50); + // 设置流类型 + DevMediaManager.getInstance().setAudioStreamType(AudioManager.STREAM_MUSIC); + + // 获取播放音量 + DevMediaManager.getInstance().getVolume(); + // 获取当前播放的地址 + DevMediaManager.getInstance().getPlayUri(); + // 获取播放的资源id + DevMediaManager.getInstance().getPlayRawId(); + // 获取 当前播放时间 + DevMediaManager.getInstance().getCurrentPosition(); + // 获取资源总时间 + DevMediaManager.getInstance().getDuration(); + // 获取播放进度百分比 + DevMediaManager.getInstance().getPlayPercent(); + // 获取 MediaPlayer 对象 + DevMediaManager.getInstance().getMediaPlayer(); + + // 获取播放的视频高度 + DevMediaManager.getInstance().getVideoHeight(); + // 获取播放的视频宽度 + DevMediaManager.getInstance().getVideoWidth(); + + // 是否播放中 + DevMediaManager.getInstance().isPlaying(); + // 停止操作 + DevMediaManager.getInstance().stop(); + // 暂停操作 + DevMediaManager.getInstance().pause(); + + // 设置事件监听 + DevMediaManager.getInstance().setMeidaListener(new DevMediaManager.MediaListener() { + @Override + public void onPrepared() { + if (DevMediaManager.getInstance().isNotNullMediaPlayer()) { + // 播放操作 + DevMediaManager.getInstance().getMediaPlayer().start(); + } + } + + @Override + public void onCompletion() { + } + + @Override + public void onBufferingUpdate(int percent) { + } + + @Override + public void onSeekComplete() { + } + + @Override + public boolean onError(int what, int extra) { + return false; + } + + @Override + public void onVideoSizeChanged(int width, int height) { + } + }); + + // = + + // 播放音频 + DevMediaManager.getInstance().playPrepareRaw(R.raw.dev_beep); + DevMediaManager.getInstance().playPrepareAssets("a.mp3"); + DevMediaManager.getInstance().playPrepare(SDCardUtils.getSDCardPath() + "/a.mp3"); + DevMediaManager.getInstance().playPrepare("http://xxx.mp3"); + DevMediaManager.getInstance().playPrepare(new DevMediaManager.MediaSet() { + @Override + public void setMediaConfig(MediaPlayer mediaPlayer) throws Exception { + mediaPlayer.setDataSource("xxx"); + } + }); // 自由设置信息 + + // = + + SurfaceView surfaceView = null; + // 播放视频 + DevVideoPlayerControl control = new DevVideoPlayerControl(surfaceView); + control.startPlayer(SDCardUtils.getSDCardPath() + "/video_3.mp4"); + control.startPlayer("http://xxx.mp4"); + control.startPlayer(new DevMediaManager.MediaSet() { + @Override + public void setMediaConfig(MediaPlayer mediaPlayer) throws Exception { + mediaPlayer.setDataSource("xxx"); + } + }); // 自由设置信息 + } +} diff --git a/app/src/main/java/com/dev/utils/record/FileRecordUse.java b/app/src/main/java/com/dev/utils/record/FileRecordUse.java new file mode 100644 index 0000000000..cac6cf93e7 --- /dev/null +++ b/app/src/main/java/com/dev/utils/record/FileRecordUse.java @@ -0,0 +1,142 @@ +package com.dev.utils.record; + +import com.dev.utils.Config; + +import java.io.File; + +import dev.utils.app.AnalysisRecordUtils; +import dev.utils.app.AppUtils; +import dev.utils.app.FileRecordUtils; +import dev.utils.app.SDCardUtils; +import dev.utils.app.logger.DevLoggerUtils; +import dev.utils.common.ThrowableUtils; + +/** + * detail: 日志、异常文件记录保存使用方法 + * @author Ttt + */ +public final class FileRecordUse { + + private FileRecordUse() { + } + + // 日志 TAG + private static final String TAG = FileRecordUse.class.getSimpleName(); + + /** + * 日志、异常文件记录保存使用方法 + */ + private void fileRecordUse() { + + // AnalysisRecordUtils + + // DevLoggerUtils + + // FileRecordUtils + + // DevLoggerUtils 内部的 Utils, 实际和 FileRecordUtils 代码相同, 使用方式一致 + + // = 记录文件 = + + // AnalysisRecordUtils 工具类使用方法 + analysisRecord(); + + // DevLoggerUtils、FileRecordUtils 工具类 + logRecord(); + } + + /** + * 日志文件夹路径 + */ + public static final String LOG_SD_PATH = Config.SDP_PATH + File.separator + "Logger" + File.separator; + + /** + * AnalysisRecordUtils 工具类使用方法 + */ + private void analysisRecord() { + // 默认存储到 android/data/包名/cache文件/, 可以自己特殊设置 + AnalysisRecordUtils.setLogStoragePath(SDCardUtils.getSDCardPath()); + + // 设置存储文件夹名 + AnalysisRecordUtils.setLogFolderName(AnalysisRecordUtils.getLogFolderName() + "/v" + AppUtils.getAppVersionName()); + + // AnalysisRecordUtils.HH、MM、SS 以对应的时间, 创建文件夹 HH_23/MM_13/SS_01 依此类推, 放到对应文件夹, 不传则放到当日文件夹下 + AnalysisRecordUtils.FileInfo fileInfo = AnalysisRecordUtils.FileInfo.obtain("test_log.txt", "测试记录", AnalysisRecordUtils.HH); + + // 存储路径、存储文件夹、文件名、记录功能提示、时间间隔、是否处理日志记录(是否保存) + // FileInfo(String storagePath, String folderName, String fileName, String fileFunction, @AnalysisRecordUtils.TIME int fileIntervalTime, boolean handler) + + // = FileInfo 配置 = + + fileInfo = AnalysisRecordUtils.FileInfo.obtain("test_log.txt", "测试记录"); + + fileInfo = AnalysisRecordUtils.FileInfo.obtain("TempRecord", "test_log.txt", "测试记录"); + + fileInfo = AnalysisRecordUtils.FileInfo.obtain(SDCardUtils.getSDCardPath(), "TempRecord", "test_log.txt", "测试记录"); + + // = + + fileInfo = AnalysisRecordUtils.FileInfo.obtain("test_log.txt", "测试记录", AnalysisRecordUtils.HH); + + fileInfo = AnalysisRecordUtils.FileInfo.obtain("TempRecord", "test_log.txt", "测试记录", AnalysisRecordUtils.MM); + + fileInfo = AnalysisRecordUtils.FileInfo.obtain(SDCardUtils.getSDCardPath(), "TempRecord", "test_log.txt", "测试记录", AnalysisRecordUtils.SS); + + // = + + // 存储到 android/data/包名/LogFolderName/2018-08-23/LogFolderName/xxx/log.txt + AnalysisRecordUtils.record(fileInfo, "日志内容"); + + // 存储到 sdcard/LogFolderName/2018-08-23/SDRecord/xxx/log.txt + AnalysisRecordUtils.record(AnalysisRecordUtils.FileInfo.obtain(SDCardUtils.getSDCardPath(), "SDRecord", "sd_log.txt", "根目录保存", AnalysisRecordUtils.HH), + "日志内容"); + + // 存储到 sdcard/特殊地址/LogFolderName/2018-08-23/OtherRecord/xxx/log.txt + AnalysisRecordUtils.record(AnalysisRecordUtils.FileInfo.obtain(SDCardUtils.getSDCardPath() + "/特殊地址", "OtherRecord", "log.txt", "临时地址", AnalysisRecordUtils.HH), + "日志内容"); + + // = + + // 保存错误信息 + NullPointerException nullPointerException = new NullPointerException("报错啦, null 异常啊"); + // 记录日志 + AnalysisRecordUtils.record(fileInfo, ThrowableUtils.getThrowable(nullPointerException)); + } + + /** + * DevLoggerUtils、FileRecordUtils 工具类 + */ + private void logRecord() { + try { + String s = null; + s.indexOf("c"); + } catch (NullPointerException e) { + + // = DevLoggerUtils 使用方法 = + + // 保存的路径 + String fileName = LOG_SD_PATH + System.currentTimeMillis() + ".log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fileName); + // = + // 保存自定义头部、底部信息 + DevLoggerUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log"); + // = + // 保存的路径 + fileName = LOG_SD_PATH + System.currentTimeMillis() + "_orgs.log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fileName); + + // 保存日志信息 + DevLoggerUtils.saveLog("日志内容", LOG_SD_PATH, System.currentTimeMillis() + ".log"); + // 保存日志信息 + DevLoggerUtils.saveLog("日志内容", "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log"); + + // = FileRecordUtils 使用方法 = + + FileRecordUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true); + + FileRecordUtils.saveLog("日志内容", "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true); + } + } +} diff --git a/app/src/main/java/com/dev/utils/shape/ShapeUse.java b/app/src/main/java/com/dev/utils/shape/ShapeUse.java new file mode 100644 index 0000000000..bdb8206647 --- /dev/null +++ b/app/src/main/java/com/dev/utils/shape/ShapeUse.java @@ -0,0 +1,53 @@ +package com.dev.utils.shape; + +import android.graphics.drawable.GradientDrawable; +import android.support.v4.content.ContextCompat; +import android.view.View; +import android.widget.Button; + +import com.dev.R; + +import dev.DevUtils; +import dev.utils.app.ImageViewUtils; +import dev.utils.app.ShapeUtils; +import dev.utils.app.StateListUtils; + +/** + * detail: ShapeUtils 使用方法 + * @author Ttt + */ +class ShapeUse { + + private void shapeUse() { + Button vid_btn1 = null; + + // 默认就设置背景色 + ShapeUtils.Builder builder = new ShapeUtils.Builder(); + builder.setRadiusLeft(10f).setColor(R.color.black); + ImageViewUtils.setBackground(vid_btn1, builder.build().getDrawable()); + + // 设置点击效果 + GradientDrawable drawable1 = ShapeUtils.newBuilder(10f, R.color.black).setStroke(5, R.color.green).build().getDrawable(); + GradientDrawable drawable2 = ShapeUtils.newBuilder(10f, R.color.sky_blue).setStroke(5, R.color.grey).build().getDrawable(); + + ImageViewUtils.setBackground(vid_btn1, StateListUtils.newSelector(drawable2, drawable1)); // 设置点击 View 背景变色, 不用写 shape xml 文件 + vid_btn1.setTextColor(StateListUtils.createColorStateList(R.color.red, R.color.white)); // 设置点击字体变色 + + // 设置渐变 + View vid_view1 = null; + // int[] colors = new int[]{ Color.RED, Color.BLUE, Color.GREEN }; + + int[] colors = new int[3]; + colors[0] = ContextCompat.getColor(DevUtils.getContext(), R.color.black); + colors[1] = ContextCompat.getColor(DevUtils.getContext(), R.color.sky_blue); + colors[2] = ContextCompat.getColor(DevUtils.getContext(), R.color.orange); + + // ShapeUtils.newBuilderToGradient(GradientDrawable.Orientation.BR_TL, colors).build().setDrawable(vid_view1); + + GradientDrawable drawable = ShapeUtils.newBuilderToGradient(GradientDrawable.Orientation.BR_TL, colors).build().getDrawable(); + // drawable.setGradientType(GradientDrawable.LINEAR_GRADIENT); // 线性渐变, 这是默认设置 + // drawable.setGradientType(GradientDrawable.RADIAL_GRADIENT); // 放射性渐变, 以开始色为中心 + drawable.setGradientType(GradientDrawable.SWEEP_GRADIENT); // 扫描线式的渐变 + ImageViewUtils.setBackground(vid_view1, drawable); + } +} diff --git a/app/src/main/java/com/dev/utils/share/ShareUse.java b/app/src/main/java/com/dev/utils/share/ShareUse.java new file mode 100644 index 0000000000..20a43acdeb --- /dev/null +++ b/app/src/main/java/com/dev/utils/share/ShareUse.java @@ -0,0 +1,59 @@ +package com.dev.utils.share; + +import android.content.Context; + +import dev.DevUtils; +import dev.utils.app.share.SPUtils; +import dev.utils.app.share.SharedUtils; + +/** + * detail: SharedPreferences 使用方法 + * @author Ttt + */ +public final class ShareUse { + + private ShareUse() { + } + + private void shareUse() { + // 具体实现方法 基于 PreferenceImpl 实现 + + // 存在可调用的方法 IPreference + + // SharedUtils 二次分装 SPUtils, 直接调用 + + // 在DevUtils.init 中初始化了, 实际可以不调用 + SharedUtils.init(DevUtils.getContext()); + + SharedUtils.put("aa", "aa"); + SharedUtils.put("ac", 123); + + // =========== + // = SPUtils = + // =========== + + // 想要自定义 模式, 名字等 + SPUtils.getPreference(DevUtils.getContext()).put("aa", 1); + SPUtils.getPreference(DevUtils.getContext(), "xxx").put("aa", 1); + SPUtils.getPreference(DevUtils.getContext(), "xxxxx", Context.MODE_PRIVATE).put("aa", 1); + + +// // 默认值如下 +// switch (type) { +// case INTEGER: +// return preferences.getInt(key, -1); +// case FLOAT: +// return preferences.getFloat(key, -1f); +// case BOOLEAN: +// return preferences.getBoolean(key, false); +// case LONG: +// return preferences.getLong(key, -1L); +// case STRING: +// return preferences.getString(key, null); +// case STRING_SET: +// return preferences.getStringSet(key, null); +// default: // 默认取出String类型的数据 +// return null; +// } + } +} diff --git a/app/src/main/java/com/dev/utils/snackbar/SnackbarUse.java b/app/src/main/java/com/dev/utils/snackbar/SnackbarUse.java new file mode 100644 index 0000000000..2a61661f19 --- /dev/null +++ b/app/src/main/java/com/dev/utils/snackbar/SnackbarUse.java @@ -0,0 +1,406 @@ +package com.dev.utils.snackbar; + +import android.app.Activity; +import android.graphics.Color; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.support.design.widget.Snackbar; +import android.support.v4.app.Fragment; +import android.text.TextUtils; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.TextView; + +import com.dev.R; + +import dev.utils.app.SnackbarUtils; + +/** + * detail: Snackbar 使用方法 + * @author Ttt + */ +public final class SnackbarUse { + + private SnackbarUse() { + } + + // 日志 TAG + private static final String TAG = SnackbarUse.class.getSimpleName(); + + View view; + Window window; + Fragment fragment; + Activity activity; + View.OnClickListener clickListener; + + int viewId; + View targetView; + TextView newTextView; + + public void snackbarUse() { + + // ================================================ + // = 只能通过以下四种方式 获取 SnackbarUtils 对象 = + // ================================================ + + SnackbarUtils.with(view); + + SnackbarUtils.with(window); + + SnackbarUtils.with(fragment); + + SnackbarUtils.with(activity); + + // ================ + // = 获取相关方法 = + // ================ + + // = 获取 View = + + // 获取 Snackbar 底层 View + View snackbarView = SnackbarUtils.with(view).getSnackbarView(); + + // 获取 Snackbar TextView(snackbar_text) - 左侧 文本TextView + TextView textView = SnackbarUtils.with(view).getTextView(); + + // 获取 Snackbar Action Button(snackbar_action) - 右侧 Button + Button actionButton = SnackbarUtils.with(view).getActionButton(); + + // = + + // 获取 Snackbar 对象 + Snackbar snackbar = SnackbarUtils.with(view).getSnackbar(); + + // 获取 View 阴影边距大小 - View 自带阴影 + int shadowMargin = SnackbarUtils.with(view).getShadowMargin(); + + // 获取 是否自动计算边距 (如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示) + boolean autoCalc = SnackbarUtils.with(view).isAutoCalc(); // 只有调用 above / bellow 该属性才有意义 + + // 获取 Snackbar 显示效果样式配置信息 + SnackbarUtils.StyleBuilder styleBuilder = SnackbarUtils.with(view).getStyle(); + + // ================ + // = 设置相关方法 = + // ================ + + // 设置 View 阴影边距大小 + SnackbarUtils.with(view).setShadowMargin(2); + + // 设置是否自动计算边距 (如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示) + SnackbarUtils.with(view).setAutoCalc(true); // 只有调用 above / bellow 该属性才有意义 + + // 设置 Snackbar 显示效果样式 + SnackbarUtils snackbarUtils = SnackbarUtils.with(view).setStyle(style); + + // = 快捷设置指定样式效果的 Snackbar = + + // 设置 Snackbar 显示效果为 当前 SnackbarUtils 对象使用的样式 + Snackbar snackbar1 = SnackbarUtils.with(view).setSnackbarStyle(snackbar); + + // 设置 Snackbar 显示效果为 自定义样式效果 + Snackbar snackbar2 = SnackbarUtils.with(view).setSnackbarStyle(snackbar, style); + + // = 设置 Action Button 文案等 = + + // 设置 Snackbar Action Button(snackbar_action) 文案 + SnackbarUtils.with(view).setAction(R.string.app_name); + + // 设置 Snackbar Action Button(snackbar_action) 文案 - 支持格式化字符串 + SnackbarUtils.with(view).setAction(R.string.app_name, "1", 2); + + // 设置 Snackbar Action Button(snackbar_action) 文案 + SnackbarUtils.with(view).setAction("撤销"); + + // 设置 Snackbar Action Button(snackbar_action) 文案 - 支持格式化字符串 + SnackbarUtils.with(view).setAction("撤销 - %s", "3"); + + // 设置 Snackbar Action Button(snackbar_action) 文案以及点击事件 + SnackbarUtils.with(view).setAction(clickListener, R.string.app_name); + + // 设置 Snackbar Action Button(snackbar_action) 文案以及点击事件 + SnackbarUtils.with(view).setAction(clickListener, "撤销"); + + // = 设置 事件相关 = + + // 设置 Snackbar 展示完成 及 隐藏完成 的监听 + SnackbarUtils.with(view).setCallback(new Snackbar.Callback() { + @Override + public void onShown(Snackbar sb) { + super.onShown(sb); + // Snackbar 显示 + } + + @Override + public void onDismissed(Snackbar transientBottomBar, int event) { + super.onDismissed(transientBottomBar, event); + // Snackbar 关闭 + } + }); + + // ============ + // = 操作方法 = + // ============ + + // = 关闭 = + + // 关闭显示 Snackbar + SnackbarUtils.with(view).dismiss(); + + // 关闭显示 Snackbar, 但不销毁 Snackbar + SnackbarUtils.with(view).dismiss(false); + + // = 显示 - 支持 String、R.string.xx 以及格式化字符串 = + + // 显示 Short Snackbar + SnackbarUtils.with(view).showShort("已收藏该消息!"); + + // 显示 Long Snackbar + SnackbarUtils.with(view).showLong("已收藏该消息!"); + + // 显示 Indefinite Snackbar (无限时, 一直显示) + SnackbarUtils.with(view).showIndefinite("已收藏该消息!"); + + // = 显示区域 = + + // 设置是否自动计算边距 (如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示) + // setAutoCalc 只有调用 above / bellow 该属性才有意义 + + // 设置 Snackbar 显示在指定 View 的上方, 并且向上边距 20 + SnackbarUtils.with(view).above(targetView, 20); + + // 设置 Snackbar 显示在指定 View 的下方, 并且向下边距 5 + SnackbarUtils.with(view).bellow(targetView, 5); + + // 向 Snackbar 布局中添加 View (Google 不建议, 复杂的布局应该使用 DialogFragment 进行展示) + SnackbarUtils.with(view).addView(newTextView, 0); + + // 向 Snackbar 布局中添加 View (Google 不建议, 复杂的布局应该使用 DialogFragment 进行展示) + SnackbarUtils.with(view).addView(viewId, 1); + + // = 结合使用 = + + // 只有调用了 showXxx, 才会进行设置样式, 并且显示 + SnackbarUtils.with(view) + .addView(viewId, 0) + .setStyle(new NightStyle()) + .setAction(new View.OnClickListener() { + @Override + public void onClick(View v) { + + } + }, "撤销") + .bellow(targetView, 0) + .setCallback(new Snackbar.Callback() { + @Override + public void onDismissed(Snackbar transientBottomBar, int event) { + } + }).setAutoCalc(true) + .showShort("已收藏该消息!"); + + // = + + // 通过已有样式创建 StyleBuilder 并修改样式效果使用 + SnackbarUtils.StyleBuilder styleBuilder1 = new SnackbarUtils.StyleBuilder(style); + styleBuilder1.setActionColor(Color.RED); + SnackbarUtils.with(view).setStyle(styleBuilder1).showShort("已收藏该消息!"); + + // 修改默认样式中的部分展示效果 + SnackbarUtils snackbarUtils1 = SnackbarUtils.with(view); + SnackbarUtils.StyleBuilder styleBuilder2 = snackbarUtils1.getStyle(); + styleBuilder2.setActionColor(Color.BLACK); + snackbarUtils1.setStyle(styleBuilder2); + } + + /** + * detail: 自定义样式 - 可参照下方实现方法, 进行配置 + * @author Ttt + */ + class NightStyle extends SnackbarUtils.Style { + @Override + public int getTextColor() { + return Color.WHITE; + } + + @Override + public float getRootAlpha() { + return 0.5f; + } + } + + /** + * detail: 样式相关 + * @author Ttt + */ + SnackbarUtils.Style style = new SnackbarUtils.Style() { + + // ============ + // = RootView = + // ============ + + /** + * RootView 的重心 + * @return + */ + public int getRootGravity() { + return 0; + } + + /** + * RootView 背景圆角大小 + * @return + */ + public float getRootCornerRadius() { + return 0f; + } + + /** + * RootView 背景着色颜色 + * @return + */ + public int getRootBackgroundTintColor() { + return 0; + } + + /** + * RootView 背景图片 + * @return + */ + public Drawable getRootBackground() { + return null; + } + + /** + * RootView margin 边距 - new int[] { left, top, right, bottom } + * @return + */ + public int[] getRootMargin() { + return null; + } + + /** + * RootView 透明度 + * @return + */ + public float getRootAlpha() { + return 1.0f; + } + + // = snackbar_text TextView 相关 = + + /** + * TextView 的重心 + * @return + */ + public int getTextGravity() { + return 0; + } + + /** + * TextView 文本颜色 + * @return + */ + public int getTextColor() { + return 0; + } + + /** + * TextView 字体大小 + * @return + */ + public float getTextSize() { + return 0f; + } + + /** + * TextView 最大行数 + * @return + */ + public int getTextMaxLines() { + return 0; + } + + /** + * TextView Ellipsize 效果 + * @return + */ + public TextUtils.TruncateAt getTextEllipsize() { + return null; + } + + /** + * TextView 字体样式 + * @return + */ + public Typeface getTextTypeface() { + return null; + } + + /** + * TextView padding 边距 - new int[] { left, top, right, bottom } + * @return + */ + public int[] getTextPadding() { + return null; + } + + // = snackbar_action Button 相关 = + + /** + * Action Button 的重心 + * @return + */ + public int getActionGravity() { + return 0; + } + + /** + * Action Button 文本颜色 + * @return + */ + public int getActionColor() { + return 0; + } + + /** + * Action Button 字体大小 + * @return + */ + public float getActionSize() { + return 0f; + } + + /** + * Action Button padding 边距 - new int[] { left, top, right, bottom } + * @return + */ + public int[] getActionPadding() { + return null; + } + + /** + * Action Button 背景圆角大小 + * @return + */ + public float getActionCornerRadius() { + return 0f; + } + + /** + * Action Button 背景着色颜色 + * @return + */ + public int getActionBackgroundTintColor() { + return 0; + } + + /** + * Action Button 背景图片 + * @return + */ + public Drawable getActionBackground() { + return null; + } + }; +} diff --git a/app/src/main/java/com/dev/utils/text/TextCalcUse.java b/app/src/main/java/com/dev/utils/text/TextCalcUse.java new file mode 100644 index 0000000000..6dd0ff1414 --- /dev/null +++ b/app/src/main/java/com/dev/utils/text/TextCalcUse.java @@ -0,0 +1,63 @@ +package com.dev.utils.text; + +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import dev.DevUtils; +import dev.utils.app.TextViewUtils; +import dev.utils.app.logger.DevLogger; + +/** + * detail: 计算字体宽度、高度 + * @author Ttt + */ +public final class TextCalcUse { + + private TextCalcUse() { + } + + // 日志 TAG + private static final String TAG = TextCalcUse.class.getSimpleName(); + + /** + * 计算字体宽度、高度 + */ + protected void textCalcUse() { + LinearLayout vid_linear = null; + // 打印信息 + for (int i = 0, len = vid_linear.getChildCount(); i < len; i++) { + View view = vid_linear.getChildAt(i); + if (view != null && view instanceof TextView) { + printInfo((TextView) view); + } + } + +// // 计算第几位超过宽度(600) +// int pos = TextViewUtils.calcTextWidth(vid_tv.getPaint(), "测试内容", 600); + + TextView tv = new TextView(DevUtils.getContext()); + // 获取字体高度 + TextViewUtils.getTextHeight(tv); + // 获取字体大小 + TextViewUtils.reckonTextSize(90); // 获取字体高度为90的字体大小 + } + + // = + + /** + * 打印信息 + * @param textView + */ + private void printInfo(TextView textView) { + StringBuilder builder = new StringBuilder(); + builder.append("内容: " + textView.getText().toString()); + builder.append("\n高度: " + TextViewUtils.getTextHeight(textView)); + builder.append("\n偏移高度: " + TextViewUtils.getTextTopOffsetHeight(textView)); + builder.append("\n宽度: " + TextViewUtils.getTextWidth(textView)); + builder.append("\n字体大小: " + textView.getTextSize()); + builder.append("\n计算字体大小: " + TextViewUtils.reckonTextSize(TextViewUtils.getTextHeight(textView))); + // 打印日志 + DevLogger.dTag(TAG, builder.toString()); + } +} diff --git a/app/src/main/java/com/dev/utils/thread/ThreadUse.java b/app/src/main/java/com/dev/utils/thread/ThreadUse.java new file mode 100644 index 0000000000..9363f3563c --- /dev/null +++ b/app/src/main/java/com/dev/utils/thread/ThreadUse.java @@ -0,0 +1,41 @@ +package com.dev.utils.thread; + +import dev.utils.common.thread.DevThreadManager; +import dev.utils.common.thread.DevThreadPool; + +/** + * detail: 线程使用方法 + * @author Ttt + */ +public final class ThreadUse { + + private ThreadUse() { + } + + /** + * 线程使用方法 + */ + private void threadUse() { + Runnable runnable = new Runnable() { + @Override + public void run() { + + } + }; + + // = 优先判断 10个线程数, 的线程池是否存在, 不存在则创建, 存在则复用 = + DevThreadManager.getInstance(10).execute(runnable); + + // 与上面 传入 int 是完全不同的线程池 + DevThreadManager.getInstance("10").execute(runnable); + + // 可以先增加配置 + DevThreadManager.putConfig("QPQP", new DevThreadPool(DevThreadPool.DevThreadPoolType.CALC_CPU)); + // 使用配置的信息 + DevThreadManager.getInstance("QPQP").execute(runnable); + + DevThreadManager.putConfig("QQQQQQ", 10); + // 使用配置的信息 + DevThreadManager.getInstance("QQQQQQ").execute(runnable); + } +} diff --git a/app/src/main/java/com/dev/utils/timer/TimerUse.java b/app/src/main/java/com/dev/utils/timer/TimerUse.java new file mode 100644 index 0000000000..5b81ef869e --- /dev/null +++ b/app/src/main/java/com/dev/utils/timer/TimerUse.java @@ -0,0 +1,145 @@ +package com.dev.utils.timer; + +import android.os.Handler; +import android.os.Message; + +import dev.utils.app.assist.manager.TimerManager; +import dev.utils.app.logger.DevLogger; + +/** + * detail: 定时器使用方法 + * @author Ttt + */ +public final class TimerUse { + + private TimerUse() { + } + + // 日志 TAG + private static final String TAG = TimerUse.class.getSimpleName(); + // 创建定时器 + TimerManager.AbsTimer absTimer; + // 通知常量 + private final int NOTIFY = 100; + + /** + * 定时器使用方法 + */ + private void timerUse() { + +// // 创建定时器 ( 立即执行、无限循环、通知默认 what ) +// public static TimerManager.AbsTimer createTimer(Handler handler, long period) { +// return createTimer(handler, TimerManager.AbsTimer.TIMER_NOTIFY_WHAT, 0L, period, -1); +// } +// +// // 创建定时器 ( 无限循环、通知默认 what ) +// public static TimerManager.AbsTimer createTimer(Handler handler, long delay, long period) { +// return createTimer(handler, TimerManager.AbsTimer.TIMER_NOTIFY_WHAT, delay, period, -1); +// } +// +// // 创建定时器 ( 立即执行、通知默认 what ) +// public static TimerManager.AbsTimer createTimer(Handler handler, long period, int triggerLimit) { +// return createTimer(handler, TimerManager.AbsTimer.TIMER_NOTIFY_WHAT, 0L, period, triggerLimit); +// } +// +// // 创建定时器 ( 立即执行、无限循环 ) +// public static TimerManager.AbsTimer createTimer(Handler handler, int what, long period) { +// return createTimer(handler, what, 0L, period, -1); +// } +// +// // 创建定时器 ( 无限循环 ) +// public static TimerManager.AbsTimer createTimer(Handler handler, int what, long delay, long period) { +// return createTimer(handler, what, delay, period, -1); +// } +// +// // 创建定时器 ( 立即执行 ) +// public static TimerManager.AbsTimer createTimer(Handler handler, int what, long period, int triggerLimit) { +// return createTimer(handler, what, 0L, period, triggerLimit); +// } +// +// /** +// * 创建定时器 +// * @param handler 通知的Handler +// * @param what 通知的what +// * @param delay 延迟时间 - 多少毫秒后开始执行 +// * @param period 循环时间 - 每隔多少秒执行一次 +// * @param triggerLimit 触发次数上限(-1 表示无限循环) +// * @return +// */ +// public static TimerManager.AbsTimer createTimer(Handler handler, int what, long delay, long period, int triggerLimit) { +// return new TimerManager.TimerTask(handler, what, delay, period, triggerLimit); +// } + + // 初始化定时器任务 + absTimer = TimerManager.createTimer(new Handler() { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + + // 获取触发次数 + int number = absTimer.getTriggerNumber(); + // 触发次数 + if (number == 1) { + DevLogger.dTag(TAG, "第一次触发, 0.5秒延迟"); + } else { + DevLogger.dTag(TAG, "每隔2秒触发一次, 触发次数: " + number); + } + } + }, NOTIFY, 500L, 2000L, -1); + // 开始定时 + absTimer.startTimer(); + + +// Handler handler = new Handler() { +// @Override +// public void handleMessage(Message msg) { +// super.handleMessage(msg); +// +// switch (msg.what) { +// case NOTIFY: +// // 获取触发次数 +// DevLogger.dTag(TAG, "已经触发次数: " + absTimer.getTriggerNumber()); +// DevLogger.dTag(TAG, "触发上限次数: " + absTimer.getTriggerLimit()); +// // 判断是否触发结束 +// if (absTimer.isTriggerEnd()) { +// DevLogger.dTag(TAG, "触发结束"); +// } +// break; +// } +// } +// }; +// +// // 配置参数 - 意思是 一开始0秒直接触发第一次, 然后后面每隔60秒触发一次, 通过Handler通知 NOTIFY 常量 (-1表示无限次) +// absTimer.setTriggerLimit(-1).setTime(0, 60 * 1000).setNotifyWhat(NOTIFY); +// +// // 配置参数 - 一秒钟后进行触发, 然后每隔1秒循环触发(但是触发一次 TriggerLimit 限制了次数), 并通过设置的Handler通知 对应传入的 What +// absTimer.setHandler(handler).setTriggerLimit(1).setTime(1000, 1000).setNotifyWhat(NOTIFY); +// +// // 配置参数 - 3秒钟后进行触发, 然后每隔3秒循环触发(但是触发10次 TriggerLimit 限制了次数), 并通过设置的Handler通知 对应传入的 What, 并且开始定时器 +// absTimer.setHandler(handler).setTriggerLimit(10).setTime(3000, 3000).setNotifyWhat(NOTIFY).startTimer(); +// +// // 开始运行定时器 +// absTimer.startTimer(); +// +// // 关闭定时器 +// absTimer.closeTimer(); +// +// // 判断是否运行中 +// absTimer.isRunTimer(); +// +// // 关闭所有符合对应的标记id的定时器任务 +// TimerManager.closeMark(id); +// +// // 关闭所有符合对应的字符串标记的定时器任务 +// TimerManager.closeMark("mark"); +// +// // 关闭所有无限循环的任务 +// TimerManager.closeInfiniteTask(); +// +// // 关闭全部任务 +// TimerManager.closeAll(); +// +// // 回收资源 - 回收需要回收的 +// TimerManager.gc(); + } +} diff --git a/app/src/main/java/com/dev/utils/toast/DevToastUse.java b/app/src/main/java/com/dev/utils/toast/DevToastUse.java new file mode 100644 index 0000000000..e6ce62a45d --- /dev/null +++ b/app/src/main/java/com/dev/utils/toast/DevToastUse.java @@ -0,0 +1,232 @@ +package com.dev.utils.toast; + +import android.app.Application; +import android.graphics.Color; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.View; + +import com.dev.R; + +import dev.utils.app.toast.toaster.DevToast; +import dev.utils.app.toast.toaster.IToast; + +/** + * detail: Toast 使用方法 + * @author Ttt + */ +public final class DevToastUse { + + private DevToastUse() { + } + + // ======== + // = 配置 = + // ======== + + private static View view = null; + private static int viewId = 0; + + /** + * Toast 配置相关 + */ + private void toastConfig(Application application) { + // 初始化 Toast - DevUtils 内部已经调用 + DevToast.init(application); // 必须调用 + + // 初始化 Toast 样式 - 全局通用 + // DevToast.initStyle(new IToast.Style() {}); // 可以实现 IToast.Style 接口, 参照 DefaultToastStyle + + // 当 Toast 内容为 null 时, 显示的内容 + DevToast.setNullText("text is null"); + + // 是否设置 Handler 显示 Toast - 默认 true, 支持子线程显示 Toast + DevToast.setIsHandler(true); + + // 设置文本长度限制, 超过设置的位数则 为 LENGTH_LONG + DevToast.setTextLength(15); + + // 支持自定义 View - 可不配置, 默认使用系统 Toast View + DevToast.setView(view); + DevToast.setView(viewId); + + // 配置 Toast 过滤, 判断是否显示 Toast、以及内容处理 + // DevToast.initToastFilter(new IToast.Filter() {}); + + // 恢复默认配置 + DevToast.reset(); + } + + // = 使用 = + + /** + * Toast 使用方法 + */ + public static void toastUse() { + // 显示 Toast + DevToast.show(view); + DevToast.show(R.string.app_name); + DevToast.show("Toast"); // initStyle - Toast + + // 使用特殊样式 - 默认统一全局样式, style 则为 这个 Toast 单独为这个样式 + DevToast.style(new TempStyle()).show("tempStyle - Toast"); + + // 获取 当前全局使用的样式 + DevToast.getToastStyle(); + + // 获取默认样式 + DevToast.defaultStyle(); + + // 关闭正在显示的 Toast + DevToast.cancel(); + + // 不同效果, 可通过实现 IToast.Style 自定义样式并初始化 initStyle/style 查看效果 + + // 默认不设置 initStyle, 会使用 defaultStyle + } + + /** + * 自定义实现样式 + */ + private static class TempStyle implements IToast.Style { + + /** + * Toast 的重心 + * @return + */ + @Override + public int getGravity() { + return 0; + } + + /** + * X轴偏移 + * @return + */ + @Override + public int getXOffset() { + return 0; + } + + /** + * Y轴偏移 + * @return + */ + @Override + public int getYOffset() { + return 0; + } + + /** + * 获取水平边距 + * @return + */ + @Override + public int getHorizontalMargin() { + return 0; + } + + /** + * 获取垂直边距 + * @return + */ + @Override + public int getVerticalMargin() { + return 0; + } + + /** + * Toast Z轴坐标阴影 + * @return + */ + @Override + public int getZ() { + return 0; + } + + /** + * 圆角大小 + * @return + */ + @Override + public float getCornerRadius() { + return 5f; + } + + /** + * 背景着色颜色 + * @return + */ + @Override + public int getBackgroundTintColor() { + return 0xB2000000; + } + + /** + * 背景图片 + * @return + */ + @Override + public Drawable getBackground() { + return null; + } + + // = TextView 相关 = + + /** + * 文本颜色 + * @return + */ + @Override + public int getTextColor() { + return Color.WHITE; + } + + /** + * 字体大小 + * @return + */ + @Override + public float getTextSize() { + return 16f; + } + + /** + * 最大行数 + * @return + */ + @Override + public int getMaxLines() { + return 0; + } + + /** + * Ellipsize 效果 + * @return + */ + @Override + public TextUtils.TruncateAt getEllipsize() { + return null; + } + + /** + * 字体样式 + * @return + */ + @Override + public Typeface getTypeface() { + // return Typeface.create("sans-serif-condensed", Typeface.NORMAL); + return null; + } + + /** + * TextView padding 边距 - new int[] { left, top, right, bottom } + * @return + */ + @Override + public int[] getPadding() { + return new int[]{25, 10, 25, 10}; + } + } +} diff --git a/app/src/main/java/com/dev/utils/toast/ToastTintUse.java b/app/src/main/java/com/dev/utils/toast/ToastTintUse.java new file mode 100644 index 0000000000..c21ac38905 --- /dev/null +++ b/app/src/main/java/com/dev/utils/toast/ToastTintUse.java @@ -0,0 +1,210 @@ +package com.dev.utils.toast; + +import android.graphics.Color; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.Gravity; + +import dev.utils.app.toast.ToastTintUtils; + +/** + * detail: Toast 使用方法 + * @author Ttt + */ +public final class ToastTintUse { + + private ToastTintUse() { + } + + // ======== + // = 配置 = + // ======== + + private static ToastTintUtils.Style style; + private static Drawable iconDrawable; + + /** + * Toast 配置相关 + */ + private void toastConfig() { + // 获取默认样式 + ToastTintUtils.getDefaultStyle(); + + // 获取 Normal 样式 + ToastTintUtils.getNormalStyle(); + // 设置 Normal 样式 + ToastTintUtils.setNormalStyle(style); + + // 获取 Error 样式 + ToastTintUtils.getErrorStyle(); + // 设置 Error 样式 + ToastTintUtils.setErrorStyle(style); + // 获取 Error 样式 小图标 + ToastTintUtils.getErrorDrawable(); + + // 获取 Warning 样式 + ToastTintUtils.getWarningStyle(); + // 设置 Warning 样式 + ToastTintUtils.setWarningStyle(style); + // 获取 Warning 样式 小图标 + ToastTintUtils.getWarningDrawable(); + + // 获取 Success 样式 + ToastTintUtils.getSuccessStyle(); + // 设置 Success 样式 + ToastTintUtils.setSuccessStyle(style); + // 获取 Success 样式 小图标 + ToastTintUtils.getSuccessDrawable(); + + // 获取 Info 样式 + ToastTintUtils.getInfoStyle(); + // 设置 Info 样式 + ToastTintUtils.setInfoStyle(style); + // 获取 Info 样式 小图标 + ToastTintUtils.getInfoDrawable(); + + // 是否使用配置 - 如 Gravity、HorizontalMargin、VerticalMargin + ToastTintUtils.setUseConfig(true); + + // 设置 Gravity + ToastTintUtils.setGravity(Gravity.BOTTOM, 0, 0); + + // 当 Toast 内容为 null 时, 显示的内容 + ToastTintUtils.setNullText("text is null"); + + // 是否设置 Handler 显示 Toast - 默认 true, 支持子线程显示 Toast + ToastTintUtils.setIsHandler(true); + + // 设置 HorizontalMargin、VerticalMargin 边距 + ToastTintUtils.setMargin(0f, 0f); + + // 配置 Toast 过滤, 判断是否显示 Toast、以及内容处理 + // ToastTintUtils.setToastFilter(new ToastTintUtils.Filter() {}); + + // 恢复默认配置 + ToastTintUtils.reset(); + } + + // = 使用 = + + /** + * Toast 使用方法 + */ + public static void toastUse() { + // 显示 Success 样式 Toast + ToastTintUtils.success("Success Style Toast"); + + // 显示 Error 样式 Toast + ToastTintUtils.error("Error Style Toast"); + + // 显示 Info 样式 Toast + ToastTintUtils.info("Info Style Toast"); + + // 显示 Normal 样式 Toast + ToastTintUtils.normal("Normal Style Toast"); + + // 显示 Warning 样式 Toast + ToastTintUtils.warning("Warning Style Toast"); + + // 显示 Custom 样式 Toast + ToastTintUtils.custom(style, "Custom Style Toast"); + + // 显示 Custom 样式 Toast, 自定义小图标 + ToastTintUtils.custom(new TempStyle(), "Custom Style Toast", iconDrawable); + } + + /** + * 自定义实现样式 + * {@link ToastTintUtils.SuccessStyle} + * {@link ToastTintUtils.ErrorStyle} + * {@link ToastTintUtils.InfoStyle} + * {@link ToastTintUtils.WarningStyle} + * {@link ToastTintUtils.NormalStyle} + * {@link ToastTintUtils.DefaultStyle} + */ + private static class TempStyle implements ToastTintUtils.Style { + + /** + * 文本颜色 + * @return + */ + @Override + public int getTextColor() { + return Color.WHITE; + } + + /** + * 字体大小 + * @return + */ + @Override + public float getTextSize() { + return 16f; + } + + /** + * 背景着色颜色 + * @return + */ + @Override + public int getBackgroundTintColor() { + return 0; + } + + /** + * 背景图片 + * @return + */ + @Override + public Drawable getBackground() { + return null; + } + + /** + * 最大行数 + * @return + */ + @Override + public int getMaxLines() { + return 0; + } + + /** + * Ellipsize 效果 + * @return + */ + @Override + public TextUtils.TruncateAt getEllipsize() { + return null; + } + + /** + * 字体样式 + * @return + */ + @Override + public Typeface getTypeface() { + // return Typeface.create("sans-serif-condensed", Typeface.NORMAL); + return null; + } + + /** + * 获取图标着色颜色 + * @return + */ + @Override + public int getTintIconColor() { + return Color.WHITE; + } + + /** + * 是否渲染图标 - getTintIconColor() 着色渲染 + * @return + */ + @Override + public boolean isTintIcon() { + return false; + } + } +} diff --git a/app/src/main/java/com/dev/utils/wifi/WifiHotUse.java b/app/src/main/java/com/dev/utils/wifi/WifiHotUse.java new file mode 100644 index 0000000000..cf964737e4 --- /dev/null +++ b/app/src/main/java/com/dev/utils/wifi/WifiHotUse.java @@ -0,0 +1,71 @@ +package com.dev.utils.wifi; + +import android.net.wifi.WifiConfiguration; +import android.os.Build; + +import dev.DevUtils; +import dev.utils.app.wifi.WifiHotUtils; + +/** + * detail: Wifi 热点使用方法 + * @author Ttt + */ +public final class WifiHotUse { + + private WifiHotUse() { + } + + /** + * Wifi 热点使用方法 + */ + private void wifiHotUse() { + + // 所需权限 + // + // + // + // + // + // + + final WifiHotUtils wifiHotUtils = new WifiHotUtils(DevUtils.getContext()); + + // 有密码 + WifiConfiguration wifiConfiguration = WifiHotUtils.createWifiConfigToAp("WifiHot_AP", "123456789"); + + // 无密码 + wifiConfiguration = WifiHotUtils.createWifiConfigToAp("WifiHot_AP", null); + + // 开启热点(兼容8.0) 7.1 跳转到热点页面, 需手动开启(但是配置信息使用上面的 WifiConfig) + wifiHotUtils.stratWifiAp(wifiConfiguration); + + // 关闭热点 + wifiHotUtils.closeWifiAp(); + + // = 8.0 特殊处理 = + + // 8.0 以后热点是针对应用开启, 并且必须强制使用随机生成的 WifiConfig 信息, 无法替换 + + // 如果应用开启了热点, 然后后台清空内存, 对应的热点会关闭, 应用开启的热点是系统随机的, 不影响系统设置中的热点配置信息 + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + wifiHotUtils.setOnWifiAPListener(new WifiHotUtils.OnWifiAPListener() { + @Override + public void onStarted(WifiConfiguration wifiConfig) { + String ssid = wifiHotUtils.getApWifiSSID(); + String pwd = wifiHotUtils.getApWifiPwd(); + } + + @Override + public void onStopped() { + + } + + @Override + public void onFailed(int reason) { + + } + }); + } + } +} diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index b824ebdd48..ddf6635d30 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..3e63f580e2 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/build.gradle b/build.gradle index bbad8e8123..f22ced6e6c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,28 +1,42 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. +apply from: rootProject.file('file/gradle/config.gradle') buildscript { - + repositories { + + // AliRepo 阿里仓库服务 https://maven.aliyun.com/mvn/view + // 阿里 Gradle 源 + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/jcenter' } + google() jcenter() } - dependencies { - classpath 'com.android.tools.build:gradle:3.0.0' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + dependencies { + classpath 'com.android.tools.build:gradle:3.5.1' + // Mavan + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' + // bintray + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' } } allprojects { repositories { + // 所有的 model 都优先使用阿里源 + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/jcenter' } + google() jcenter() } -// // https://blog.csdn.net/u012416928/article/details/47356887 -// // 打印错误信息 // gradle.projectsEvaluated { // tasks.withType(JavaCompile) { // options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" @@ -33,3 +47,8 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir } + +// 全局编码设置 +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} \ No newline at end of file diff --git a/file/DevUtils.png b/file/DevUtils.png new file mode 100644 index 0000000000..a34b7284d6 Binary files /dev/null and b/file/DevUtils.png differ diff --git a/file/bintray/bintrayUploadAndroid.gradle b/file/bintray/bintrayUploadAndroid.gradle new file mode 100644 index 0000000000..90f0f68643 --- /dev/null +++ b/file/bintray/bintrayUploadAndroid.gradle @@ -0,0 +1,119 @@ +apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.jfrog.bintray' + +// load properties +Properties properties = new Properties() +File localPropertiesFile = project.file("local.properties"); +if (localPropertiesFile.exists()) { + properties.load(localPropertiesFile.newDataInputStream()) +} +File projectPropertiesFile = project.file("project.properties"); +if (projectPropertiesFile.exists()) { + properties.load(projectPropertiesFile.newDataInputStream()) +} + +// read properties +def projectName = properties.getProperty("project.name") +def projectGroupId = properties.getProperty("project.groupId") +def projectArtifactId = properties.getProperty("project.artifactId") +def projectVersionName = android.defaultConfig.versionName +def projectPackaging = properties.getProperty("project.packaging") +def projectSiteUrl = properties.getProperty("project.siteUrl") +def projectGitUrl = properties.getProperty("project.gitUrl") + +def developerId = properties.getProperty("developer.id") +def developerName = properties.getProperty("developer.name") +def developerEmail = properties.getProperty("developer.email") + +def bintrayUser = properties.getProperty("bintray.user") +def bintrayApikey = properties.getProperty("bintray.apikey") + +def javadocName = properties.getProperty("javadoc.name") + +group = projectGroupId + +// This generates POM.xml with proper parameters +install { + repositories.mavenInstaller { + pom { + project { + name projectName + groupId projectGroupId + artifactId projectArtifactId + version projectVersionName + packaging projectPackaging + url projectSiteUrl + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + scm { + connection projectGitUrl + developerConnection projectGitUrl + url projectSiteUrl + } + } + } + } +} + +// This generates sources.jar +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + source = android.sourceSets.main.java.srcDirs +// classpath += configurations.compile + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + failOnError false +} + +// This generates javadoc.jar +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives javadocJar + archives sourcesJar +} + +// javadoc configuration +javadoc { + options { + encoding "UTF-8" + charSet 'UTF-8' + author true + version projectVersionName + links "http://docs.oracle.com/javase/7/docs/api" + title javadocName + } +// options.addStringOption('Xdoclint:none', '-quiet') +} + +// bintray configuration +bintray { + user = bintrayUser + key = bintrayApikey + configurations = ['archives'] + pkg { + repo = "maven" + name = projectName + websiteUrl = projectSiteUrl + vcsUrl = projectGitUrl + licenses = ["Apache-2.0"] + publish = true + } +} \ No newline at end of file diff --git a/file/bintray/bintrayUploadJava.gradle b/file/bintray/bintrayUploadJava.gradle new file mode 100644 index 0000000000..b291b4bf83 --- /dev/null +++ b/file/bintray/bintrayUploadJava.gradle @@ -0,0 +1,112 @@ +apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.jfrog.bintray' + +// load properties +Properties properties = new Properties() +File localPropertiesFile = project.file("local.properties"); +if (localPropertiesFile.exists()) { + properties.load(localPropertiesFile.newDataInputStream()) +} +File projectPropertiesFile = project.file("project.properties"); +if (projectPropertiesFile.exists()) { + properties.load(projectPropertiesFile.newDataInputStream()) +} + +// read properties +def projectName = properties.getProperty("project.name") +def projectGroupId = properties.getProperty("project.groupId") +def projectArtifactId = properties.getProperty("project.artifactId") +def projectVersionName = version +def projectPackaging = properties.getProperty("project.packaging") +def projectSiteUrl = properties.getProperty("project.siteUrl") +def projectGitUrl = properties.getProperty("project.gitUrl") + +def developerId = properties.getProperty("developer.id") +def developerName = properties.getProperty("developer.name") +def developerEmail = properties.getProperty("developer.email") + +def bintrayUser = properties.getProperty("bintray.user") +def bintrayApikey = properties.getProperty("bintray.apikey") + +def javadocName = properties.getProperty("javadoc.name") + +group = projectGroupId + +// This generates POM.xml with proper parameters +install { + repositories.mavenInstaller { + pom { + project { + name projectName + groupId projectGroupId + artifactId projectArtifactId + version projectVersionName + packaging projectPackaging + url projectSiteUrl + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + scm { + connection projectGitUrl + developerConnection projectGitUrl + url projectSiteUrl + } + } + } + } +} + +// This generates sources.jar +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource +} + +// This generates javadoc.jar +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives javadocJar + archives sourcesJar +} + +// javadoc configuration +javadoc { + options { + encoding "UTF-8" + charSet 'UTF-8' + author true + version projectVersionName + links "http://docs.oracle.com/javase/7/docs/api" + title javadocName + } + options.addStringOption('Xdoclint:none', '-quiet') +} + +// bintray configuration +bintray { + user = bintrayUser + key = bintrayApikey + configurations = ['archives'] + pkg { + repo = "maven" + name = projectName + websiteUrl = projectSiteUrl + vcsUrl = projectGitUrl + licenses = ["Apache-2.0"] + publish = true + } +} \ No newline at end of file diff --git a/file/diagram.zip b/file/diagram.zip new file mode 100644 index 0000000000..ddb8632253 Binary files /dev/null and b/file/diagram.zip differ diff --git a/file/gradle/app_build.gradle b/file/gradle/app_build.gradle new file mode 100644 index 0000000000..8255446744 --- /dev/null +++ b/file/gradle/app_build.gradle @@ -0,0 +1,37 @@ +apply { + plugin 'com.android.application' +} + +android { + + compileSdkVersion versions.compileSdkVersion + + defaultConfig { + minSdkVersion versions.minSdkVersion + versionCode versions.versionCode + versionName versions.versionName + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + lintOptions { + abortOnError false + } + + compileOptions { + sourceCompatibility versions.javaVersion + targetCompatibility versions.javaVersion + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation deps.android.appcompat_v7 + implementation deps.android.support_v4 + implementation deps.android.design +} \ No newline at end of file diff --git a/file/gradle/app_lib_build.gradle b/file/gradle/app_lib_build.gradle new file mode 100644 index 0000000000..2fb13da1ef --- /dev/null +++ b/file/gradle/app_lib_build.gradle @@ -0,0 +1,21 @@ +apply { + plugin "com.android.library" +} + +android { + + compileSdkVersion versions.compileSdkVersion + + defaultConfig { + minSdkVersion versions.minSdkVersion + } + + lintOptions { + abortOnError false + } + + compileOptions { + sourceCompatibility versions.javaVersion + targetCompatibility versions.javaVersion + } +} \ No newline at end of file diff --git a/file/gradle/config.gradle b/file/gradle/config.gradle new file mode 100644 index 0000000000..dd68c3eac9 --- /dev/null +++ b/file/gradle/config.gradle @@ -0,0 +1,102 @@ +ext { + + files = [ + // Android App 配置文件地址 + app_gradle : 'file/gradle/app_build.gradle', + // Android Lib 通用配置文件地址 + app_lib_gradle : 'file/gradle/app_lib_build.gradle', + // Android Bintray Upload + app_bintray_upload : 'file/bintray/bintrayUploadAndroid.gradle', + + // Java Lib 通用配置文件地址 + java_lib_gradle : 'file/gradle/java_lib_build.gradle', + // Java Bintray Upload + java_bintray_upload: 'file/bintray/bintrayUploadJava.gradle', + ] + + versions = [ + + // 版本号 + versionCode : 1, + // 版本名 + versionName : '1.0.0', + // 支持最小的 SDK + minSdkVersion : 14, + // 编译 SDK 版本 + compileSdkVersion : 29, + // 向前兼容 SDK 版本 + targetSdkVersion : 29, + // support 包 Lib 版本 + supportLibVersion : '28.0.0', + // java 版本 + javaVersion : JavaVersion.VERSION_1_8, + + // =========== + // = Dev Lib = + // =========== + + // 版本号 + dev_app_versionCode : 182, + // 版本名 + dev_app_versionName : '1.8.2', + + // 版本号 + dev_assist_versionCode: 102, + // 版本名 + dev_assist_versionName: '1.0.2', + + // 版本号 + dev_java_versionCode : 103, + // 版本名 + dev_java_version : '1.0.3', + + // 版本号 + dev_other_versionCode : 100, + // 版本名 + dev_other_versionName : '1.0.0', + ] + + deps = [ + 'android' : [ + appcompat_v7 : "com.android.support:appcompat-v7:${versions.supportLibVersion}", + support_v4 : "com.android.support:support-v4:${versions.supportLibVersion}", + design : "com.android.support:design:${versions.supportLibVersion}", + multidex : "com.android.support:multidex:1.0.3", + constraint_layout: "com.android.support.constraint:constraint-layout:1.1.3", + ], + 'androidx': [ + appcompat : "androidx.appcompat:appcompat:1.0.0", + support_v4 : "androidx.legacy:legacy-support-v4:1.0.0", + design : "com.google.android.material:material:1.0.0", + multidex : "androidx.multidex:multidex:2.0.0", + constraint_layout: "androidx.constraintlayout:constraintlayout:1.1.3", + ], + 'lib' : [ + // https://github.com/google/gson + gson : "com.google.code.gson:gson:2.8.5", + // https://github.com/nostra13/Android-Universal-Image-Loader + imageloader : "com.nostra13.universalimageloader:universal-image-loader:1.9.5", + // https://github.com/bumptech/glide + glide : "com.github.bumptech.glide:glide:4.9.0", + glide_compiler : "com.github.bumptech.glide:compiler:4.9.0", + // https://github.com/greenrobot/EventBus + eventbus : "org.greenrobot:eventbus:3.1.1", + // https://github.com/square/okio + okio : "com.squareup.okio:okio:2.4.0", + // https://github.com/square/okhttp/ + okhttp3 : "com.squareup.okhttp3:okhttp:4.1.0", + // okhttp 日志拦截器 https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor + okhttp3_logging: "com.squareup.okhttp3:logging-interceptor:4.1.1", + // https://github.com/jeasonlzy/okhttp-OkGo + okgo : "com.lzy.net:okgo:3.0.4", + // 下载管理和上传管理扩展 + okserver : "com.lzy.net:okserver:2.0.5", + ], + 'dev' : [ + // https://github.com/afkT/DevUtils + dev_app : "com.afkt:DevApp:${versions.dev_app_versionName}", + dev_assist: "com.afkt:DevAssist:${versions.dev_assist_versionName}", + dev_java : "com.afkt:DevJava:${versions.dev_java_version}", + ] + ] +} \ No newline at end of file diff --git a/file/gradle/java_lib_build.gradle b/file/gradle/java_lib_build.gradle new file mode 100644 index 0000000000..ae11ebcb3c --- /dev/null +++ b/file/gradle/java_lib_build.gradle @@ -0,0 +1,6 @@ +apply { + plugin "java-library" +} + +sourceCompatibility = versions.javaVersion +targetCompatibility = versions.javaVersion \ No newline at end of file diff --git a/file/icon.zip b/file/icon.zip new file mode 100644 index 0000000000..c521d1121a Binary files /dev/null and b/file/icon.zip differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fab5eef69c..f48908d354 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Sep 07 12:58:58 CST 2018 +#Tue Aug 27 16:07:28 CST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/DevLibUtils/.gitignore b/lib/DevApp/.gitignore similarity index 100% rename from DevLibUtils/.gitignore rename to lib/DevApp/.gitignore diff --git a/lib/DevApp/CHANGELOG.md b/lib/DevApp/CHANGELOG.md new file mode 100644 index 0000000000..ed26ae4ee3 --- /dev/null +++ b/lib/DevApp/CHANGELOG.md @@ -0,0 +1,105 @@ +Change Log +========== + +Version 1.8.2 *(2019-10-18)* +---------------------------- + + * `[Add]` TextViewUtils#setMinLines、setMaxEms、setMinEms、setEms、setMaxLength、setMaxLengthAndText、setInputType、setImeOptions + + * `[Add]` EditTextUtils#setInputType、setImeOptions + + * `[Add]` JSONObjectUtils#isJSON + + * `[Add]` ViewUtils#setLayerType、setAllCaps、setAlpha、getAlpha、setScrollContainer、setNextFocusForwardId、setNextFocusDownId、setNextFocusLeftId、setNextFocusRightId、setNextFocusUpId、setRotation、setRotationX、setRotationY、setScaleX、setScaleY、setTextAlignment、setTextDirection、setPivotX、setPivotY、setTranslationX、setTranslationY + +Version 1.8.1 *(2019-10-13)* +---------------------------- + + * `[Add]` EditTextUtils#addTextChangedListener、removeTextChangedListener、setTexts + + * `[Add]` TextViewUtils#getHint、getHints、getHintTextColors、setHintTextColor、setHintTextColors、getTextColors、setTextColor、setTextColors、setGravity、setHint、setAutoLinkMask、setEllipsize、setMaxLines、setLines + + * `[Add]` ViewUtils#getMinimumHeight、setMinimumHeight、getMinimumWidth、setMinimumWidth + + * `[Add]` ImageViewUtils#getAdjustViewBounds、setAdjustViewBounds、getMaxHeight、setMaxHeight、getMaxWidth、setMaxWidth + +Version 1.8.0 *(2019-10-09)* +---------------------------- + + * `[Update]` TextViewUtils#calcTextWidth 使用二分法优化处理 + + * `[Add]` TextViewUtils#calcTextLine、TextViewUtils#getPaint、TextViewUtils#getTextWidth + + * `[Add]` DialogUtils#dismiss(DialogFragment) + + * `[Add]` ViewUtils#inflate + + * `[Add]` NumberUtils#getMultiple、getMultipleI、getMultipleD、getMultipleL、getMultipleF + +Version 1.7.9 *(2019-09-19)* +---------------------------- + + * `[Update]` compileSdkVersion 29 Android Q + + * `[Update]` AppCommonUtils#convertSDKVersion + + * `[Update]` ImageUtils#getImageType、ImageUtils#isImage modify to isImageFormats + + * `[Update]` 修改部分方法 void 返回值 (返回当前对象, 方便链式调用) + + * `[Add]` AppCommonUtils#isQ + + * `[Add]` BitmapUtils#isImage + + * `[Add]` ListenerUtils#setOnLongClicks + + * `[Add]` ImageUtils#isICO、ImageUtils#isTIFF + + * `[Add]` ViewUtils#getTag、setTag + + +Version 1.7.8 *(2019-09-12)* +---------------------------- + + * `[Add]` ImageViewUtils#setBackgroundResources + + * `[Add]` ViewUtils#getParent + + * `[Add]` ConvertUtils#convert + + * `[Fix]` DialogUtils#showDialog、closeDialog try catch + + +Version 1.7.7 *(2019-08-25)* +---------------------------- + + * `[New]` Support for AndroidX + + * `[Add]` DevCommonUtils#subSetSymbol + + * `[Add]` ScreenUtils#setWindowSecure + + * `[Add]` ViewUtils#set/getMargin、set/getPadding, ViewUtils#set/getLayoutParams + + * `[Add]` AndroidManifest.xml FileProvider config + + * `[Update]` Update AppUtils、IntentUtils、UriUtils 使用 FileProvider authority 处理 + + * `[Fix]` Reflect2Utils#getDeclaredFieldParent fieldNumber param 判断处理 + + +Version 1.7.6 *(2019-08-02)* +---------------------------- + + * `[New]` SpannableStringUtils + + * `[Add]` ViewUtils#set/getCompoundDrawables、set/getCompoundDrawablePadding + + * `[Add]` ImageUtils#setBounds + + +Version 1.0.0~1.7.5 *(2019-07-26)* +---------------------------- + + * 整个工具类 review code, 并规范代码风格、检测注释、代码间距等 + diff --git a/lib/DevApp/README.md b/lib/DevApp/README.md new file mode 100644 index 0000000000..d39460baf2 --- /dev/null +++ b/lib/DevApp/README.md @@ -0,0 +1,3245 @@ + +## Gradle + +```java +implementation 'com.afkt:DevApp:1.8.2' + +// AndroidX +implementation 'com.afkt:DevAppX:1.8.2' +``` + +## 目录结构 + +``` +- dev.utils | 根目录 + - app | APP 相关工具类 + - anim | 动画工具类 + - assist | 辅助类 + - manager | 管理器 如: TimerManager + - cache | 缓存工具类 + - camera1 | 摄像头相关 android.hardware.Camera + - image | 图片相关处理 + - info | APP 信息、PackageInfo 等 + - logger | 日志库 DevLogger + - permission | 权限工具类 + - player | 多媒体 ( 视频、音频 ) 播放封装 + - share | SharedPreferences 封装 + - toast | Toast + - toaster | Toaster 处理无通知权限 + - wifi | Wifi、热点工具类 + - common | Java 工具类, 不依赖 android api + - assist | 各种快捷辅助类 + - search | 搜索相关 ( 文件搜索等 ) + - cipher | 编 / 解码工具类 + - encrypt | 加密工具类 + - random | 随机概率算法工具类 + - thread | 线程相关 + - validator | 数据校验工具类 +``` + + +## 使用 + +> ##### 只需要在 Application 中调用 `DevUtils.init()` 进行初始化就行 + +## 事项 + +- 内部存在两个日志工具类 (工具类内部调用),对外使用 [DevLogger](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/logger/DevLogger.md) + +```java +// 整个工具类内部日志信息,都通过以下两个工具类输出打印,并且通过 DevUtils.openLog() 控制开关 + +// dev.utils.app - APP 日志打印工具类 +LogPrintUtils +// dev.utils.common - Java Common 日志打印工具类 +JCLogUtils +``` + +- 开启日志 +```java +// 打开 lib 内部日志 - 线上 (release) 环境,不调用方法就行 +DevUtils.openLog(); +// 标示 debug 模式 +DevUtils.openDebug(); +``` + +- 工具类部分模块配置与使用 - [Use and Config](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/USE_CONFIG.md) + +- 部分 API 更新不及时或有遗漏等,`具体以对应的工具类为准` + +- [检测代码规范、注释内容排版,API 文档生成](https://github.com/afkT/JavaDoc) + +- [Change Log](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/CHANGELOG.md) + +- [View 链式调用快捷设置 Helper 类](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/ViewHelper.java) + +- [Dev 工具类链式调用 Helper 类](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/DevHelper.java) + +## API + + +- dev.utils | 根目录 + - [app](#devutilsapp) | APP 相关工具类 + - [anim](#devutilsappanim) | 动画工具类 + - [assist](#devutilsappassist) | 辅助类 + - [manager](#devutilsappassistmanager) | 管理器 如: TimerManager + - [cache](#devutilsappcache) | 缓存工具类 + - [camera1](#devutilsappcamera1) | 摄像头相关 android.hardware.Camera + - [image](#devutilsappimage) | 图片相关处理 + - [info](#devutilsappinfo) | APP 信息、PackageInfo 等 + - [logger](#devutilsapplogger) | 日志库 DevLogger + - [permission](#devutilsapppermission) | 权限工具类 + - [player](#devutilsappplayer) | 多媒体 ( 视频、音频 ) 播放封装 + - [share](#devutilsappshare) | SharedPreferences 封装 + - [toast](#devutilsapptoast) | Toast + - [toaster](#devutilsapptoasttoaster) | Toaster 处理无通知权限 + - [wifi](#devutilsappwifi) | Wifi、热点工具类 + - [common](#devutilscommon) | Java 工具类, 不依赖 android api + - [assist](#devutilscommonassist) | 各种快捷辅助类 + - [search](#devutilscommonassistsearch) | 搜索相关 ( 文件搜索等 ) + - [cipher](#devutilscommoncipher) | 编 / 解码工具类 + - [encrypt](#devutilscommonencrypt) | 加密工具类 + - [random](#devutilscommonrandom) | 随机概率算法工具类 + - [thread](#devutilscommonthread) | 线程相关 + - [validator](#devutilscommonvalidator) | 数据校验工具类 + + + + +## **`dev.utils.app`** + + +* **无障碍功能工具类 ->** [AccessibilityUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/AccessibilityUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getService | 获取 AccessibilityService 对象 | +| setService | 设置 AccessibilityService 对象 | +| checkAccessibility | 检查是否开启无障碍功能 | +| isAccessibilitySettingsOn | 判断是否开启无障碍功能 | +| printAccessibilityEvent | 打印 AccessibilityEvent 信息日志 | +| findFocus | 查找符合条件的节点 | +| findAccessibilityNodeInfosByText | 查找符合条件的节点 | +| findAccessibilityNodeInfosByViewId | 查找符合条件的节点 | +| performClick | 点击指定的节点 | +| performLongClick | 长按指定的节点 | +| preformActionBack | 触发返回键 | +| preformActionHome | 触发 Home 键 | +| preformActionPowerDialog | 启动长按电源按钮 Dialog | +| preformActionLockScreen | 锁定屏幕 ( 非锁屏 ) | +| preformActionTakeScreenshot | 截屏 | +| preformActionNotifications | 打开通知栏 | +| preformActionRecents | 最近打开应用列表 | +| preformActionQuickSettings | 打开设置 | +| preformActionSplitScreen | 分屏 | +| preformAction | 模拟对应 Action 操作 | +| performGlobalAction | 模拟全局对应 Action 操作 | + + +* **Activity 工具类 ( 包含 Activity 控制管理 ) ->** [ActivityUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ActivityUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getActivity | 通过 Context 获取 Activity | +| isFinishing | 判断 Activity 是否关闭 | +| isActivityExists | 判断是否存在指定的 Activity | +| startHomeActivity | 回到桌面 ( 同点击 Home 键效果 ) | +| getLauncherActivity | 获取 Launcher activity | +| getActivityIcon | 获取 Activity 对应的 icon | +| getActivityLogo | 获取 Activity 对应的 logo | +| getActivityToLauncher | 获取对应包名应用启动的 Activity | +| getLauncherCategoryHomeToResolveInfo | 获取系统桌面信息 | +| getLauncherCategoryHomeToPackageName | 获取系统桌面信息 - packageName | +| getLauncherCategoryHomeToActivityName | 获取系统桌面信息 - activityName | +| getLauncherCategoryHomeToPackageAndName | 获取系统桌面信息 - package/activityName | +| getOptionsBundle | 设置跳转动画 | +| getManager | 获取 ActivityUtils 管理实例 | +| getActivityStacks | 获取 Activity 栈 | +| addActivity | 添加 Activity | +| removeActivity | 移除 Activity | +| currentActivity | 获取最后一个 ( 当前 ) Activity | +| finishActivity | 关闭最后一个 ( 当前 ) Activity | +| existActivitys | 检测是否包含指定的 Activity | +| finishAllActivityToIgnore | 结束全部 Activity 除忽略的 Activity 外 | +| finishAllActivity | 结束所有 Activity | +| appExit | 退出应用程序 | +| restartApplication | 重启 APP | + + +* **ADB shell 工具类 ->** [ADBUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ADBUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isDeviceRooted | 判断设备是否 root | +| requestRoot | 请求 Root 权限 | +| isGrantedRoot | 判断 APP 是否授权 Root 权限 | +| getAppList | 获取 APP 列表 ( 包名 ) | +| getInstallAppList | 获取 APP 安装列表 ( 包名 ) | +| getUserAppList | 获取用户安装的应用列表 ( 包名 ) | +| getSystemAppList | 获取系统应用列表 ( 包名 ) | +| getEnableAppList | 获取启用的应用列表 ( 包名 ) | +| getDisableAppList | 获取禁用的应用列表 ( 包名 ) | +| getAppListToFilter | 获取包名包含字符串 xxx 的应用列表 | +| isInstalledApp | 判断是否安装应用 | +| getAppInstallPath | 查看应用安装路径 | +| clearAppDataCache | 清除应用数据与缓存 - 相当于在设置里的应用信息界面点击了「清除缓存」和「清除数据」 | +| getAppMessage | 查看应用详细信息 | +| getVersionCode | 获取 APP versionCode | +| getVersionName | 获取 APP versionName | +| installApp | 安装应用 | +| installAppSilent | 静默安装应用 | +| uninstallApp | 卸载应用 | +| uninstallAppSilent | 静默卸载应用 | +| getActivityToLauncher | 获取对应包名应用启动的 Activity | +| getWindowCurrent | 获取当前显示的 Window | +| getWindowCurrent2 | 获取当前显示的 Window | +| getWindowCurrentToPackage | 获取对应包名显示的 Window | +| getActivityCurrent | 获取当前显示的 Activity | +| getActivitys | 获取 Activity 栈 | +| getActivitysToPackage | 获取对应包名的 Activity 栈 | +| getActivitysToPackageLists | 获取对应包名的 Activity 栈 ( 处理成 List) - 最新的 Activity 越靠后 | +| isActivityTopRepeat | 判断 Activity 栈顶是否重复 | +| getActivityTopRepeatCount | 获取 Activity 栈顶重复总数 | +| getServices | 查看正在运行的 Services | +| startSelfApp | 启动自身应用 | +| startActivity | 跳转页面 Activity | +| startService | 启动服务 | +| stopService | 停止服务 | +| sendBroadcastToAll | 发送广播 ( 向所有组件发送 ) | +| sendBroadcast | 发送广播 | +| kill | 销毁进程 | +| sendTrimMemory | 收紧内存 | +| tap | 点击某个区域 | +| swipeClick | 按压某个区域 ( 点击 ) | +| swipe | 滑动到某个区域 | +| text | 输入文本 - 不支持中文 | +| keyevent | 触发某些按键 | +| screencap | 屏幕截图 | +| screenrecord | 录制屏幕 ( 以 mp4 格式保存到 /sdcard) | +| wifiConf | 查看连接过的 Wifi 密码 | +| wifiSwitch | 开启 / 关闭 Wifi | +| setSystemTime | 设置系统时间 | +| setSystemTime2 | 设置系统时间 | +| shutdown | 关机 ( 需要 root 权限 ) | +| reboot | 重启设备 ( 需要 root 权限 ) | +| rebootToRecovery | 重启引导到 recovery ( 需要 root 权限 ) | +| rebootToBootloader | 重启引导到 bootloader ( 需要 root 权限 ) | +| sendEventSlide | 发送事件滑动 | +| getSDKVersion | 获取 SDK 版本 | +| getAndroidVersion | 获取 Android 系统版本 | +| getModel | 获取设备型号 - 如 RedmiNote4X | +| getBrand | 获取设备品牌 | +| getDeviceName | 获取设备名 | +| getCpuAbiList | 获取 CPU 支持的 abi 列表 | +| getAppHeapsize | 获取每个应用程序的内存上限 | +| getBattery | 获取电池状况 | +| getDensity | 获取屏幕密度 | +| getScreenSize | 获取屏幕分辨率 | +| getDisplays | 获取显示屏参数 | +| getAndroidId | 获取 Android id | +| getIMEI | 获取 IMEI 码 | +| getIPAddress | 获取 IP 地址 | +| getMac | 获取 Mac 地址 | +| getCPU | 获取 CPU 信息 | +| getMeminfo | 获取内存信息 | +| setScreenSize | 设置屏幕大小 | +| resetScreen | 恢复原分辨率命令 | +| setDensity | 设置屏幕密度 | +| resetDensity | 恢复原屏幕密度 | +| setOverscan | 显示区域 ( 设置留白边距 ) | +| resetOverscan | 恢复原显示区域 | +| getScreenBrightnessMode | 获取亮度是否为自动获取 ( 自动调节亮度 ) | +| setScreenBrightnessMode | 设置亮度是否为自动获取 ( 自动调节亮度 ) | +| getScreenBrightness | 获取屏幕亮度值 | +| setScreenBrightness | 更改屏幕亮度值 ( 亮度值在 0-255 之间 ) | +| getScreenOffTimeout | 获取自动锁屏休眠时间 ( 单位毫秒 ) | +| setScreenOffTimeout | 设置自动锁屏休眠时间 ( 单位毫秒 ) | +| getGlobalAutoTime | 获取日期时间选项中通过网络获取时间的状态 | +| setGlobalAutoTime | 修改日期时间选项中通过网络获取时间的状态, 设置是否开启 | +| disableADB | 关闭 USB 调试模式 | +| putHiddenApi | 允许访问非 SDK API | +| deleteHiddenApi | 禁止访问非 SDK API | +| openAccessibility | 开启无障碍辅助功能 | +| closeAccessibility | 关闭无障碍辅助功能 | + + +* **AlarmManager( 全局定时器、闹钟 ) 工具类 ->** [AlarmUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/AlarmUtils.java) + +| 方法 | 注释 | +| :- | :- | +| startAlarmIntent | 开启一次性闹钟 | +| stopAlarmIntent | 关闭闹钟 | +| startAlarmService | 开启 Service 闹钟 | +| stopAlarmService | 关闭 Service 闹钟 | +| startAlarmForegroundService | 开启 ForegroundService 闹钟 | +| stopAlarmForegroundService | 关闭 ForegroundService 闹钟 | +| startAlarmBroadcast | 开启 Receiver 闹钟 | +| stopAlarmBroadcast | 关闭 Receiver 闹钟 | +| startAlarmActivity | 开启 Activity 闹钟 | +| stopAlarmActivity | 关闭 Activity 闹钟 | + + +* **日志记录分析工具类 ->** [AnalysisRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/AnalysisRecordUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化操作 ( 内部已调用 ) | +| record | 日志记录 | +| isHandler | 判断是否处理日志记录 | +| setHandler | 设置是否处理日志记录 | +| isAppendSpace | 判断是否追加空格 | +| setAppendSpace | 设置是否追加空格 | +| getLogFolderName | 获取日志文件名 | +| setLogFolderName | 设置日志文件夹名 | +| getLogStoragePath | 获取日志存储路径 | +| setLogStoragePath | 设置日志存储路径 | +| obtain | 获取日志记录分析文件对象 | +| getStoragePath | 获取存储路径 | +| getFileName | 获取日志文件名 | +| getFileFunction | 获取日志文件记录功能 | +| getFileIntervalTime | 获取日志文件记录间隔时间 | +| getFolderName | 获取日志文件夹名 | +| getLogPath | 获取日志文件地址 | +| getIntervalTimeFolder | 获取时间间隔所属的文件夹 | + + +* **APP 通用工具类 ->** [AppCommonUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/AppCommonUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getUUID | 获取设备唯一 UUID | +| getFormatRes | 获取 R.string 资源的格式化字符串 | +| getSDKVersion | 获取 SDK 版本 | +| isFroyo | 是否在 2.2 版本及以上 | +| isGingerbread | 是否在 2.3 版本及以上 | +| isGingerbreadMR1 | 是否在 2.3.3 版本及以上 | +| isHoneycomb | 是否在 3.0 版本及以上 | +| isHoneycombMR1 | 是否在 3.1 版本及以上 | +| isIceCreamSandwich | 是否在 4.0 版本及以上 | +| isIceCreamSandwichMR1 | 是否在 4.0.3 版本及以上 | +| isJellyBean | 是否在 4.1 版本及以上 | +| isJellyBeanMR1 | 是否在 4.2 版本及以上 | +| isJellyBeanMR2 | 是否在 4.3 版本及以上 | +| isKitkat | 是否在 4.4.2 版本及以上 | +| isLollipop | 是否在 5.0.1 版本及以上 | +| isM | 是否在 6.0 版本及以上 | +| isN | 是否在 7.0 版本及以上 | +| isN_MR1 | 是否在 7.1.1 版本及以上 | +| isO | 是否在 8.0 版本及以上 | +| isO_MR1 | 是否在 8.1 版本及以上 | +| isP | 是否在 9.0 版本及以上 | +| isQ | 是否在 10.0 版本及以上 | +| convertSDKVersion | 转换 SDK 版本 convertSDKVersion(14) = Android 4.0.0-2 | + + +* **APP (Android) 工具类 ->** [AppUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/AppUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getWindowManager | 获取 WindowManager | +| getSystemService | 获取 SystemService | +| getPackageManager | 获取 PackageManager | +| getAppPackageName | 获取 APP 包名 | +| getAppIcon | 获取 APP 图标 | +| getAppName | 获取 APP 应用名 | +| getAppVersionName | 获取 APP versionName | +| getAppVersionCode | 获取 APP versionCode | +| getAppPath | 获取 APP 安装包路径 /data/data/packageName/.apk | +| getAppSignature | 获取 APP Signature | +| getAppSignatureMD5 | 获取 APP 签名 MD5 值 | +| getAppSignatureSHA1 | 获取 APP 签名 SHA1 值 | +| getAppSignatureSHA256 | 获取 APP 签名 SHA256 值 | +| isAppDebug | 判断 APP 是否 debug 模式 | +| isAppRelease | 判断 APP 是否 release 模式 | +| isAppSystem | 判断 APP 是否系统 app | +| isAppForeground | 判断 APP 是否在前台 | +| isInstalledApp | 判断是否安装了 APP | +| isInstalledApp2 | 判断是否安装了 APP | +| installApp | 安装 APP( 支持 8.0) 的意图 | +| installAppSilent | 静默安装应用 | +| uninstallApp | 卸载应用 | +| uninstallAppSilent | 静默卸载应用 | +| launchApp | 打开 APP | +| launchAppDetailsSettings | 跳转到 APP 设置详情页面 | +| launchAppDetails | 跳转到 APP 应用商城详情页面 | +| openFile | 打开文件 | +| openFileByApp | 打开文件 - 指定应用 | +| openPDFFile | 打开 PDF 文件 | +| openWordFile | 打开 Word 文件 | +| openOfficeByWPS | 调用 WPS 打开 office 文档 | +| startSysSetting | 跳转到系统设置页面 | +| openWirelessSettings | 打开网络设置界面 | +| openGpsSettings | 打开 GPS 设置界面 | + + +* **音频管理工具类 ->** [AudioManagerUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/AudioManagerUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getAudioManager | 获取 AudioManager | +| getStreamMaxVolume | 获取指定声音流最大音量大小 | +| getStreamVolume | 获取指定声音流音量大小 | +| setStreamVolume | 设置指定声音流音量大小 | +| adjustVolumeLower | 控制手机音量, 调小一个单位 | +| adjustVolumeRaise | 控制手机音量, 调大一个单位 | +| adjustVolume | 控制手机音量, 调大或者调小一个单位 | +| adjustStreamVolumeLower | 控制指定声音流音量, 调小一个单位 | +| adjustStreamVolumeRaise | 控制指定声音流音量, 调大一个单位 | +| adjustStreamVolume | 控制指定声音流音量, 调大或者调小一个单位 | +| setStreamMuteByMusic | 设置媒体声音静音状态 | +| setStreamMuteByVoiceCall | 设置通话声音静音状态 | +| setStreamMuteBySystem | 设置系统声音静音状态 | +| setStreamMuteByRing | 设置来电响铃静音状态 | +| setStreamMuteByAlarm | 设置闹钟声音静音状态 | +| setStreamMuteByNotification | 设置通知声音静音状态 | +| setStreamMute | 设置指定声音流静音状态 | +| getMode | 获取当前的音频模式 | +| setMode | 设置当前的音频模式 | +| getRingerMode | 获取当前的铃声模式 | +| setRingerMode | 获取当前的铃声模式 | +| ringerSilent | 设置静音模式 ( 静音, 且无振动 ) | +| ringerVibrate | 设置震动模式 ( 静音, 但有振动 ) | +| ringerNormal | 设置正常模式 ( 正常声音, 振动开关由 setVibrateSetting 决定 ) | +| isDoNotDisturb | 判断是否授权 Do not disturb 权限 | +| setSpeakerphoneOn | 设置是否打开扩音器 ( 扬声器 ) | +| setMicrophoneMute | 设置是否让麦克风静音 | +| isSpeakerphoneOn | 判断是否打开扩音器 ( 扬声器 ) | +| isMicrophoneMute | 判断麦克风是否静音 | +| isMusicActive | 判断是否有音乐处于活跃状态 | +| isWiredHeadsetOn | 判断是否插入了耳机 | +| isBluetoothA2dpOn | 检查蓝牙 A2DP 音频外设是否已连接 | +| isBluetoothScoAvailableOffCall | 检查当前平台是否支持使用 SCO 的关闭调用用例 | +| isBluetoothScoOn | 检查通信是否使用蓝牙 SCO | +| setBluetoothScoOn | 设置是否使用蓝牙 SCO 耳机进行通讯 | +| startBluetoothSco | 启动蓝牙 SCO 音频连接 | +| stopBluetoothSco | 停止蓝牙 SCO 音频连接 | +| loadSoundEffects | 加载音效 | +| unloadSoundEffects | 卸载音效 | +| playSoundEffect | 播放音效 | +| abandonAudioFocus | 放弃音频焦点, 使上一个焦点所有者 ( 如果有 ) 接收焦点 | +| adjustSuggestedStreamVolume | 调整最相关的流的音量, 或者给定的回退流 | +| getParameters | 获取音频硬件指定 key 的参数值 | +| getVibrateSetting | 获取用户对振动类型的振动设置 | + + +* **状态栏相关工具类 ->** [BarUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/BarUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getStatusBarHeight | 获取状态栏高度 | +| setStatusBarVisibility | 设置状态栏是否显示 | +| isStatusBarVisible | 判断状态栏是否显示 | +| setStatusBarLightMode | 设置状态是否高亮模式 | +| addMarginTopEqualStatusBarHeight | 添加状态栏同等高度到 View 的顶部 | +| subtractMarginTopEqualStatusBarHeight | 添加状态栏同等高度到 View 的顶部 | +| setStatusBarColor | 设置状态栏颜色 | +| setStatusBarAlpha | 设置状态栏透明度 | +| setStatusBarCustom | 设置自定义状态栏 | +| setStatusBarColor4Drawer | 设置状态栏的颜色 | +| setStatusBarAlpha4Drawer | 设置状态栏透明度 | +| getActionBarHeight | 获取 ActionBase 高度 | +| setNotificationBarVisibility | 设置通知栏是否显示 | +| getNavBarHeight | 获取 NavigationView 高度 | +| setNavBarVisibility | 设置导航栏是否可见 ( 图标显示 ) | +| isNavBarVisible | 判断导航栏是否可见 | +| setNavBarColor | 设置导航栏颜色 | +| getNavBarColor | 设置导航栏颜色 | +| isSupportNavBar | 判断是否支持导航栏 | + + +* **亮度相关工具类 ->** [BrightnessUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/BrightnessUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isAutoBrightnessEnabled | 判断是否开启自动调节亮度 | +| setAutoBrightnessEnabled | 设置是否开启自动调节亮度 | +| getBrightness | 获取屏幕亮度 0-255 | +| setBrightness | 设置屏幕亮度 | +| setWindowBrightness | 设置窗口亮度 | +| getWindowBrightness | 获取窗口亮度 | + + +* **本应用数据清除管理工具类 ->** [CleanUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/CleanUtils.java) + +| 方法 | 注释 | +| :- | :- | +| cleanInternalCache | 清除内部缓存 - path /data/data/package/cache | +| cleanInternalFiles | 清除内部文件 - path /data/data/package/files | +| cleanInternalDbs | 清除内部数据库 - path /data/data/package/databases | +| cleanInternalDbByName | 根据名称清除数据库 - path /data/data/package/databases/dbName | +| cleanInternalSp | 清除内部 SP - path /data/data/package/shared_prefs | +| cleanExternalCache | 清除外部缓存 - path /storage/emulated/0/android/data/package/cache | +| cleanCustomDir | 清除自定义路径下的文件, 使用需小心请不要误删, 而且只支持目录下的文件删除 | +| cleanApplicationData | 清除本应用所有的数据 | + + +* **点击 ( 双击 ) 工具类 ->** [ClickUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ClickUtils.java) + +| 方法 | 注释 | +| :- | :- | +| addTouchArea | 增加控件的触摸范围, 最大范围只能是父布局所包含的的区域 | +| get | 获取对应功能模块点击辅助类 | +| remove | 移除对应功能模块点击辅助类 | +| isFastDoubleClick | 判断是否双击 ( 无效点击 - 短时间内多次点击 ) | +| initConfig | 初始化配置信息 | +| putConfig | 添加配置信息 | +| removeConfig | 移除配置信息 | +| getConfigTime | 获取配置时间 | +| removeRecord | 移除点击记录 | +| clearRecord | 清空全部点击记录 | +| setIntervalTime | 设置默认点击时间间隔 | +| reset | 重置处理 | + + +* **剪贴板相关工具类 ->** [ClipboardUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ClipboardUtils.java) + +| 方法 | 注释 | +| :- | :- | +| copyText | 复制文本到剪贴板 | +| getText | 获取剪贴板文本 | +| copyUri | 复制 URI 到剪贴板 | +| getUri | 获取剪贴板 URI | +| copyIntent | 复制意图到剪贴板 | +| getIntent | 获取剪贴板意图 | + + +* **ContentResolver 工具类 ->** [ContentResolverUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ContentResolverUtils.java) + +| 方法 | 注释 | +| :- | :- | +| notifyMediaStore | 通知刷新本地资源 | +| insertImageIntoMediaStore | 添加图片到系统相册 ( 包含原图、相册图, 会存在两张 ) - 想要一张, 直接调用 notifyMediaStore() | +| insertVideoIntoMediaStore | 添加视频到系统相册 | +| insertIntoMediaStore | 保存到系统相册 | + + +* **获取 CPU 信息工具类 ->** [CPUUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/CPUUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getProcessorsCount | 获取处理器的 Java 虚拟机的数量 | +| getSysCPUSerialNum | 获取手机 CPU 序列号 | +| getCpuInfo | 获取 CPU 信息 | +| getCpuModel | 获取 CPU 型号 | +| getMaxCpuFreq | 获取 CPU 最大频率 ( 单位 KHZ) | +| getMinCpuFreq | 获取 CPU 最小频率 ( 单位 KHZ) | +| getCurCpuFreq | 获取 CPU 当前频率 ( 单位 KHZ) | +| getCoresNumbers | 获取 CPU 核心数 | +| getCpuName | 获取 CPU 名字 | +| getCMDOutputString | 获取 CMD 指令回调数据 | + + +* **数据库工具类 ( 导入导出等 ) ->** [DBUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/DBUtils.java) + +| 方法 | 注释 | +| :- | :- | +| startExportDatabase | 导出数据库 | +| startImportDatabase | 导入数据库 | +| getDBPath | 获取数据库路径 | + + +* **设备相关工具类 ->** [DeviceUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/DeviceUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getDeviceInfo | 获取设备信息 | +| handlerDeviceInfo | 处理设备信息 | +| getBoard | 获取设备基板名称 | +| getBootloader | 获取设备引导程序版本号 | +| getBrand | 获取设备品牌 | +| getCPU_ABI | 获取支持的第一个指令集 | +| getCPU_ABI2 | 获取支持的第二个指令集 | +| getABIs | 获取支持的指令集 如: [arm64-v8a, armeabi-v7a, armeabi] | +| getSUPPORTED_32_BIT_ABIS | 获取支持的 32 位指令集 | +| getSUPPORTED_64_BIT_ABIS | 获取支持的 64 位指令集 | +| getDevice | 获取设备驱动名称 | +| getDisplay | 获取设备显示的版本包 ( 在系统设置中显示为版本号 ) 和 ID 一样 | +| getFingerprint | 获取设备的唯一标识, 由设备的多个信息拼接合成 | +| getHardware | 获取设备硬件名称, 一般和基板名称一样 (BOARD) | +| getHost | 获取设备主机地址 | +| getID | 获取设备版本号 | +| getModel | 获取设备型号 如 RedmiNote4X | +| getManufacturer | 获取设备厂商 如 Xiaomi | +| getProduct | 获取整个产品的名称 | +| getRadio | 获取无线电固件版本号, 通常是不可用的 显示 unknown | +| getTags | 获取设备标签, 如 release-keys 或测试的 test-keys | +| getTime | 获取设备时间 | +| getType | 获取设备版本类型 主要为 "user" 或 "eng". | +| getUser | 获取设备用户名 基本上都为 android-build | +| getSDKVersion | 获取 SDK 版本号 | +| getRelease | 获取系统版本号, 如 4.1.2 或 2.2 或 2.3 等 | +| getCodename | 获取设备当前的系统开发代号, 一般使用 REL 代替 | +| getIncremental | 获取系统源代码控制值, 一个数字或者 git hash 值 | +| getAndroidId | 获取 Android id | +| getBaseband_Ver | 获取基带版本 BASEBAND-VER | +| getLinuxCore_Ver | 获取内核版本 CORE-VER | +| isDeviceRooted | 判断设备是否 root | +| isAdbEnabled | 获取是否启用 ADB | +| getMacAddress | 获取设备 MAC 地址 | +| shutdown | 关机 ( 需要 root 权限 ) | +| reboot | 重启设备 ( 需要 root 权限 ) | +| rebootToRecovery | 重启引导到 recovery ( 需要 root 权限 ) | +| rebootToBootloader | 重启引导到 bootloader ( 需要 root 权限 ) | + + +* **Dialog 操作相关工具类 ->** [DialogUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/DialogUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getWindow | 获取 Dialog Window | +| getAttributes | 获取 Dialog Window LayoutParams | +| setAttributes | 设置 Dialog Window LayoutParams | +| setWidth | 设置 Dialog 宽度 | +| setHeight | 设置 Dialog 高度 | +| setWidthHeight | 设置 Dialog 宽度、高度 | +| setX | 设置 Dialog X 轴坐标 | +| setY | 设置 Dialog Y 轴坐标 | +| setXY | 设置 Dialog X、Y 轴坐标 | +| setGravity | 设置 Dialog Gravity | +| setDimAmount | 设置 Dialog 透明度 | +| setCancelable | 设置是否允许返回键关闭 | +| setCanceledOnTouchOutside | 设置是否允许点击其他地方自动关闭 | +| setCancelableAndTouchOutside | 设置是否允许 返回键关闭、点击其他地方自动关闭 | +| showDialog | 显示 Dialog | +| closeDialog | 关闭 Dialog | +| closeDialogs | 关闭多个 Dialog | +| closePopupWindow | 关闭 PopupWindow | +| closePopupWindows | 关闭多个 PopupWindow | +| createAlertDialog | 创建提示 Dialog ( 原生样式 ) | +| createProgressDialog | 创建加载中 Dialog ( 原生样式 ) | +| autoCloseDialog | 自动关闭 dialog | +| autoClosePopupWindow | 自动关闭 PopupWindow | +| createSingleChoiceListDialog | 创建单选列表样式 Dialog | +| createSingleChoiceDialog | 创建单选样式 Dialog | +| createMultiChoiceDialog | 创建多选样式 Dialog | +| createViewDialog | 创建自定义 View 样式 Dialog | + + +* **EditText 工具类 ->** [EditTextUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/EditTextUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getEditText | 获取 EditText | +| getText | 获取输入的内容 | +| getTextLength | 获取输入的内容长度 | +| setText | 设置内容 | +| setTexts | 设置多个 EditText 文本 | +| insert | 追加内容 ( 当前光标位置追加 ) | +| setMaxLength | 设置长度限制 | +| setMaxLengthAndText | 设置长度限制, 并且设置内容 | +| setCursorVisible | 设置是否显示光标 | +| getSelectionStart | 获取光标位置 | +| setSelectionToTop | 设置光标在第一位 | +| setSelectionToBottom | 设置光标在最后一位 | +| setSelection | 设置光标位置 | +| setInputType | 设置输入类型 | +| setImeOptions | 设置软键盘右下角按钮类型 | +| addTextChangedListener | 添加输入监听事件 | +| removeTextChangedListener | 移除输入监听事件 | +| setKeyListener | 设置 KeyListener | +| getLettersKeyListener | 获取 DigitsKeyListener ( 限制只能输入字母, 默认弹出英文输入法 ) | +| getNumberAndLettersKeyListener | 获取 DigitsKeyListener ( 限制只能输入字母和数字, 默认弹出英文输入法 ) | +| getNumberKeyListener | 获取 DigitsKeyListener ( 限制只能输入数字, 默认弹出数字列表 ) | +| createDigitsKeyListener | 创建 DigitsKeyListener | + + +* **文件记录工具类 ->** [FileRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/FileRecordUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化调用方法 ( 内部已调用 ) | +| saveErrorLog | 保存异常日志 | +| saveLog | 保存日志 | + + +* **手电筒工具类 ->** [FlashlightUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/FlashlightUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 FlashlightUtils 实例 | +| register | 注册摄像头 | +| unregister | 注销摄像头 | +| setFlashlightOn | 打开闪光灯 | +| setFlashlightOff | 关闭闪光灯 | +| isFlashlightOn | 是否打开闪光灯 | +| isFlashlightEnable | 是否支持手机闪光灯 | + + +* **Handler 工具类 ->** [HandlerUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/HandlerUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getMainHandler | 获取主线程 Handler | +| postRunnable | 在主线程 Handler 中执行任务 | +| removeRunnable | 在主线程 Handler 中清除任务 | + + +* **Html 工具类 ->** [HtmlUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/HtmlUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setHtmlText | 设置 Html 内容 | +| addHtmlColor | 为给定的字符串添加 HTML 颜色标记 | +| addHtmlBold | 为给定的字符串添加 HTML 加粗标记 | +| addHtmlColorAndBlod | 为给定的字符串添加 HTML 颜色标记并加粗 | +| addHtmlUnderline | 为给定的字符串添加 HTML 下划线 | +| addHtmlIncline | 为给定的字符串添加 HTML 字体倾斜 | +| keywordReplaceHtmlColor | 将给定的字符串中所有给定的关键字标色 | + + +* **ImageView 工具类 ->** [ImageViewUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ImageViewUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getImageView | 获取 ImageView | +| getAdjustViewBounds | 获取 ImageView 是否保持宽高比 | +| setAdjustViewBounds | 设置 ImageView 是否保持宽高比 | +| getMaxHeight | 获取 ImageView 最大高度 | +| setMaxHeight | 设置 ImageView 最大高度 | +| getMaxWidth | 获取 ImageView 最大宽度 | +| setMaxWidth | 设置 ImageView 最大宽度 | +| setBackground | 设置背景图片 | +| setBackgroundColor | 设置背景颜色 | +| setBackgroundResource | 设置背景资源 | +| setBackgroundTintList | 设置背景着色颜色 | +| setBackgroundTintMode | 设置背景着色模式 | +| setForeground | 设置前景图片 | +| setForegroundGravity | 设置前景重心 | +| setForegroundTintList | 设置前景着色颜色 | +| setForegroundTintMode | 设置前景着色模式 | +| setImageBitmap | 设置 ImageView Bitmap | +| setImageDrawable | 设置 ImageView Drawable | +| setImageResource | 设置 ImageView 资源 | +| setImageMatrix | 设置 ImageView Matrix | +| setImageTintList | 设置 ImageView 着色颜色 | +| setImageTintMode | 设置 ImageView 着色模式 | +| setScaleType | 设置 ImageView 缩放类型 | +| getBackground | 获取 View 背景 Drawable | +| getBackgroundTintList | 获取 View 背景着色颜色 | +| getBackgroundTintMode | 获取 View 背景着色模式 | +| getForeground | 获取 View 前景 Drawable | +| getForegroundGravity | 获取 View 前景重心 | +| getForegroundTintList | 获取 View 前景着色颜色 | +| getForegroundTintMode | 获取 View 前景着色模式 | +| getImageMatrix | 获取 ImageView Matrix | +| getImageTintList | 获取 ImageView 着色颜色 | +| getImageTintMode | 获取 ImageView 着色模式 | +| getScaleType | 获取 ImageView 缩放模式 | +| getDrawable | 获取 ImageView Drawable | +| setColorFilter | ImageView 着色处理 | +| setBackgroundResources | 设置 View 图片资源 | +| setImageResources | 设置 View 图片资源 | +| setImageBitmaps | 设置 View Bitmap | +| setImageDrawables | 设置 View Drawable | +| setScaleTypes | 设置 View 缩放模式 | +| getImageViewSize | 根据 ImageView 获适当的宽高 | + + +* **Intent 相关工具类 ->** [IntentUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/IntentUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getIntent | 获取 Intent | +| isIntentAvailable | 判断 Intent 是否可用 | +| getInstallAppIntent | 获取安装 APP( 支持 8.0) 的意图 | +| getUninstallAppIntent | 获取卸载 APP 的意图 | +| getLaunchAppIntent | 获取打开 APP 的意图 | +| getSystemSettingIntent | 获取跳转到系统设置的意图 | +| getLaunchAppDetailsSettingsIntent | 获取 APP 具体设置的意图 | +| getLaunchAppDetailIntent | 获取到应用商店 APP 详情界面的意图 | +| getShareTextIntent | 获取分享文本的意图 | +| getShareImageIntent | 获取分享图片的意图 | +| getComponentIntent | 获取其他应用组件的意图 | +| getShutdownIntent | 获取关机的意图 | +| getDialIntent | 获取跳至拨号界面意图 | +| getCallIntent | 获取拨打电话意图 | +| getSendSmsIntent | 获取发送短信界面的意图 | +| getCaptureIntent | 获取拍照的意图 | + + +* **Android 原生 JSONObject 工具类 ->** [JSONObjectUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/JSONObjectUtils.java) + +| 方法 | 注释 | +| :- | :- | +| toJson | 转换为 JSON 格式字符串 | +| fromJson | Object 转换 JSON 对象 | +| wrap | 包装转换 Object - {@link JSONObject#wrap(Object)} | +| stringJSONEscape | 字符串 JSON 转义处理 | +| isJSON | 判断字符串是否 JSON 格式 | + + +* **软键盘相关工具类 ->** [KeyBoardUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/KeyBoardUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setDelayMillis | 设置延迟时间 | +| openKeyboard | 打开软键盘 | +| closeKeyboard | 关闭软键盘 | +| closeKeyBoardSpecial | 关闭软键盘 - 特殊处理 | +| toggleKeyboard | 自动切换键盘状态, 如果键盘显示了则隐藏, 隐藏着显示 | +| judgeView | 设置某个 View 内所有非 EditText 的子 View OnTouchListener 事件 | +| isSoftInputVisible | 判断软键盘是否可见 | +| registerSoftInputChangedListener | 注册软键盘改变监听 | +| registerSoftInputChangedListener2 | 注册软键盘改变监听 | +| fixSoftInputLeaks | 修复软键盘内存泄漏 在 Activity.onDestroy() 中使用 | + + +* **锁屏管理工具类 ( 锁屏、禁用锁屏, 判断是否锁屏 ) ->** [KeyguardUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/KeyguardUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 KeyguardUtils 实例 | +| isKeyguardLocked | 是否锁屏 - android 4.1 以上支持 | +| isKeyguardSecure | 是否有锁屏密码 - android 4.1 以上支持 | +| inKeyguardRestrictedInputMode | 是否锁屏 | +| getKeyguardManager | 获取 KeyguardManager | +| setKeyguardManager | 设置 KeyguardManager | +| disableKeyguard | 屏蔽系统的屏保 | +| reenableKeyguard | 使能显示锁屏界面, 如果你之前调用了 disableKeyguard() 方法取消锁屏界面, 那么会马上显示锁屏界面 | +| release | 释放资源 | +| getKeyguardLock | 获取 KeyguardManager.KeyguardLock | +| setKeyguardLock | 设置 KeyguardManager.KeyguardLock | + + +* **语言工具类 ->** [LanguageUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/LanguageUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getSystemLanguage | 获取系统语言 | +| getSystemPreferredLanguage | 获取系统首选语言 | +| applyLanguage | 修改系统语言 (APP 多语言, 单独改变 APP 语言 ) | +| getSupportLanguages | 获取支持的语言 | +| putSupportLanguage | 添加支持的语言 | +| removeSupportLanguage | 移除支持的语言 | +| isSupportLanguage | 是否支持此语言 | +| getSupportLanguage | 获取支持语言 | + + +* **事件工具类 ->** [ListenerUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ListenerUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getTouchListener | 获取 View 设置的 OnTouchListener 事件对象 | +| getListenerInfo | 获取 View ListenerInfo 对象 ( 内部类 ) | +| getListenerInfoListener | 获取 View ListenerInfo 对象内部事件对象 | +| setOnClicks | 设置点击事件 | +| setOnLongClicks | 设置长按事件 | + + +* **定位相关工具类 ->** [LocationUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/LocationUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getLocation | 获取位置, 需要先判断是否开启了定位 | +| isGpsEnabled | 判断 GPS 是否可用 | +| isLocationEnabled | 判断定位是否可用 | +| openGpsSettings | 打开 GPS 设置界面 | +| register | 注册 - 使用完记得调用 {@link #unregister()} | +| unregister | 注销监听 | +| getAddress | 根据经纬度获取地理位置 | +| getCountryName | 根据经纬度获取所在国家 | +| getLocality | 根据经纬度获取所在地 | +| getStreet | 根据经纬度获取所在街道 | +| isBetterLocation | 判断是否更好的位置 | +| isSameProvider | 是否相同的提供者 | +| getLastKnownLocation | 获取最后一次保留的坐标 | +| onLocationChanged | 当坐标改变时触发此函数, 如果 Provider 传进相同的坐标, 它就不会被触发 | +| onStatusChanged | provider 的在可用、暂时不可用和无服务三个状态直接切换时触发此函数 | + + +* **Android Manifest 工具类 ->** [ManifestUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ManifestUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getMetaData | 获取 Application meta Data | +| getMetaDataInActivity | 获取 Activity meta Data | +| getMetaDataInService | 获取 Service meta Data | +| getMetaDataInReceiver | 获取 Receiver meta Data | +| getMetaDataInProvider | 获取 ContentProvider meta Data | +| getAppVersion | 获取 APP 版本信息 | +| getAppVersionCode | 获取 APP versionCode | +| getAppVersionName | 获取 APP versionName | + + +* **内存信息工具类 ->** [MemoryUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/MemoryUtils.java) + +| 方法 | 注释 | +| :- | :- | +| printMemoryInfo | 获取内存信息 | +| printMemoryInfo2 | 获取内存信息 | +| getMemoryInfo | 获取内存信息 | +| getAvailMemory | 获取可用内存信息 | +| getAvailMemoryFormat | 获取可用内存信息 ( 格式化 ) | +| getTotalMemory | 获取总内存大小 | +| getTotalMemoryFormat | 获取总内存大小 ( 格式化 ) | +| getMemoryAvailable | 获取可用内存大小 | +| getMemoryAvailableFormat | 获取可用内存大小 ( 格式化 ) | +| getMemInfoIype | 通过不同 type 获取对应的内存信息 | + + +* **网络管理工具类 ->** [NetWorkUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/NetWorkUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getMobileDataEnabled | 获取移动网络打开状态 ( 默认属于未打开 ) | +| setMobileDataEnabled | 设置移动网络开关 ( 无判断是否已开启移动网络 ) - 实际无效果, 非系统应用无权限 | +| isConnect | 判断是否连接了网络 | +| getConnectType | 获取连接的网络类型 | +| isConnWifi | 判断是否连接 Wifi( 连接上、连接中 ) | +| isConnMobileData | 判断是否连接移动网络 ( 连接上、连接中 ) | +| isAvailable | 判断网络是否可用 | +| isAvailableByPing | 使用 ping ip 方式判断网络是否可用 | +| getActiveNetworkInfo | 获取活动网络信息 | +| getActiveNetwork | 获取活动网络信息 | +| is4G | 判断是否 4G 网络 | +| getWifiEnabled | 判断 wifi 是否打开 | +| isWifiAvailable | 判断 wifi 数据是否可用 | +| getNetworkOperatorName | 获取网络运营商名称 - 中国移动、如中国联通、中国电信 | +| getNetworkType | 获取当前网络类型 | +| getNetworkClass | 获取移动网络连接类型 | +| getBroadcastIpAddress | 获取广播 IP 地址 | +| getDomainAddress | 获取域名 IP 地址 | +| getIPAddress | 获取 IP 地址 | +| getIpAddressByWifi | 根据 Wifi 获取网络 IP 地址 | +| getGatewayByWifi | 根据 Wifi 获取网关 IP 地址 | +| getNetMaskByWifi | 根据 Wifi 获取子网掩码 IP 地址 | +| getServerAddressByWifi | 根据 Wifi 获取服务端 IP 地址 | + + +* **通知栏管理工具类 ->** [NotificationUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/NotificationUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getNotificationManager | 获取通知栏管理对象 | +| cancelAll | 移除通知 - 移除所有通知 ( 只是针对当前 Context 下的 Notification) | +| cancel | 移除通知 - 移除标记为 id 的通知 ( 只是针对当前 Context 下的所有 Notification) | +| notify | 进行通知 | +| createPendingIntent | 获取 PendingIntent | +| createNotification | 创建通知栏对象 | +| obtain | 获取 Led 配置参数 | +| isEmpty | 判断是否为 null | + + +* **OS 系统相关工具类 ->** [OSUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/OSUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getRomType | 获取 ROM 类型 | +| getVersion | 获取 ROM 具体版本号, 如 MIUI 9 8.4.26 获取的是 8.4.26 | +| getBaseVersion | 获取 ROM 版本, 如 MIUI 9 获取的是 9 | + + +* **路径相关工具类 ->** [PathUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/PathUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getRootPath | 获取 Android 系统根目录 - path /system | +| getDataPath | 获取 data 目录 - path /data | +| getDownloadCachePath | 获取下载缓存目录 - path data/cache | +| getInternalAppDataPath | 获取内存应用数据路径 - path /data/data/package | +| getInternalAppCodeCacheDir | 获取内存应用代码缓存路径 - path /data/data/package/code_cache | +| getInternalAppDbsPath | 获取内存应用数据库路径 - path /data/data/package/databases | +| getInternalAppDbPath | 获取内存应用数据库路径 - path /data/data/package/databases/name | +| getInternalAppFilesPath | 获取内存应用文件路径 - path/data/data/package/files | +| getInternalAppSpPath | 获取内存应用 SP 路径 - path/data/data/package/shared_prefs | +| getInternalAppNoBackupFilesPath | 获取内存应用未备份文件路径 - path/data/data/package/no_backup | +| getExternalStoragePath | 获取外存路径 - path/storage/emulated/0 | +| getExternalMusicPath | 获取外存音乐路径 - path/storage/emulated/0/Music | +| getExternalPodcastsPath | 获取外存播客路径 - path/storage/emulated/0/Podcasts | +| getExternalRingtonesPath | 获取外存铃声路径 - path/storage/emulated/0/Ringtones | +| getExternalAlarmsPath | 获取外存闹铃路径 - path/storage/emulated/0/Alarms | +| getExternalNotificationsPath | 获取外存通知路径 - path/storage/emulated/0/Notifications | +| getExternalPicturesPath | 获取外存图片路径 - path/storage/emulated/0/Pictures | +| getExternalMoviesPath | 获取外存影片路径 - path/storage/emulated/0/Movies | +| getExternalDownloadsPath | 获取外存下载路径 - path/storage/emulated/0/Download | +| getExternalDcimPath | 获取外存数码相机图片路径 - path/storage/emulated/0/DCIM | +| getExternalDocumentsPath | 获取外存文档路径 - path/storage/emulated/0/Documents | +| getExternalAppDataPath | 获取外存应用数据路径 - path/storage/emulated/0/Android/data/package | +| getExternalAppCachePath | 获取外存应用缓存路径 - path/storage/emulated/0/Android/data/package/cache | +| getExternalAppFilesPath | 获取外存应用文件路径 - path/storage/emulated/0/Android/data/package/files | +| getExternalAppMusicPath | 获取外存应用音乐路径 - path/storage/emulated/0/Android/data/package/files/Music | +| getExternalAppPodcastsPath | 获取外存应用播客路径 - path/storage/emulated/0/Android/data/package/files/Podcasts | +| getExternalAppRingtonesPath | 获取外存应用铃声路径 - path/storage/emulated/0/Android/data/package/files/Ringtones | +| getExternalAppAlarmsPath | 获取外存应用闹铃路径 - path/storage/emulated/0/Android/data/package/files/Alarms | +| getExternalAppNotificationsPath | 获取外存应用通知路径 - path/storage/emulated/0/Android/data/package/files/Notifications | +| getExternalAppPicturesPath | 获取外存应用图片路径 - path/storage/emulated/0/Android/data/package/files/Pictures | +| getExternalAppMoviesPath | 获取外存应用影片路径 - path/storage/emulated/0/Android/data/package/files/Movies | +| getExternalAppDownloadPath | 获取外存应用下载路径 - path/storage/emulated/0/Android/data/package/files/Download | +| getExternalAppDcimPath | 获取外存应用数码相机图片路径 - path/storage/emulated/0/Android/data/package/files/DCIM | +| getExternalAppDocumentsPath | 获取外存应用文档路径 - path/storage/emulated/0/Android/data/package/files/Documents | +| getExternalAppObbPath | 获取外存应用 OBB 路径 - path/storage/emulated/0/Android/obb/package | + + +* **手机相关工具类 ->** [PhoneUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/PhoneUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getTelephonyManager | 获取 TelephonyManager | +| isPhone | 判断设备是否是手机 | +| getSimState | 获取 SIM 卡状态 | +| isSimReady | 判断是否装载 SIM 卡 | +| getSimCountryIso | 获取 SIM 卡运营商的国家代码 | +| getNetworkCountryIso | 获取 SIM 卡注册的网络运营商的国家代码 | +| getSimCountry | 获取 SIM 卡运营商的国家代码 | +| checkSimCountry | 判断 SIM 卡运营商是否国内 | +| getMEID | 获取 MEID 码 | +| getIMEI | 获取 IMEI 码 | +| getIMSI | 获取 IMSI 码 | +| getSimOperatorName | 获取 SIM 卡运营商名称 ( 如: 中国移动、如中国联通、中国电信 ) | +| getSimOperator | 获取 SIM 卡运营商 MCC + MNC | +| getChinaOperatorByIMSI | 通过 IMSI 获取中国运营商简称 | +| getChinaOperatorBySimOperator | 获取 SIM 卡中国运营商简称 | +| getPhoneType | 获取手机类型 | +| getDeviceId | 获取设备 id | +| getAndroidId | 获取 Android id | +| getSerialNumber | 获取设备序列号 | +| getSimSerialNumber | 获取 SIM 卡序列号 | +| getUUID | 获取设备唯一 UUID | +| getPhoneStatus | 获取手机状态信息 | +| dial | 跳至拨号界面 | +| call | 拨打电话 | +| sendSms | 跳至发送短信界面 | +| sendSmsSilent | 发送短信 | +| getContactNum | 打开手机联系人界面点击联系人后便获取该号码 | +| getAllContactInfo | 获取手机联系人信息 | +| getAllContactInfo2 | 获取手机联系人信息 | +| getAllSMS | 获取手机短信并保存到 xml 中 | +| getMtkTeleInfo | 获取 MTK 神机的双卡 IMSI、IMSI 信息 | +| getMtkTeleInfo2 | 获取 MTK 神机的双卡 IMSI、IMSI 信息 | +| getQualcommTeleInfo | 获取 高通 神机的双卡 IMSI、IMSI 信息 | +| getSpreadtrumTeleInfo | 获取 展讯 神机的双卡 IMSI、IMSI 信息 | + + +* **电源管理工具类 ->** [PowerManagerUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/PowerManagerUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 PowerManagerUtils 实例 | +| isScreenOn | 屏幕是否打开 ( 亮屏 ) | +| turnScreenOn | 唤醒 / 点亮 屏幕 | +| turnScreenOff | 释放屏幕锁, 允许休眠时间自动黑屏 | +| getWakeLock | 获取 PowerManager.WakeLock | +| setWakeLock | 设置 PowerManager.WakeLock | +| getPowerManager | 获取 PowerManager | +| setPowerManager | 设置 PowerManager | +| setBright | 设置屏幕常亮 | +| setWakeLockToBright | 设置 WakeLock 常亮 | + + +* **进程相关工具类 ->** [ProcessUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ProcessUtils.java) + +| 方法 | 注释 | +| :- | :- | +| kill | 销毁自身进程 | +| isCurProcess | 判断是否当前进程 | +| getCurProcessName | 获取当前进程名 | +| getProcessName | 获取进程 id 对应的进程名 | +| getPid | 根据包名获取进程 id | +| getRunningAppProcessInfo | 根据进程 id 获取进程信息 | +| getForegroundProcessName | 获取前台线程包名 | +| getAllBackgroundProcesses | 获取后台服务进程 | +| killAllBackgroundProcesses | 杀死所有的后台服务进程 | +| killBackgroundProcesses | 杀死后台服务进程 | + + +* **资源文件工具类 ->** [ResourceUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ResourceUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getResources | 获取 Resources | +| getTheme | 获取 Resources.Theme | +| getAssets | 获取 AssetManager | +| getContentResolver | 获取 ContentResolver | +| getDisplayMetrics | 获取 DisplayMetrics | +| getConfiguration | 获取 Configuration | +| getColorStateList | 获取 ColorStateList | +| getString | 获取 String | +| getColor | 获取 Color | +| getDrawable | 获取 Drawable | +| getColorDrawable | 获取指定颜色 Drawable | +| getBitmap | 获取 Bitmap | +| getDimension | 获取 Dimension | +| getBoolean | 获取 Boolean | +| getInteger | 获取 Integer | +| getAnimation | 获取 Animation | +| getResourceName | 获取给定资源标识符的全名 | +| getIntArray | 获取 int[] | +| getStringArray | 获取 String[] | +| getTextArray | 获取 CharSequence[] | +| getLayoutId | 获取 layout id | +| getDrawableId | 获取 drawable id | +| getMipmapId | 获取 mipmap id | +| getMenuId | 获取 menu id | +| getRawId | 获取 raw id | +| getAnimId | 获取 anim id | +| getColorId | 获取 color id | +| getDimenId | 获取 dimen id | +| getAttrId | 获取 attr id | +| getStyleId | 获取 style id | +| getStyleableId | 获取 styleable id | +| getId | 获取 id | +| getStringId | 获取 string id | +| getBoolId | 获取 bool id | +| getIntegerId | 获取 integer id | +| getIdentifier | 获取资源 id | +| openAssetsResource | 获取 AssetManager 指定资源 InputStream | +| openRawResource | 获取对应资源 InputStream | +| openRawResourceFd | 获取对应资源 AssetFileDescriptor | +| readBytesFromAssets | 获取 Assets 资源文件数据 | +| readStringFromAssets | 获取 Assets 资源文件数据 | +| readBytesFromRaw | 获取 Raw 资源文件数据 | +| readStringFromRaw | 获取 Raw 资源文件数据 | +| geFileToListFromAssets | 获取 Assets 资源文件数据 ( 返回 List 一行的全部内容属于一个索引 ) | +| geFileToListFromRaw | 获取 Raw 资源文件数据 ( 返回 List 一行的全部内容属于一个索引 ) | +| saveAssetsFormFile | 获取 Assets 资源文件数据并保存到本地 | +| saveRawFormFile | 获取 Raw 资源文件数据并保存到本地 | + + +* **屏幕相关工具类 ->** [ScreenUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ScreenUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getWindowManager | 获取 WindowManager | +| getDisplayMetrics | 获取 DisplayMetrics | +| getScreenWidth | 获取屏幕宽度 | +| getScreenHeight | 获取屏幕高度 | +| getScreenWidthHeight | 获取屏幕宽高 | +| getScreenWidthHeightToPoint | 获取屏幕宽高 | +| getScreenSize | 获取屏幕分辨率 | +| getScreenSizeOfDevice | 获取屏幕英寸 - 例 5.5 英寸 | +| getDensity | 获取屏幕密度 | +| getDensityDpi | 获取屏幕密度 dpi | +| getScaledDensity | 获取屏幕缩放密度 | +| getXDpi | 获取 X 轴 dpi | +| getYDpi | 获取 Y 轴 dpi | +| getWidthDpi | 获取宽度比例 dpi 基准 | +| getHeightDpi | 获取高度比例 dpi 基准 | +| getScreenInfo | 获取屏幕信息 | +| setWindowSecure | 设置禁止截屏 | +| setFullScreen | 设置屏幕为全屏 | +| setLandscape | 设置屏幕为横屏 | +| setPortrait | 设置屏幕为竖屏 | +| isLandscape | 判断是否横屏 | +| isPortrait | 判断是否竖屏 | +| toggleScreenOrientation | 切换屏幕方向 | +| getScreenRotation | 获取屏幕旋转角度 | +| isScreenLock | 判断是否锁屏 | +| isTablet | 判断是否是平板 | +| getStatusHeight | 获取状态栏的高度 ( 无关 android:theme 获取状态栏高度 ) | +| getStatusBarHeight | 获取应用区域 TitleBar 高度 ( 顶部灰色 TitleBar 高度, 没有设置 android:theme 的 NoTitleBar 时会显示 ) | +| setSleepDuration | 设置进入休眠时长 | +| getSleepDuration | 获取进入休眠时长 | +| snapShotWithStatusBar | 获取当前屏幕截图, 包含状态栏 ( 顶部灰色 TitleBar 高度, 没有设置 android:theme 的 NoTitleBar 时会显示 ) | +| snapShotWithoutStatusBar | 获取当前屏幕截图, 不包含状态栏 ( 如果 android:theme 全屏, 则截图无状态栏 ) | +| getNavigationBarHeight | 获取底部导航栏高度 | +| checkDeviceHasNavigationBar | 检测是否具有底部导航栏 | + + +* **SDCard 工具类 ->** [SDCardUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/SDCardUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isSDCardEnable | 判断内置 SDCard 是否正常挂载 | +| getSDCardFile | 获取内置 SDCard File | +| getSDCardPath | 获取内置 SDCard 绝对路径 | +| getSDCardPathSeparator | 获取内置 SDCard 绝对路径 | +| isSDCardEnablePath | 判断 SDCard 是否可用 | +| getSDCardPaths | 获取 SDCard 路径 | +| getAllBlockSizeFormat | 获取内置 SDCard 空间总大小 | +| getAvailableBlocksFormat | 获取内置 SDCard 空闲空间大小 | +| getUsedBlocksFormat | 获取内置 SDCard 已使用空间大小 | +| getAllBlockSize | 获取内置 SDCard 空间总大小 | +| getAvailableBlocks | 获取内置 SDCard 空闲空间大小 | +| getUsedBlocks | 获取内置 SDCard 已使用空间大小 | +| getBlockSizeInfos | 返回内置 SDCard 空间大小信息 | +| getDiskCacheDir | 获取 APP Cache 文件夹地址 | +| getCacheFile | 获取 APP Cache 路径 File | +| getCachePath | 获取 APP Cache 路径 | + + +* **服务相关工具类 ->** [ServiceUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ServiceUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isServiceRunning | 判断服务是否运行 | +| getAllRunningService | 获取所有运行的服务 | +| startService | 启动服务 | +| stopService | 停止服务 | +| bindService | 绑定服务 | +| unbindService | 解绑服务 | + + +* **Shape 工具类 ->** [ShapeUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ShapeUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getDrawable | 获取 GradientDrawable | +| setDrawable | 设置 Drawable 背景 | +| newBuilder | 创建新的 Shape Builder 对象 | +| newBuilderToLeft | 创建新的 Shape Builder 对象 | +| newBuilderToRight | 创建新的 Shape Builder 对象 | +| newBuilderToGradient | 创建渐变的 Shape Builder 对象 | +| build | build ShapeUtils 对象 | +| setRadius | 设置圆角 | +| setRadiusLeft | 设置圆角 | +| setRadiusRight | 设置圆角 | +| setCornerRadii | 设置圆角 ( 统一处理方法 ) | +| setColor | 设置背景色 ( 填充铺满 ) | +| setStroke | 设置边框颜色 | +| setSize | 设置大小 | +| setPadding | 设置边距 | +| getOrientation | 获取渐变角度 | + + +* **Shell 命令工具类 ->** [ShellUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ShellUtils.java) + +| 方法 | 注释 | +| :- | :- | +| execCmd | 执行 shell 命令 | +| isSuccess | 判断是否执行成功 | +| isSuccess2 | 判断是否执行成功 ( 判断 errorMsg) | +| isSuccess3 | 判断是否执行成功 ( 判断 successMsg) | +| isSuccess4 | 判断是否执行成功 ( 判断 successMsg), 并且 successMsg 是否包含某个字符串 | + + +* **快捷方式工具类 ->** [ShortCutUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ShortCutUtils.java) + +| 方法 | 注释 | +| :- | :- | +| hasShortcut | 检测是否存在桌面快捷方式 | +| getShortCutIntent | 获取桌面快捷方式点击 Intent | +| addShortcut | 创建桌面快捷方式 | +| deleteShortcut | 删除桌面快捷方式 | + + +* **签名工具类 ( 获取 APP 签名信息 ) ->** [SignaturesUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/SignaturesUtils.java) + +| 方法 | 注释 | +| :- | :- | +| signatureMD5 | 获取 MD5 签名 | +| signatureSHA1 | 获取签名 SHA1 加密字符串 | +| signatureSHA256 | 获取签名 SHA256 加密字符串 | +| isDebuggable | 判断 debug 签名还是 release 签名 | +| getX509Certificate | 获取证书对象 | +| printSignatureInfo | 打印签名信息 | +| getSignaturesFromApk | 从 APK 中读取签名 | +| getCertificateFromApk | 从 APK 中读取签名 | + + +* **大小工具类 (dp, px, sp 转换、View 获取宽高等 ) ->** [SizeUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/SizeUtils.java) + +| 方法 | 注释 | +| :- | :- | +| dipConvertPx | dp 转 px | +| dipConvertPxf | dp 转 px (float) | +| pxConvertDip | px 转 dp | +| pxConvertDipf | px 转 dp (float) | +| spConvertPx | sp 转 px | +| spConvertPxf | sp 转 px (float) | +| pxConvertSp | px 转 sp | +| pxConvertSpf | px 转 sp (float) | +| applyDimension | 各种单位转换 - 该方法存在于 TypedValue.applyDimension | +| forceGetViewSize | 在 onCreate 中获取视图的尺寸 - 需回调 onGetSizeListener 接口, 在 onGetSize 中获取 View 宽高 | +| measureView | 测量 View | +| getMeasuredWidth | 获取 View 的宽度 | +| getMeasuredHeight | 获取 View 的高度 | + + +* **Snackbar 工具类 ->** [SnackbarUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/SnackbarUtils.java) + +| 方法 | 注释 | +| :- | :- | +| with | 获取 SnackbarUtils 对象 | +| getStyle | 获取样式 | +| setStyle | 设置样式 | +| getSnackbar | 获取 Snackbar | +| getSnackbarView | 获取 Snackbar View | +| getTextView | 获取 Snackbar TextView(snackbar_text) | +| getActionButton | 获取 Snackbar Action Button(snackbar_action) | +| addView | 向 Snackbar 布局中添加 View (Google 不建议, 复杂的布局应该使用 DialogFragment 进行展示 ) | +| setCallback | 设置 Snackbar 展示完成、隐藏完成 的监听 | +| setAction | 设置 Action 按钮文字内容及点击监听 | +| dismiss | 关闭 Snackbar | +| showShort | 显示 Short Snackbar | +| showLong | 显示 Long Snackbar | +| showIndefinite | 显示 Indefinite Snackbar ( 无限时, 一直显示 ) | +| setSnackbarStyle | 设置 Snackbar 样式配置 | +| getShadowMargin | 获取阴影边距 | +| setShadowMargin | 设置阴影边距 | +| isAutoCalc | 判断是否自动计算边距 ( 如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示 ) | +| setAutoCalc | 设置是否自动计算边距 ( 如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示 ) | +| above | 设置 Snackbar 显示在指定 View 的上方 | +| bellow | 设置 Snackbar 显示在指定 View 的下方 | + + +* **SpannableString 工具类 ->** [SpannableStringUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/SpannableStringUtils.java) + +| 方法 | 注释 | +| :- | :- | +| with | 获取持有 TextView SpannableStringUtils | +| setFlag | 设置标识 | +| setForegroundColor | 设置前景色 | +| setBackgroundColor | 设置背景色 | +| setLineHeight | 设置行高 | +| setQuoteColor | 设置引用线的颜色 | +| setLeadingMargin | 设置缩进 | +| setBullet | 设置列表标记 | +| setFontSize | 设置字体尺寸 | +| setFontProportion | 设置字体比例 | +| setFontXProportion | 设置字体横向比例 | +| setStrikethrough | 设置删除线 | +| setUnderline | 设置下划线 | +| setSuperscript | 设置上标 | +| setSubscript | 设置下标 | +| setBold | 设置粗体 | +| setItalic | 设置斜体 | +| setBoldItalic | 设置粗斜体 | +| setFontFamily | 设置字体系列 | +| setTypeface | 设置字体 | +| setHorizontalAlign | 设置水平对齐 | +| setVerticalAlign | 设置垂直对齐 | +| setClickSpan | 设置点击事件 | +| setUrl | 设置超链接 | +| setBlur | 设置模糊 | +| setShader | 设置着色器 | +| setShadow | 设置阴影 | +| setSpans | 自定义 setSpan 参数 | +| append | 追加文本 | +| appendLine | 追加换行 | +| appendImage | 追加 Image | +| appendSpace | 追加空格 | +| get | 获取 SpannableStringBuilder | +| create | 创建 SpannableStringBuilder | + + +* **颜色状态列表工具类 ->** [StateListUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/StateListUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getColorStateList | 获取 ColorStateList | +| createColorStateList | 创建 ColorStateList | +| newSelector | 创建 StateListDrawable | + + +* **TextView 工具类 ->** [TextViewUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/TextViewUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getTextView | 获取 TextView | +| getHint | 获取 Hint 文本 | +| getText | 获取文本 | +| getHints | 获取多个 TextView Hint 文本 | +| getTexts | 获取多个 TextView 文本 | +| setHint | 设置 Hint 文本 | +| setText | 设置文本 | +| setTexts | 设置多个 TextView 文本 | +| setHtmlText | 设置 Html 内容 | +| setHtmlTexts | 设置多个 TextView Html 内容 | +| getHintTextColors | 获取 Hint 字体颜色 | +| setHintTextColor | 设置 Hint 字体颜色 | +| getTextColors | 获取字体颜色 | +| setTextColor | 设置字体颜色 | +| setHintTextColors | 设置多个 TextView Hint 字体颜色 | +| setTextColors | 设置多个 TextView 字体颜色 | +| setTypeface | 设置字体 | +| setTextSizeByPx | 设置字体大小 - px 像素 | +| setTextSizeBySp | 设置字体大小 - sp 缩放像素 | +| setTextSizeByDp | 设置字体大小 - dp 与设备无关的像素 | +| setTextSizeByIn | 设置字体大小 - inches 英寸 | +| setTextSize | 设置字体大小 | +| setTextSizes | 设置多个 TextView 字体大小 | +| getTextSize | 获取 TextView 字体大小 - px | +| clearFlags | 清空 flags | +| setBold | 设置 TextView 是否加粗 | +| setUnderlineText | 设置下划线 | +| setStrikeThruText | 设置中划线 | +| setLetterSpacing | 设置文字水平间距 | +| setLineSpacing | 设置文字行间距 ( 行高 ) | +| setLineSpacingAndMultiplier | 设置文字行间距 ( 行高 )、行间距倍数 | +| setTextScaleX | 设置字体水平方向的缩放 | +| setIncludeFontPadding | 设置是否保留字体留白间隙区域 | +| setInputType | 设置输入类型 | +| setImeOptions | 设置软键盘右下角按钮类型 | +| setLines | 设置行数 | +| setMaxLines | 设置最大行数 | +| setMinLines | 设置最小行数 | +| setMaxEms | 设置最大字符宽度限制 | +| setMinEms | 设置最小字符宽度限制 | +| setEms | 设置指定字符宽度 | +| setMaxLength | 设置长度限制 | +| setMaxLengthAndText | 设置长度限制, 并且设置内容 | +| setEllipsize | 设置 Ellipsize 效果 | +| setAutoLinkMask | 设置自动识别文本链接 | +| setAllCaps | 设置文本全为大写 | +| setGravity | 设置 Gravity | +| getPaint | 获取 TextView Paint | +| getTextHeight | 获取字体高度 | +| getTextTopOffsetHeight | 获取字体顶部偏移高度 | +| getTextWidth | 计算字体宽度 | +| getCenterRectY | 获取画布中间居中位置 | +| reckonTextSize | 通过需要的高度, 计算字体大小 | +| calcTextWidth | 计算第几位超过宽度 | +| calcTextLine | 计算文本换行行数 | + + +* **Uri 工具类 ->** [UriUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/UriUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getUriForFileToName | 获取文件 Uri ( 自动添加包名 ${applicationId}) | +| getUriForFile | 获取文件 Uri | +| getFilePathByUri | 通过 Uri 获取文件路径 | + + +* **震动相关工具类 ->** [VibrationUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/VibrationUtils.java) + +| 方法 | 注释 | +| :- | :- | +| vibrate | 震动 | +| cancel | 取消震动 | + + +* **View 操作相关工具类 ->** [ViewUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ViewUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getContext | 获取 Context | +| getActivity | 获取 View context 所属的 Activity | +| inflate | 获取 View | +| getParent | 获取指定 View 父布局 | +| getChildAt | 获取指定索引 View | +| removeAllViews | 移除全部子 View | +| getLayoutParams | 获取 LayoutParams | +| setLayoutParams | 设置 View LayoutParams | +| findViewById | 初始化 View | +| convertView | 转换 View | +| isEmpty | 判断 View 是否为 null | +| isNotEmpty | 判断 View 是否不为 null | +| getMinimumHeight | 获取 View 最小高度 | +| setMinimumHeight | 设置 View 最小高度 | +| getMinimumWidth | 获取 View 最小宽度 | +| setMinimumWidth | 设置 View 最小宽度 | +| getAlpha | 获取 View 透明度 | +| setAlpha | 设置 View 透明度 | +| getTag | 获取 View Tag | +| setTag | 设置 View Tag | +| setScrollContainer | 设置 View 滚动效应 | +| setNextFocusForwardId | 设置下一个获取焦点的 View id | +| setNextFocusDownId | 设置向下移动焦点时, 下一个获取焦点的 View id | +| setNextFocusLeftId | 设置向左移动焦点时, 下一个获取焦点的 View id | +| setNextFocusRightId | 设置向右移动焦点时, 下一个获取焦点的 View id | +| setNextFocusUpId | 设置向上移动焦点时, 下一个获取焦点的 View id | +| setRotation | 设置 View 旋转度数 | +| setRotationX | 设置 View 水平旋转度数 | +| setRotationY | 设置 View 竖直旋转度数 | +| setScaleX | 设置 View 水平方向缩放比例 | +| setScaleY | 设置 View 竖直方向缩放比例 | +| setTextAlignment | 设置文本的显示方式 | +| setTextDirection | 设置文本的显示方向 | +| setPivotX | 设置水平方向偏转量 | +| setPivotY | 设置竖直方向偏转量 | +| setTranslationX | 设置水平方向的移动距离 | +| setTranslationY | 设置竖直方向的移动距离 | +| setLayerType | 设置 View 硬件加速类型 | +| setFocusable | 设置 View 是否可以获取焦点 | +| setSelected | 设置 View 是否选中 | +| setEnabled | 设置 View 是否启用 | +| setClickable | 设置 View 是否可以点击 | +| setLongClickable | 设置 View 是否可以长按 | +| isVisibility | 判断 View 是否显示 | +| isVisibilitys | 判断 View 是否都显示 | +| isVisibilityIN | 判断 View 是否隐藏占位 | +| isVisibilityINs | 判断 View 是否都隐藏占位 | +| isVisibilityGone | 判断 View 是否隐藏 | +| isVisibilityGones | 判断 View 是否都隐藏 | +| getVisibility | 获取显示的状态 (View.VISIBLE : View.GONE) | +| getVisibilityIN | 获取显示的状态 (View.VISIBLE : View.INVISIBLE) | +| setVisibility | 设置 View 显示的状态 | +| setVisibilitys | 设置 View 显示的状态 | +| toggleVisibilitys | 切换 View 显示的状态 | +| reverseVisibilitys | 反转 View 显示的状态 | +| toggleView | 切换 View 状态 | +| toggleViews | 切换 View 状态 | +| removeSelfFromParent | 把自身从父 View 中移除 | +| isTouchInView | 判断触点是否落在该 View 上 | +| requestLayoutParent | View 请求更新 | +| measureView | 测量 View | +| getMeasuredWidth | 获取 View 的宽度 | +| getMeasuredHeight | 获取 View 的高度 | +| getLayoutGravity | 获取 View Layout Gravity | +| setLayoutGravity | 设置 View Layout Gravity | +| getMarginLeft | 获取 View Left Margin | +| getMarginTop | 获取 View Top Margin | +| getMarginRight | 获取 View Right Margin | +| getMarginBottom | 获取 View Bottom Margin | +| getMargin | 获取 View Margin | +| setMarginLeft | 设置 View Left Margin | +| setMarginTop | 设置 View Top Margin | +| setMarginRight | 设置 View Right Margin | +| setMarginBottom | 设置 View Bottom Margin | +| setMargin | 设置 Margin 边距 | +| getPaddingLeft | 获取 View Left Padding | +| getPaddingTop | 获取 View Top Padding | +| getPaddingRight | 获取 View Right Padding | +| getPaddingBottom | 获取 View Bottom Padding | +| getPadding | 获取 View Padding | +| setPaddingLeft | 设置 View Left Padding | +| setPaddingTop | 设置 View Top Padding | +| setPaddingRight | 设置 View Right Padding | +| setPaddingBottom | 设置 View Bottom Padding | +| setPadding | 设置 Padding 边距 | +| getCompoundDrawables | 获取 CompoundDrawables | +| getCompoundDrawablePadding | 获取 CompoundDrawables Padding | +| setCompoundDrawablesByLeft | 设置 Left CompoundDrawables | +| setCompoundDrawablesByTop | 设置 Top CompoundDrawables | +| setCompoundDrawablesByRight | 设置 Right CompoundDrawables | +| setCompoundDrawablesByBottom | 设置 Bottom CompoundDrawables | +| setCompoundDrawables | 设置 CompoundDrawables | +| setCompoundDrawablesWithIntrinsicBoundsByLeft | 设置 Left CompoundDrawables - 按照原有比例大小显示图片 | +| setCompoundDrawablesWithIntrinsicBoundsByTop | 设置 Top CompoundDrawables - 按照原有比例大小显示图片 | +| setCompoundDrawablesWithIntrinsicBoundsByRight | 设置 Right CompoundDrawables - 按照原有比例大小显示图片 | +| setCompoundDrawablesWithIntrinsicBoundsByBottom | 设置 Bottom CompoundDrawables - 按照原有比例大小显示图片 | +| setCompoundDrawablesWithIntrinsicBounds | 设置 CompoundDrawables - 按照原有比例大小显示图片 | +| calcListViewItemHeight | 计算 ListView Item 高度 | +| calcGridViewItemHeight | 计算 GridView Item 高度 | +| getItemHeighet | 获取单独一个 Item 高度 | + + +## **`dev.utils.app.anim`** + + +* **动画工具类 ->** [AnimationUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/anim/AnimationUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setAnimRepeat | 设置动画重复处理 | +| getRotateAnimation | 获取一个旋转动画 | +| getRotateAnimationByCenter | 获取一个根据视图自身中心点旋转的动画 | +| getAlphaAnimation | 获取一个透明度渐变动画 | +| getHiddenAlphaAnimation | 获取一个由完全显示变为不可见的透明度渐变动画 | +| getShowAlphaAnimation | 获取一个由不可见变为完全显示的透明度渐变动画 | +| getScaleAnimation | 获取一个缩放动画 | +| getScaleAnimationCenter | 获取一个中心点缩放动画 | +| getLessenScaleAnimation | 获取一个缩小动画 | +| getAmplificationAnimation | 获取一个放大动画 | +| getTranslateAnimation | 获取一个视图移动动画 | +| getShakeAnimation | 获取一个视图摇晃动画 | + + +* **视图动画工具类 (AnimationUtils 基础上封装 ), 提供简单的控制视图的动画的工具方法 ->** [ViewAnimationUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/anim/ViewAnimationUtils.java) + +| 方法 | 注释 | +| :- | :- | +| invisibleViewByAlpha | 将给定视图渐渐隐去 - view.setVisibility(View.INVISIBLE) | +| goneViewByAlpha | 将给定视图渐渐隐去最后从界面中移除 - view.setVisibility(View.GONE) | +| visibleViewByAlpha | 将给定视图渐渐显示出来 - view.setVisibility(View.VISIBLE) | +| translate | 视图移动 | +| shake | 视图摇晃 | + + +## **`dev.utils.app.assist`** + + +* **异步执行辅助类 ->** [AsyncExecutor.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/assist/AsyncExecutor.java) + +| 方法 | 注释 | +| :- | :- | +| shutdownNow | 立即关闭线程池任务 | +| execute | 将任务投入线程池执行 | + + +* **播放「bee」的声音, 并且震动辅助类 ->** [BeepVibrateAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/assist/BeepVibrateAssist.java) + +| 方法 | 注释 | +| :- | :- | +| isPlayBeep | 判断是否允许播放声音 | +| isVibrate | 判断是否允许震动 | +| setVibrate | 设置是否允许震动 | +| setMediaPlayer | 设置播放资源对象 | +| playBeepSoundAndVibrate | 进行播放声音, 并且震动 | +| close | 关闭震动、提示声, 并释放资源 | +| buildMediaPlayer | 创建 MediaPlayer 对象 | + + +* **Activity 无操作定时辅助类 ->** [InactivityTimerAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java) + +| 方法 | 注释 | +| :- | :- | +| onPause | 暂停检测 | +| onResume | 回到 Activity 处理 | +| onDestroy | Activity 销毁处理 | + + +* **屏幕传感器辅助类 ( 监听是否横竖屏 ) ->** [ScreenSensorAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java) + +| 方法 | 注释 | +| :- | :- | +| start | 开始监听 | +| stop | 停止监听 | +| isPortrait | 是否竖屏 | +| isAllowChange | 是否允许切屏 | + + +## **`dev.utils.app.assist.manager`** + + +* **定时器工具类 ->** [TimerManager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/assist/manager/TimerManager.java) + +| 方法 | 注释 | +| :- | :- | +| gc | 回收定时器资源 | +| getTimerSize | 获取全部任务总数 | +| getTimer | 获取属于对应字符串标记的定时器任务 ( 优先获取符合的 ) | +| getTimers | 获取属于对应字符串标记的定时器任务集合 | +| closeAll | 关闭全部任务 | +| closeNotRunTask | 关闭所有未运行的任务 | +| closeInfiniteTask | 关闭所有无限循环的任务 | +| closeMark | 关闭所有符合对应的字符串标记的定时器任务 | +| createTimer | 创建定时器 ( 立即执行、无限循环、通知默认 what ) | +| getMarkId | 获取标记 id | +| getMarkStr | 获取标记字符串 | +| setMarkId | 设置标记 id | +| setMarkStr | 设置标记字符串 | +| startTimer | 运行定时器 | +| closeTimer | 关闭定时器 | +| isRunTimer | 判断是否运行中 | +| getTriggerNumber | 获取已经触发的次数 | +| getTriggerLimit | 获取允许触发的上限次数 | +| isTriggerEnd | 是否触发结束 ( 到达最大次数 ) | +| isInfinite | 是否无限循环 | +| setHandler | 设置通知的 Handler | +| setNotifyWhat | 设置通知的 what | +| setNotifyObject | 设置通知的 Object | +| setTime | 设置时间 | +| setTriggerLimit | 设置触发次数上限 | + + +## **`dev.utils.app.cache`** + + +* **缓存工具类 ->** [DevCache.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/cache/DevCache.java) + +| 方法 | 注释 | +| :- | :- | +| getCacheDir | 获取缓存地址 | +| get | 默认缓存地址 | +| put | 保存 String 数据到缓存中 | +| getAsString | 读取 String 数据 | +| getAsJSONObject | 读取 JSONObject 数据 | +| getAsJSONArray | 读取 JSONArray 数据 | +| getAsBinary | 获取 byte[] 数据 | +| getAsObject | 读取 Serializable 数据 | +| getAsBitmap | 读取 Bitmap 数据 | +| getAsDrawable | 读取 Drawable 数据 | +| file | 获取缓存文件 | +| remove | 移除某个 key 的数据 | +| clear | 清除所有数据 | + + +## **`dev.utils.app.camera1`** + + +* **摄像头自动获取焦点辅助类 ->** [AutoFocusAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/camera1/AutoFocusAssist.java) + +| 方法 | 注释 | +| :- | :- | +| setFocusModes | 设置对焦模式 | +| isAutoFocus | 是否允许自动对焦 | +| setAutoFocus | 设置是否开启自动对焦 | +| start | 开始对焦 | +| stop | 停止对焦 | + + +* **摄像头辅助类 ->** [CameraAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraAssist.java) + +| 方法 | 注释 | +| :- | :- | +| openDriver | 打开摄像头程序 | +| closeDriver | 关闭摄像头程序 | +| startPreview | 开始将 Camera 画面预览到手机上 | +| stopPreview | 停止 Camera 画面预览 | +| getCameraResolution | 获取相机分辨率 | +| getPreviewSize | 获取预览分辨率 | +| getCameraSizeAssist | 获取 Camera.Size 计算辅助类 | +| getCamera | 获取摄像头 | +| setCamera | 设置摄像头 | +| setPreviewNotify | 设置预览回调 | +| setAutoFocus | 设置是否开启自动对焦 | +| isPreviewing | 是否预览中 | +| setAutoInterval | 设置自动对焦时间间隔 | +| setFlashlightOn | 打开闪光灯 | +| setFlashlightOff | 关闭闪光灯 | +| isFlashlightOn | 是否打开闪光灯 | +| isFlashlightEnable | 是否支持手机闪光灯 | + + +* **摄像头 ( 预览、输出大小 ) 辅助类 ->** [CameraSizeAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraSizeAssist.java) + +| 方法 | 注释 | +| :- | :- | +| getCamera | 获取摄像头 | +| setPreviewSize | 设置预览大小 | +| getPreviewSize | 根据手机支持的预览分辨率计算, 设置预览尺寸 | +| setPictureSize | 设置拍照图片大小 | +| getPictureSize | 根据手机支持的拍照分辨率计算 | +| getVideoSize | 根据手机支持的视频录制分辨率计算 | + + +* **摄像头相关工具类 ->** [CameraUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isSupportReverse | 判断是否支持反转摄像头 ( 是否存在前置摄像头 ) | +| checkCameraFacing | 检查是否有指定的摄像头 | +| isFrontCamera | 判断是否使用前置摄像头 | +| isBackCamera | 判断是否使用后置摄像头 | +| isUseCameraFacing | 判断使用的摄像头 | +| freeCameraResource | 释放摄像头资源 | +| initCamera | 初始化摄像头 | +| open | 打开摄像头 | + + +## **`dev.utils.app.image`** + + +* **Bitmap 工具类 ->** [BitmapUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/image/BitmapUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断 Bitmap 对象是否为 null | +| isNotEmpty | 判断 Bitmap 对象是否不为 null | +| isImage | 根据文件判断是否为图片 | +| getBitmapWidth | 获取 Bitmap 宽度 | +| getBitmapHeight | 获取 Bitmap 高度 | +| getBitmapWidthHeight | 获取 Bitmap 宽高 | +| copy | 复制 Bitmap | +| extractAlpha | 获取 Alpha 位图 ( 获取源图片的轮廓 rgb 为 0) | +| recode | 重新编码 Bitmap | +| recycle | Bitmap 通知回收 | +| rotate | 旋转图片 | +| getRotateDegree | 读取图片属性, 获取图片被旋转的角度 | +| reverseByHorizontal | 水平翻转图片 ( 左右颠倒 ) | +| reverseByVertical | 垂直翻转图片 ( 上下颠倒 ) | +| reverse | 翻转图片 | +| zoom | 缩放图片 ( 指定所需宽高 ) | +| scale | 缩放图片 ( 比例缩放 ) | +| skew | 倾斜图片 | +| clip | 裁剪图片 | +| crop | 裁剪图片 ( 返回指定比例图片 ) | +| combine | 合并图片 | +| combineToCenter | 合并图片 ( 居中 ) | +| combineToSameSize | 合并图片 ( 转为相同大小 ) | +| reflection | 图片倒影处理 | +| roundCorner | 图片圆角处理 ( 非圆形 ) | +| roundCornerTop | 图片圆角处理 ( 非圆形 ) - 只有 leftTop、rightTop | +| roundCornerBottom | 图片圆角处理 ( 非圆形 ) - 只有 leftBottom、rightBottom | +| round | 图片圆形处理 | +| addCornerBorder | 添加圆角边框 | +| addCircleBorder | 添加圆形边框 | +| addTextWatermark | 添加文字水印 | +| addImageWatermark | 添加图片水印 | +| compressByZoom | 按缩放宽高压缩 | +| compressByScale | 按缩放比例压缩 | +| compressByQuality | 按质量压缩 | +| compressByByteSize | 按质量压缩 ( 图片大小 ) | +| compressBySampleSize | 按采样大小压缩 | +| calculateInSampleSize | 计算采样大小 | + + +* **图片格式转换工具类 ->** [ImageConvertUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/image/ImageConvertUtils.java) + +| 方法 | 注释 | +| :- | :- | +| convertBMP | 图片转换 BMP 格式 byte[] 数据 | + + +* **图片 ( 滤镜、效果 ) 工具类 ->** [ImageFilterUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/image/ImageFilterUtils.java) + +| 方法 | 注释 | +| :- | :- | +| blur | 图片模糊处理 ( Android RenderScript 实现, 效率最高 ) | +| fastBlur | 图片模糊处理 ( 毛玻璃化 FastBlur Java 实现 ) | +| nostalgic | 怀旧效果处理 | +| sunshine | 光照效果处理 | +| film | 底片效果处理 | +| soften | 柔化效果处理 | +| sharpen | 锐化效果处理 | +| emboss | 浮雕效果处理 | +| toGray | 转为灰度图片 | +| saturation | 饱和度处理 | +| lum | 亮度处理 | +| hue | 色相处理 | +| lumHueSaturation | 亮度、色相、饱和度处理 | +| yuvLandscapeToPortrait | 将 YUV 格式的图片的源数据从横屏模式转为竖屏模式 | + + +* **Image ( Bitmap、Drawable 等 ) 工具类 ->** [ImageUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/image/ImageUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断 Bitmap 对象是否为 null | +| isNotEmpty | 判断 Bitmap 对象是否不为 null | +| isImageFormats | 根据文件名判断文件是否为图片 | +| getImageType | 获取图片类型 | +| isPNG | 判断是否 PNG 图片 | +| isJPEG | 判断是否 JPG 图片 | +| isBMP | 判断是否 BMP 图片 | +| isGif | 判断是否 GIF 图片 | +| isWEBP | 判断是否 WEBP 图片 | +| isICO | 判断是否 ICO 图片 | +| isTIFF | 判断是否 TIFF 图片 | +| decodeFile | 获取 Bitmap | +| decodeResource | 获取 Bitmap | +| decodeStream | 获取 Bitmap | +| decodeFileDescriptor | 获取 Bitmap | +| decodeByteArray | 获取 Bitmap | +| saveBitmapToSDCardJPEG | 保存图片到 SDCard - JPEG | +| saveBitmapToSDCardPNG | 保存图片到 SDCard - PNG | +| saveBitmapToSDCardWEBP | 保存图片到 SDCard - WEBP | +| saveBitmapToSDCard | 保存图片到 SDCard | +| get9PatchDrawable | 获取 .9 Drawable | +| setColorFilter | 图片着色 - tint | +| getBitmap | 获取 Bitmap | +| getBitmapFromView | 通过 View 绘制为 Bitmap | +| getBitmapFromViewCache | 通过 View Cache 绘制为 Bitmap | +| bitmapToByte | Bitmap 转换成 byte[] | +| drawableToByte | Drawable 转换成 byte[] | +| byteToBitmap | byte[] 转 Bitmap | +| bitmapToDrawable | Bitmap 转 Drawable | +| byteToDrawable | byte[] 转 Drawable | +| drawableToBitmap | Drawable 转 Bitmap | +| setBounds | 设置 Drawable 绘制区域 | + + +## **`dev.utils.app.info`** + + +* **APK 信息 Item ->** [ApkInfoItem.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/info/ApkInfoItem.java) + +| 方法 | 注释 | +| :- | :- | +| obtain | 获取 ApkInfoItem | +| getAppInfoBean | 获取 AppInfoBean | +| getListKeyValues | 获取 List 信息键对值集合 | +| getAppMD5 | 获取 APP MD5 签名 | +| getAppSHA1 | 获取 APP SHA1 签名 | +| getAppSHA256 | 获取 APP SHA256 签名 | +| getMinSdkVersion | 获取 APP 最低支持 Android SDK 版本 | +| getTargetSdkVersion | 获取 APP 兼容 SDK 版本 | +| getApkLength | 获取 APP 安装包大小 | +| getX509Certificate | 获取证书对象 | +| getNotBefore | 获取证书生成日期 | +| getNotAfter | 获取证书有效期 | +| isEffective | 获取证书是否过期 | +| getCertPrincipal | 获取证书发布方 | +| getCertVersion | 获取证书版本号 | +| getCertSigalgname | 获取证书算法名称 | +| getCertSigalgoid | 获取证书算法 OID | +| getCertSerialnumber | 获取证书机器码 | +| getCertDercode | 获取证书 DER 编码 | + + +* **APP 信息实体类 ->** [AppInfoBean.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoBean.java) + +| 方法 | 注释 | +| :- | :- | +| obtain | 获取 AppInfoBean | +| getAppPackName | 获取 APP 包名 | +| getAppName | 获取 APP 应用名 | +| getAppIcon | 获取 APP 图标 | +| getAppType | 获取 APP 类型 | +| getVersionCode | 获取 versionCode | +| getVersionName | 获取 versionName | +| getFirstInstallTime | 获取 APP 首次安装时间 | +| getLastUpdateTime | 获取 APP 最后更新时间 | +| getSourceDir | 获取 APK 地址 | +| getApkSize | 获取 APK 大小 | +| isSystemApp | 是否系统程序 | +| isSystemUpdateApp | 是否系统程序被手动更新后, 也成为第三方应用程序 | + + +* **APP 信息 Item ->** [AppInfoItem.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoItem.java) + +| 方法 | 注释 | +| :- | :- | +| obtain | 获取 AppInfoItem | +| getAppInfoBean | 获取 AppInfoBean | +| getListKeyValues | 获取 List 信息键对值集合 | +| getAppMD5 | 获取 APP MD5 签名 | +| getAppSHA1 | 获取 APP SHA1 签名 | +| getAppSHA256 | 获取 APP SHA256 签名 | +| getMinSdkVersion | 获取 APP 最低支持 Android SDK 版本 | +| getTargetSdkVersion | 获取 APP 兼容 SDK 版本 | +| getApkLength | 获取 APP 安装包大小 | +| getX509Certificate | 获取证书对象 | +| getNotBefore | 获取证书生成日期 | +| getNotAfter | 获取证书有效期 | +| isEffective | 获取证书是否过期 | +| getCertPrincipal | 获取证书发布方 | +| getCertVersion | 获取证书版本号 | +| getCertSigalgname | 获取证书算法名称 | +| getCertSigalgoid | 获取证书算法 OID | +| getCertSerialnumber | 获取证书机器码 | +| getCertDercode | 获取证书 DER 编码 | + + +* **APP 信息获取工具类 ->** [AppInfoUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getPackageInfoToFile | 通过 APK 路径 初始化 PackageInfo | +| getPackageInfoToPath | 通过 APK 路径 初始化 PackageInfo | +| getPackageInfo | 获取当前应用 PackageInfo | +| getAppInfoBeanToFile | 通过 APK 路径 获取 AppInfoBean | +| getAppInfoBeanToPath | 通过 APK 路径 获取 AppInfoBean | +| getAppInfoBean | 获取当前应用 AppInfoBean | +| getApkInfoItem | 获取 APK 详细信息 | +| getAppInfoItem | 获取 APP 详细信息 | +| getAppLists | 获取全部 APP 列表 | +| getAppPermissionToList | 获取 APP 注册的权限 | +| getAppPermissionToSet | 获取 APP 注册的权限 | +| getAppPermission | 获取 APP 注册的权限 | +| printAppPermission | 打印 APP 注册的权限 | + + +* **键对值实体类 ->** [KeyValueBean.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/info/KeyValueBean.java) + +| 方法 | 注释 | +| :- | :- | +| getKey | 获取 key | +| getValue | 获取 value | +| toString | toString | +| get | 通过 resId 设置 key, 并且初始化 KeyValueBean | + + +## **`dev.utils.app.logger`** + + +* **日志操作类 ( 对外公开直接调用 ) ->** [DevLogger.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/DevLogger.java) + +| 方法 | 注释 | +| :- | :- | +| other | 使用单次其他日志配置 | +| getLogConfig | 获取日志配置信息 | +| init | 初始化日志配置信息 ( 使用默认配置 ) | +| d | 打印 Log.DEBUG | +| e | 打印 Log.ERROR | +| w | 打印 Log.WARN | +| i | 打印 Log.INFO | +| v | 打印 Log.VERBOSE | +| wtf | 打印 Log.ASSERT | +| json | 格式化 JSON 格式数据, 并打印 | +| xml | 格式化 XML 格式数据, 并打印 | +| dTag | 打印 Log.DEBUG | +| eTag | 打印 Log.ERROR | +| wTag | 打印 Log.WARN | +| iTag | 打印 Log.INFO | +| vTag | 打印 Log.VERBOSE | +| wtfTag | 打印 Log.ASSERT | +| jsonTag | 格式化 JSON 格式数据, 并打印 | +| xmlTag | 格式化 XML 格式数据, 并打印 | + + +* **日志操作工具类 ->** [DevLoggerUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/DevLoggerUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化调用方法 ( 内部已调用 ) | +| getReleaseLogConfig | 获取 Release Log 配置 ( 打印线程信息、显示方法总数 3、从 0 开始、不进行排序、默认只打印 ERROR 级别日志 ) | +| getDebugLogConfig | 获取 Debug Log 配置 ( 打印线程信息、显示方法总数 3、从 0 开始、不进行排序、默认只打印 ERROR 级别日志 ) | +| getSortLogConfig | 获取 Log 配置 ( 打印线程信息、显示方法总数 3、从 0 开始、并且美化日志信息、默认打印 DEBUG 级别及以上日志 ) | +| getLogConfig | 获取 Log 配置 | +| saveErrorLog | 保存异常日志 | +| saveLog | 保存日志 | + + +## **`dev.utils.app.permission`** + + +* **权限常量 ->** [PermissionConstants.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/permission/PermissionConstants.java) + +| 方法 | 注释 | +| :- | :- | +| getPermissions | 获取权限组 | + + +* **权限请求工具类 ->** [PermissionUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/permission/PermissionUtils.java) + +| 方法 | 注释 | +| :- | :- | +| permission | 申请权限初始化 | +| callBack | 设置回调方法 | +| setRequestPermissionsResult | 设置是否需要在 Activity 的 onRequestPermissionsResult 回调中, 调用 PermissionUtils.onRequestPermissionsResult(this); | +| request | 请求权限 | +| onRequestPermissionsResult | 请求权限回调 - 需要在 Activity 的 onRequestPermissionsResult 回调中, 调用 PermissionUtils.onRequestPermissionsResult(this); | +| notifyPermissionsChange | 刷新权限改变处理 ( 清空已拒绝的权限记录 ) | +| isGranted | 判断是否授予了权限 | +| shouldShowRequestPermissionRationale | 是否拒绝了权限 - 拒绝过一次, 再次申请时, 弹出选择不再提醒并拒绝才会触发 true | +| getAllPermissionToSet | 获取全部权限 | +| getAllPermissionToList | 获取全部权限 | +| getAppPermissionToList | 获取 APP 注册的权限 | +| getAppPermissionToSet | 获取 APP 注册的权限 | +| getAppPermission | 获取 APP 注册的权限 | + + +## **`dev.utils.app.player`** + + +* **MediaPlayer 统一管理类 ->** [DevMediaManager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/player/DevMediaManager.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 DevMediaManager 实例 | +| setAudioStreamType | 设置流类型 | +| playPrepareRaw | 播放 Raw 资源 | +| playPrepareAssets | 播放 Assets 资源 | +| playPrepare | 预加载播放 - (file-path or http/rtsp URL) http 资源、本地资源 | +| isPlaying | 是否播放中 | +| pause | 暂停操作 | +| stop | 停止操作 - 销毁 MediaPlayer | +| isIgnoreWhat | 是否忽略错误类型 | +| setMeidaListener | 设置 MediaPlayer 回调事件 | +| isNullMediaPlayer | 判断 MediaPlayer 是否为 null | +| isNotNullMediaPlayer | 判断 MediaPlayer 是否不为 null | +| getMediaPlayer | 获取 MediaPlayer 对象 | +| setMediaPlayer | 设置 MediaPlayer 对象 | +| setTAG | 设置日志打印 TAG | +| getVolume | 获取播放音量 | +| setVolume | 设置播放音量 | +| getPlayRawId | 获取播放资源 id | +| getPlayUri | 获取播放地址 | +| getVideoWidth | 获取视频宽度 | +| getVideoHeight | 获取视频高度 | +| getCurrentPosition | 获取播放时间 | +| getDuration | 获取资源总时间 | +| getPlayPercent | 获取播放进度百分比 | + + +* **视频播放控制器 ->** [DevVideoPlayerControl.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/player/DevVideoPlayerControl.java) + +| 方法 | 注释 | +| :- | :- | +| setMediaListener | 设置播放监听事件 | +| pausePlayer | 暂停播放 | +| stopPlayer | 停止播放 | +| startPlayer | 开始播放 | +| getSurfaceview | 获取 SurfaceView | +| isPlaying | 是否播放中 | +| isAutoPlay | 判断是否自动播放 | +| setAutoPlay | 设置自动播放 | +| getPlayUri | 获取播放地址 | +| getVideoWidth | 获取视频宽度 | +| getVideoHeight | 获取视频高度 | +| getCurrentPosition | 获取播放时间 | +| getDuration | 获取资源总时间 | +| getPlayPercent | 获取播放进度百分比 | + + +## **`dev.utils.app.share`** + + +* **SPUtils 工具类 ( 单独使用 ) ->** [SharedUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/share/SharedUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化操作 ( 内部已调用 ) | +| put | 保存数据 | +| putAll | 保存 Map 集合 ( 只能是 Integer、Long、Boolean、Float、String、Set) | +| get | 根据 key 获取数据 | +| getAll | 获取全部数据 | +| remove | 移除数据 | +| removeAll | 移除集合的数据 | +| contains | 是否存在 key | +| clear | 清除全部数据 | +| getInt | 获取 int 类型的数据 | +| getFloat | 获取 float 类型的数据 | +| getLong | 获取 long 类型的数据 | +| getBoolean | 获取 boolean 类型的数据 | +| getString | 获取 String 类型的数据 | +| getSet | 获取 Set 类型的数据 | + + +## **`dev.utils.app.toast`** + + +* **自定义 View 着色美化 Toast 工具类 ->** [ToastTintUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/toast/ToastTintUtils.java) + +| 方法 | 注释 | +| :- | :- | +| reset | 重置默认参数 | +| setToastFilter | 设置 Toast 过滤器 | +| setIsHandler | 设置是否使用 Handler 显示 Toast | +| setNullText | 设置 Text 为 null 的文本 | +| setUseConfig | 设置是否使用配置 | +| setGravity | 设置 Toast 显示在屏幕上的位置 | +| setMargin | 设置边距 | +| getDefaultStyle | 获取默认样式 | +| getNormalStyle | 获取 Normal 样式 | +| getInfoStyle | 获取 Info 样式 | +| getWarningStyle | 获取 Warning 样式 | +| getErrorStyle | 获取 Error 样式 | +| getSuccessStyle | 获取 Success 样式 | +| setNormalStyle | 设置 Normal 样式 | +| setInfoStyle | 设置 Info 样式 | +| setWarningStyle | 设置 Warning 样式 | +| setErrorStyle | 设置 Error 样式 | +| setSuccessStyle | 设置 Success 样式 | +| getInfoDrawable | 获取 Info 样式 icon | +| getWarningDrawable | 获取 Warning 样式 icon | +| getErrorDrawable | 获取 Error 样式 icon | +| getSuccessDrawable | 获取 Success 样式 icon | +| normal | normal 样式 Toast | +| info | info 样式 Toast | +| warning | warning 样式 Toast | +| error | error 样式 Toast | +| success | success 样式 Toast | +| custom | custom Toast | + + +* **Simple Toast 工具类 ( 简单的 Toast 工具类, 支持子线程弹出 Toast) ->** [ToastUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/toast/ToastUtils.java) + +| 方法 | 注释 | +| :- | :- | +| reset | 重置默认参数 | +| setToastFilter | 设置 Toast 过滤器 | +| setIsHandler | 设置是否使用 Handler 显示 Toast | +| setNullText | 设置 Text 为 null 的文本 | +| setUseConfig | 设置是否使用配置 | +| setGravity | 设置 Toast 显示在屏幕上的位置 | +| setMargin | 设置边距 | +| showShort | 显示 LENGTH_SHORT Toast | +| showLong | 显示 LENGTH_LONG Toast | +| showToast | 显示 Toast | +| showShortNew | 显示 new LENGTH_SHORT Toast | +| showLongNew | 显示 new LENGTH_LONG Toast | +| showToastNew | 显示新的 Toast | +| newToastText | 获取一个新的 Text Toast | +| showToastView | 显示 View Toast 方法 | +| newToastView | 获取一个新的 View Toast | + + +## **`dev.utils.app.toast.toaster`** + + +* **Toast 工具类 ( 支持子线程弹出 Toast, 处理无通知权限 ) ->** [DevToast.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/DevToast.java) + +| 方法 | 注释 | +| :- | :- | +| reset | 重置默认参数 | +| setIsHandler | 设置是否使用 Handler 显示 Toast | +| setNullText | 设置 Text 为 null 的文本 | +| setTextLength | 设置 Toast 文案长度转换 显示时间 | +| init | Application 初始化调用 ( 内部已调用 ) | +| style | 使用单次 Toast 样式配置 | +| defaultStyle | 使用默认 Toast 样式 | +| getToastStyle | 获取 Toast 样式配置 | +| initStyle | 初始化 Toast 样式配置 | +| initToastFilter | 初始化 Toast 过滤器 | +| setView | 设置 Toast 显示的 View | +| show | 显示 Toast | +| cancel | 取消当前显示的 Toast | + + +## **`dev.utils.app.wifi`** + + +* **Wifi 热点工具类 ->** [WifiHotUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiHotUtils.java) + +| 方法 | 注释 | +| :- | :- | +| createWifiConfigToAp | 创建 wifi 热点配置 ( 支持 无密码 /WPA2 PSK) | +| stratWifiAp | 开启 wifi 热点 | +| closeWifiAp | 关闭 wifi 热点 | +| getWifiApState | 获取 wifi 热点状态 | +| getWifiApConfiguration | 获取 wifi 热点配置信息 | +| setWifiApConfiguration | 设置 wifi 热点配置信息 | +| isOpenWifiAp | 判断是否打开 wifi 热点 | +| closeWifiApCheck | 关闭 wifi 热点 ( 判断当前状态 ) | +| isConnectHot | 是否有设备连接热点 | +| getHotspotServiceIp | 获取热点主机 IP 地址 | +| getHotspotAllotIp | 获取连接上的子网关热点 IP ( 一个 ) | +| getConnectHotspotMsg | 获取连接的热点信息 | +| getHotspotSplitIpMask | 获取热点拼接后的 IP 网关掩码 | +| getApWifiSSID | 获取 wifi 热点名 | +| getApWifiPwd | 获取 wifi 热点密码 | +| setOnWifiAPListener | 设置 wifi 热点监听事件 | + + +* **Wifi 工具类 ->** [WifiUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getWifiManager | 获取 WifiManager | +| isOpenWifi | 判断是否打开 wifi | +| openWifi | 打开 wifi | +| closeWifi | 关闭 wifi | +| toggleWifiEnabled | 自动切换 wifi 开关状态 | +| getWifiState | 获取当前 wifi 连接状态 | +| startScan | 开始扫描 wifi | +| getConfiguration | 获取已配置 ( 连接过 ) 的 wifi 配置 | +| getWifiList | 获取附近的 wifi 列表 | +| getWifiInfo | 获取连接的 WifiInfo | +| getMacAddress | 获取 MAC 地址 | +| getBSSID | 获取连接的 BSSID | +| getIPAddress | 获取 IP 地址 | +| getNetworkId | 获取连接的 Network Id | +| getSSID | 获取 wifi SSID | +| formatSSID | 判断是否存在 \"ssid\", 存在则裁剪返回 | +| getPassword | 获取处理后的密码 | +| isHexWepKey | 判断是否 wep 加密 | +| getWifiType | 获取加密类型 | +| getWifiTypeInt | 获取加密类型 | +| getWifiTypeStr | 获取加密类型 | +| isConnNull | 判断是否连接为 null - unknown ssid | +| isConnectAphot | 获取连接的 wifi 热点 SSID | +| getSecurity | 获取 wifi 加密类型 | +| isExistsPwd | 判断 wifi 加密类型, 是否为加密类型 | +| isExists | 获取指定的 ssid 网络配置 ( 需连接保存过, 才存在 ) | +| delWifiConfig | 删除指定的 wifi(SSID) 配置信息 | +| quickConnWifi | 快速连接 wifi ( 不使用静态 IP 方式 ) | +| createWifiConfig | 创建 wifi 配置信息 | +| removeWifiConfig | 移除 wifi 配置信息 | +| disconnectWifi | 断开指定 networkId 的网络 | + + +## **`dev.utils.common`** + + +* **Array 数组工具类 ->** [ArrayUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/ArrayUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断数组是否为 null | +| isNotEmpty | 判断数组是否不为 null | +| length | 获取数组长度 | +| isLength | 判断数组长度是否等于期望长度 | +| get | 获取数组对应索引数据 | +| getFirst | 获取数组第一条数据 | +| getLast | 获取数组最后一条数据 | +| getPosition | 根据指定值获取 value 所在位置 + 偏移量的索引 | +| getNotNull | 根据指定 value 获取 value 所在位置 + 偏移量的值, 不允许值为 null | +| getPositionNotNull | 根据指定 value 获取索引, 不允许值为 null | +| intsToIntegers | int[] 转换 Integer[] | +| bytesToBytes | byte[] 转换 Byte[] | +| charsToCharacters | char[] 转换 Character[] | +| shortsToShorts | short[] 转换 Short[] | +| longsToLongs | long[] 转换 Long[] | +| floatsToFloats | float[] 转换 Float[] | +| doublesToDoubles | double[] 转换 Double[] | +| booleansToBooleans | boolean[] 转换 Boolean[] | +| integersToInts | Integer[] 转换 int[] | +| charactersToChars | Character[] 转换 char[] | +| asList | 转换数组为集合 | +| equals | 判断两个值是否一样 | +| arraycopy | 拼接数组 | +| newarray | 创建指定长度数组 | +| subarray | 从数组上截取一段 | +| appendToString | 追加数组内容字符串 | + + +* **资金运算工具类 ->** [BigDecimalUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/BigDecimalUtils.java) + +| 方法 | 注释 | +| :- | :- | +| add | 提供精确的加法运算 | +| substract | 提供精确的减法运算 | +| multiply | 提供精确的乘法运算 | +| divide | 提供 ( 相对 ) 精确的除法运算 | +| round | 提供精确的小数位四舍五入处理 | +| remainder | 取余数 | +| compare | 比较大小 | +| formatMoney | 金额分割, 四舍五入金额 | +| adjustDouble | 获取自己想要的数据格式 | + + +* **类 (Class) 工具类 ->** [ClassUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/ClassUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isBaseDataType | 判断类是否是基础数据类型 - 目前支持 11 种 | +| newInstance | 根据类获取对象, 不再必须一个无参构造 | +| getDefaultPrimiticeValue | 判断 Class 是否为原始类型 (boolean、char、byte、short、int、long、float、double) | +| isCollection | 判断是否集合类型 | +| isArray | 判断是否数组类型 | +| getGenericSuperclass | 获取父类泛型类型 | +| getGenericInterfaces | 获取接口泛型类型 | + + +* **克隆工具类 ->** [CloneUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/CloneUtils.java) + +| 方法 | 注释 | +| :- | :- | +| deepClone | 进行克隆 | +| serializableToBytes | 通过序列化实体类, 获取对应的 byte[] 数据 | + + +* **关闭 (IO 流 ) 工具类 ->** [CloseUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/CloseUtils.java) + +| 方法 | 注释 | +| :- | :- | +| closeIO | 关闭 IO | +| closeIOQuietly | 安静关闭 IO | + + +* **集合工具类 (Collection - List、Set、Queue) 等 ->** [CollectionUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/CollectionUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断 Collection 是否为 null | +| isNotEmpty | 判断 Collection 是否不为 null | +| length | 获取 Collection 长度 | +| isLength | 获取长度 to Collection 是否等于期望长度 | +| greaterThan | 判断 Collection 长度是否大于指定长度 | +| greaterThanOrEqual | 判断 Collection 长度是否大于等于指定长度 | +| lessThan | 判断 Collection 长度是否小于指定长度 | +| lessThanOrEqual | 判断 Collection 长度是否小于等于指定长度 | +| get | 获取数据 | +| getFirst | 获取第一条数据 | +| getLast | 获取最后一条数据 | +| getPosition | 根据指定 value 获取 value 所在位置 + 偏移量的索引 | +| getPositionNotNull | 根据指定 value 获取索引, 不允许值为 null | +| getNext | 根据指定 value 获取 value 所在位置的下一个值 | +| getNextNotNull | 根据指定 value 获取 value 所在位置的下一个值, 不允许值为 null | +| getPrevious | 根据指定 value 获取 value 所在位置的上一个值 | +| getPreviousNotNull | 根据指定 value 获取 value 所在位置的上一个值, 不允许值为 null | +| add | 添加一条数据 | +| addNotNull | 添加一条数据 - value 不允许为 null | +| addAll | 添加集合数据 | +| addAllNotNull | 添加集合数据 - values 内的值不允许为 null | +| remove | 移除一条数据 | +| removeAll | 移除集合数据 | +| clear | 清空集合中符合指定 value 的全部数据 | +| clearNotBelong | 保留集合中符合指定 value 的全部数据 | +| clearAll | 清空集合全部数据 | +| clearNull | 清空集合中为 null 的值 | +| isEqualCollection | 判断两个集合是否相同 | +| isEqualCollections | 判断多个集合是否相同 | +| union | 两个集合并集处理 | +| unions | 多个集合并集处理 | +| intersection | 两个集合交集处理 | +| disjunction | 两个集合交集的补集处理 | +| subtract | 两个集合差集 ( 扣除 ) 处理 | +| equals | 判断两个值是否一样 | +| toArray | 转换数组 to Object | +| reverse | 集合翻转处理 | + + +* **颜色工具类 ( 包括常用的色值 ) ->** [ColorUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/ColorUtils.java) + +| 方法 | 注释 | +| :- | :- | +| toHexAlpha | 获取十六进制透明度字符串 | +| alpha | 返回一个颜色中的透明度值 ( 返回十进制 ) | +| alphaPercent | 返回一个颜色中的透明度百分比值 | +| red | 返回一个颜色中红色的色值 ( 返回十进制 ) | +| redPercent | 返回一个颜色中红色的百分比值 | +| green | 返回一个颜色中绿色的色值 ( 返回十进制 ) | +| greenPercent | 返回一个颜色中绿色的百分比值 | +| blue | 返回一个颜色中蓝色的色值 ( 返回十进制 ) | +| bluePercent | 返回一个颜色中蓝色的百分比值 | +| rgb | 根据对应的 red、green、blue 生成一个颜色值 | +| argb | 根据对应的 alpha、red、green、blue 生成一个颜色值 ( 含透明度 ) | +| isRGB | 判断颜色 RGB 是否有效 | +| isARGB | 判断颜色 ARGB 是否有效 | +| setAlpha | 设置透明度 | +| setRed | 改变颜色值中的红色色值 | +| setGreen | 改变颜色值中的绿色色值 | +| setBlue | 改变颜色值中的蓝色色值 | +| parseColor | 解析颜色字符串, 返回对应的颜色值 | +| intToRgbString | 颜色值 转换 RGB 颜色字符串 | +| intToArgbString | 颜色值 转换 ARGB 颜色字符串 | +| getRandomColor | 获取随机颜色值 | +| judgeColorString | 判断是否为 ARGB 格式的十六进制颜色, 例如: FF990587 | +| setDark | 颜色加深 ( 单独修改 RGB 值, 不变动透明度 ) | +| setLight | 颜色变浅, 变亮 ( 单独修改 RGB 值, 不变动透明度 ) | +| setAlphaDark | 设置透明度加深 | +| setAlphaLight | 设置透明度变浅 | + + +* **转换工具类 (Byte、Hex 等 ) ->** [ConvertUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/ConvertUtils.java) + +| 方法 | 注释 | +| :- | :- | +| convert | Object 转换所需类型对象 | +| toString | char[] 转 String | +| toInt | 字符串 转 int | +| toBoolean | 字符串 转 boolean | +| toFloat | 字符串 转 float | +| toDouble | 字符串 转 double | +| toLong | 字符串 转 long | +| toShort | 基本类型对象 转 short | +| toChar | 基本类型对象 转 char | +| toByte | 基本类型对象 转 byte | +| toCharInt | char 转换 unicode 编码 | +| toChars | 字符串 获取 char[] | +| toBytes | 字符串 获取 byte[] | +| parseInt | 字符串转换对应的进制 | +| parseLong | 字符串转换对应的进制 | +| bytesToObject | byte[] 转为 Object | +| objectToBytes | Object 转为 byte[] | +| bytesToChars | byte[] 转换 char[], 并且进行补码 | +| charsToBytes | char[] 转换 byte[] | +| intsToStrings | int[] 转换 string[] | +| doublesToStrings | double[] 转换 string[] | +| longsToStrings | long[] 转换 string[] | +| floatsToStrings | float[] 转换 string[] | +| intsToDoubles | int[] 转换 double[] | +| intsToLongs | int[] 转换 long[] | +| intsToFloats | int[] 转换 float[] | +| stringsToInts | string[] 转换 int[] | +| stringsToDoubles | string[] 转换 double[] | +| stringsToLongs | string[] 转换 long[] | +| stringsToFloats | string[] 转换 float[] | +| doublesToInts | double[] 转换 int[] | +| longsToInts | long[] 转换 int[] | +| floatsToInts | float[] 转换 int[] | +| toBinaryString | 将 字节转换 为 二进制字符串 | +| decodeBinary | 二进制字符串 转换 byte[] 解码 | +| isHex | 判断是否十六进制数据 | +| decodeHex | 将十六进制字节数组解码 | +| hexToInt | 十六进制 char 转换 int | +| toHexString | int 转换十六进制 | +| toHexChars | 将 string 转换为 十六进制 char[] | +| bytesBitwiseAND | 按位求补 byte[] 位移编解码 ( 共用同一个方法 ) | + + +* **坐标 (GPS 纠偏 ) 相关工具类 ->** [CoordinateUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/CoordinateUtils.java) + +| 方法 | 注释 | +| :- | :- | +| bd09ToGcj02 | BD09 坐标转 GCJ02 坐标 | +| gcj02ToBd09 | GCJ02 坐标转 BD09 坐标 | +| gcj02ToWGS84 | GCJ02 坐标转 WGS84 坐标 | +| wgs84ToGcj02 | WGS84 坐标转 GCJ02 坐标 | +| bd09ToWGS84 | BD09 坐标转 WGS84 坐标 | +| wgs84ToBd09 | WGS84 坐标转 BD09 坐标 | +| outOfChina | 判断是否中国境外 | + + +* **日期工具类 ->** [DateUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/DateUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getDateNow | 获取当前日期的字符串 - yyyy-MM-dd HH:mm:ss | +| formatTime | 将时间戳转换日期字符串 | +| formatDate | 将 Date 转换日期字符串 | +| parseDate | 将时间戳转换成 Date | +| parseLong | 解析时间字符串转换为 long 毫秒 - 默认表示 time 属于 yyyy-MM-dd HH:mm:ss 格式 | +| parseToString | 转换时间为指定字符串 | +| getTimeDiffMinute | 获取时间差 - 分钟 | +| getTimeDiffHour | 获取时间差 - 小时 | +| getTimeDiffDay | 获取时间差 - 天 | +| getTimeDiff | 获取时间差 - ( 传入时间 - 当前时间 ) | +| getYear | 获取年 | +| getMonth | 获取月 (0 - 11) + 1 | +| getDay | 获取日 | +| getWeek | 获取日期是星期几 | +| get24Hour | 获取时 - 24 | +| get12Hour | 获取时 - 12 | +| getMinute | 获取分 | +| getSecond | 获取秒 | +| convertTime | 转换时间处理, 小于 10, 则自动补充 0x | +| isLeapYear | 判断是否闰年 | +| getMonthDayNumberAll | 根据年份、月份, 获取对应的天数 ( 完整天数, 无判断是否属于未来日期 ) | +| getYearMonthNumber | 根据年份, 获取对应的月份 | +| getMonthDayNumber | 根据年份、月份, 获取对应的天数 | +| getArrayToHH | 生成 HH 按时间排序数组 | +| getListToHH | 生成 HH 按时间排序集合 | +| getArrayToMM | 生成 MM 按时间排序数组 | +| getListToMM | 生成 MM 按时间排序集合 | +| getArrayToHHMM | 生成 HH:mm 按间隔时间排序数组 | +| getListToHHMM | 生成 HH:mm 按间隔时间排序集合 | +| getListToHHMMPosition | 获取 HH:mm 按间隔时间排序的集合中, 指定时间所在索引 | +| secToTimeRetain | 传入时间, 获取时间 (00:00:00 格式 ) - 不处理大于一天 | +| convertTimeArys | 传入时间, 时间参数 ( 小时、分钟、秒 ) | +| millisToFitTimeSpan | 转换时间 | +| millisToTimeArys | 转换时间为数组 | +| isInTimeHHmm | 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 | +| isInTimeHHmmss | 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 | +| isInTime | 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 | +| isInDate | 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 | +| getEndTimeDiffHHmm | 获取指定时间距离该时间第二天的指定时段的时间 ( 判断凌晨情况 ) | +| getEndTimeDiff | 获取指定时间距离该时间第二天的指定时段的时间差 ( 判断凌晨情况 ) | + + +* **开发常用方法工具类 ->** [DevCommonUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/DevCommonUtils.java) + +| 方法 | 注释 | +| :- | :- | +| timeRecord | 耗时时间记录 | +| getOperateTime | 获取操作时间 | +| sleepOperate | 堵塞操作 | +| isHttpRes | 判断是否网络资源 | +| whileMD5 | 循环 MD5 加密处理 | +| randomUUID | 获取随机唯一数 | +| randomUUIDToHashCode | 获取随机唯一数 HashCode | +| getRandomUUID | 获取随机规则生成 UUID | +| getRandomUUIDToString | 获取随机规则生成 UUID 字符串 | +| converHideMobile | 转换手机号 | +| converSymbolHide | 转换符号处理 | +| subEllipsize | 裁剪超出的内容, 并且追加符号 ( 如 ...) | +| subSymbolHide | 裁剪符号处理 | +| subSetSymbol | 裁剪内容, 设置符号处理 | +| substring | 裁剪字符串 | +| toReplaceSEWith | 替换 ( 删除 - 替换成 "") 字符串中符合 特定标记字符的 startsWith - endsWith | +| toReplaceStartsWith | 替换开头字符串 | +| toReplaceEndsWith | 替换结尾字符串 | +| toClearSEWiths | 这个方法功能主要把字符符合标记的 头部和尾部都替换成 "" | +| toClearStartsWith | 清空属于特定字符串开头的字段 | +| toClearEndsWith | 清空属于特定字符串结尾的字段 | +| replaceStr | 替换字符串 | +| replaceStrToNull | 替换字符串 | +| replaceStrs | 替换字符串 | +| isEmpty | 判断数组是否为 null | +| isNotEmpty | 判断数组是否不为 null | +| length | 获取数组长度 | +| isLength | 判断数组长度是否等于期望长度 | +| equals | 判断两个值是否一样 | +| isEquals | 判断多个字符串是否相等, 只有全相等才返回 true - 对比大小写 | +| isOrEquals | 判断多个字符串, 只要有一个符合条件则通过 | +| isContains | 判断一堆值中, 是否存在符合该条件的 ( 包含 ) | +| isStartsWith | 判断内容, 是否属于特定字符串开头 - 对比大小写 | +| isEndsWith | 判断内容, 是否属于特定字符串结尾 - 对比大小写 | +| isSpace | 判断字符串是否为 null 或全为空白字符 | +| toClearSpace | 清空字符串全部空格 | +| toClearSpaceTrim | 清空字符串前后所有空格 | +| appendSpace | 追加空格 | +| appendTab | 追加 Tab | +| appendLine | 追加换行 | +| toCheckValue | 检查字符串 | +| toCheckValues | 检查字符串 - 多个值 | +| toCheckValuesSpace | 检查字符串 - 多个值 ( 删除前后空格对比判断 ) | +| getFormatString | 获取格式化后的字符串 | +| getAutoFormatString | 获取自动数量格式化后的字符串 ( 可变参数 ) | +| getAutoFormatString2 | 获取自动数量格式化后的字符串 ( 可变参数 ) | +| appends | StringBuilder 拼接处理 | + + +* **编码工具类 ->** [EncodeUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/EncodeUtils.java) + +| 方法 | 注释 | +| :- | :- | +| base64Encode | Base64 编码 | +| base64EncodeToString | Base64 编码 | +| base64Decode | Base64 解码 | +| base64DecodeToString | Base64 解码 | +| htmlEncode | Html 字符串编码 | + + +* **变量字段工具类 ->** [FieldUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/FieldUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isSerializable | 判断是否序列化 | +| set | 设置字段的值 | +| get | 获取字段的值 | +| isLong | 是否 long/Long 类型 | +| isFloat | 是否 float/Float 类型 | +| isDouble | 是否 double/Double 类型 | +| isInteger | 是否 int/Integer 类型 | +| isString | 是否 String 类型 | +| getGenericType | 获取字段的泛型类型, 如果不带泛型返回 null | +| getComponentType | 获取数组的类型 | +| getAllDeclaredFields | 获取全部 Field, 包括父类 | +| isInvalid | 是静态常量或者内部结构属性 | + + +* **文件 (IO 流 ) 工具类 ->** [FileIOUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/FileIOUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setBufferSize | 设置缓冲区的大小, 默认大小等于 8192 字节 | +| writeFileFromIS | 通过输入流写入文件 | +| writeFileFromBytesByStream | 通过字节流写入文件 | +| writeFileFromBytesByChannel | 通过 FileChannel 把字节流写入文件 | +| writeFileFromBytesByMap | 通过 MappedByteBuffer 把字节流写入文件 | +| writeFileFromString | 通过字符串写入文件 | +| readFileToList | 读取文件内容, 返回换行 List | +| readFileToString | 读取文件内容, 返回字符串 | +| readFileToBytesByStream | 读取文件内容, 返回 byte[] | +| readFileToBytesByChannel | 通过 FileChannel, 读取文件内容, 返回 byte[] | +| readFileToBytesByMap | 通过 MappedByteBuffer, 读取文件内容, 返回 byte[] | + + +* **文件操作工具类 ->** [FileUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/FileUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getFile | 获取文件 | +| getFileByPath | 获取文件 | +| getFileCreateFolder | 获取路径, 并且进行创建目录 | +| getFilePathCreateFolder | 获取路径, 并且进行创建目录 | +| createFolder | 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) | +| createFolderByPath | 创建文件夹目录 - 可以传入文件名 | +| createFolderByPaths | 创建多个文件夹, 如果不存在则创建 | +| createOrExistsDir | 判断目录是否存在, 不存在则判断是否创建成功 | +| createOrExistsFile | 判断文件是否存在, 不存在则判断是否创建成功 | +| createFileByDeleteOldFile | 判断文件是否存在, 存在则在创建之前删除 | +| getPath | 获取文件路径 | +| getAbsolutePath | 获取文件绝对路径 | +| getName | 获取文件名 | +| getFileSuffix | 获取文件后缀名 ( 无 "." 单独后缀 ) | +| getFileNotSuffix | 获取文件名 ( 无后缀 ) | +| getFileNotSuffixToPath | 获取文件名 ( 无后缀 ) | +| getFileNameNoExtension | 获取路径中的不带拓展名的文件名 | +| getFileExtension | 获取路径中的文件拓展名 | +| isFileExists | 检查是否存在某个文件 | +| isFile | 判断是否文件 | +| isDirectory | 判断是否文件夹 | +| isHidden | 判断是否隐藏文件 | +| getFileLastModified | 获取文件最后修改的毫秒时间戳 | +| getFileCharsetSimple | 获取文件编码格式 | +| getFileLines | 获取文件行数 | +| getFileSize | 获取文件大小 | +| getDirSize | 获取目录大小 | +| getFileLength | 获取文件大小 | +| getDirLength | 获取目录全部文件大小 | +| getFileLengthNetwork | 获取文件大小 - 网络资源 | +| getFileName | 获取路径中的文件名 | +| getDirName | 获取路径中的最长目录地址 | +| rename | 重命名文件 - 同个目录下, 修改文件名 | +| formatFileSize | 传入文件路径, 返回对应的文件大小 | +| formatByteMemorySize | 字节数转合适内存大小 保留 3 位小数 (%.位数f) | +| deleteFile | 删除文件 | +| deleteFiles | 删除多个文件 | +| deleteFolder | 删除文件夹 | +| saveFile | 保存文件 | +| appendFile | 追加文件 ( 使用 FileWriter) | +| readFileBytes | 读取文件 | +| readFile | 读取文件 | +| copyFile | 复制单个文件 | +| copyFolder | 复制文件夹 | +| moveFile | 移动 ( 剪切 ) 文件 | +| moveFolder | 移动 ( 剪切 ) 文件夹 | +| copyOrMoveDir | 复制或移动目录 | +| copyOrMoveFile | 复制或移动文件 | +| copyDir | 复制目录 | +| moveDir | 移动目录 | +| deleteDir | 删除目录 | +| deleteAllInDir | 删除目录下所有东西 | +| deleteFilesInDir | 删除目录下所有文件 | +| deleteFilesInDirWithFilter | 删除目录下所有过滤的文件 | +| listFilesInDir | 获取目录下所有文件 - 不递归进子目录 | +| listFilesInDirWithFilter | 获取目录下所有过滤的文件 - 不递归进子目录 | +| getFileMD5 | 获取文件 MD5 值 | +| getFileMD5ToHexString | 获取文件 MD5 值 | + + +* **Http 参数工具类 ->** [HttpParamsUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/HttpParamsUtils.java) + +| 方法 | 注释 | +| :- | :- | +| splitParams | 拆分参数 | +| joinParams | 拼接请求参数 - value(String) | +| joinParamsObj | 拼接请求参数 - value(Object) | +| toConvertObjToMS | 进行转换对象处理 ( 请求发送对象 ) | +| toConvertObjToMO | 进行转换对象处理 ( 请求发送对象 ) | +| toUrlEncode | 进行 URL 编码, 默认 UTF-8 | + + +* **HttpURLConnection 网络工具类 ->** [HttpURLConnectionUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/HttpURLConnectionUtils.java) + +| 方法 | 注释 | +| :- | :- | +| doGetAsyn | 异步的 Get 请求 | +| doPostAsyn | 异步的 Post 请求 | +| request | 发送请求 | +| getNetTime | 获取网络时间 - 默认使用百度链接 | + + +* **Map 工具类 ->** [MapUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/MapUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断 Map 是否为 null | +| isNotEmpty | 判断 Map 是否不为 null | +| length | 获取 Map 长度 | +| isLength | 获取长度 to Map 是否等于期望长度 | +| greaterThan | 判断 Map 长度是否大于指定长度 | +| greaterThanOrEqual | 判断 Map 长度是否大于等于指定长度 | +| lessThan | 判断 Map 长度是否小于指定长度 | +| lessThanOrEqual | 判断 Map 长度是否小于等于指定长度 | +| get | 获取 value | +| getKeyByValue | 通过 value 获取 key | +| getKeysByValue | 通过 value 获取 key 集合 ( 返回等于 value 的 key 集合 ) | +| getKeys | 通过 Map 获取 key 集合 | +| getKeysToArrays | 通过 Map 获取 key 数组 | +| getValues | 通过 Map 获取 value 集合 | +| getValuesToArrays | 通过 Map 获取 value 数组 | +| getFirst | 获取第一条数据 | +| getLast | 获取最后一条数据 | +| getNext | 根据指定 key 获取 key 所在位置的下一条数据 | +| getPrevious | 根据指定 key 获取 key 所在位置的上一条数据 | +| put | 添加一条数据 | +| putNotNull | 添加一条数据, 不允许 key 为 null | +| putAll | 添加多条数据 | +| putAllNotNull | 添加多条数据, 不允许 key 为 null | +| remove | 移除一条数据 | +| removeToKeys | 移除多条数据 | +| removeToValue | 移除等于 value 的所有数据 | +| removeToValues | 移除等于 value 的所有数据 (Collection) | +| equals | 判断两个值是否一样 | +| toggle | 切换保存状态 | +| isNullToValue | 判断指定 key 的 value 是否为 null | +| containsKey | 判断 Map 是否存储了 key | +| containsValue | 判断 Map 是否存储了 value | +| putToList | 添加一条数据 - (Value) List | +| removeToList | 移除一条数据 - (Value) List | +| removeToLists | 移除多条数据 - (Value) List | +| removeToMap | 移除多条数据 - 通过 Map 进行移除 | + + +* **数字 ( 计算 ) 工具类 ->** [NumberUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/NumberUtils.java) + +| 方法 | 注释 | +| :- | :- | +| percentI | 计算百分比值 ( 最大 100%) | +| percentD | 计算百分比值 ( 最大 100%) | +| percentL | 计算百分比值 ( 最大 100%) | +| percentF | 计算百分比值 ( 最大 100%) | +| percentI2 | 计算百分比值 ( 可超出 100%) | +| percentD2 | 计算百分比值 ( 可超出 100%) | +| percentL2 | 计算百分比值 ( 可超出 100%) | +| percentF2 | 计算百分比值 ( 可超出 100%) | +| clamp | 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max | +| getMultiple | 获取倍数 ( 自动补 1) | +| getMultipleI | 获取倍数 | +| getMultipleD | 获取倍数 | +| getMultipleL | 获取倍数 | +| getMultipleF | 获取倍数 | +| numberToCHN | 数字转中文数值 | +| isNumber | 检验数字 | +| isNumberDecimal | 检验数字或包含小数点 | + + +* **对象相关工具类 ->** [ObjectUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/ObjectUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断对象是否为空 | +| isNotEmpty | 判断对象是否非空 | +| equals | 判断两个值是否一样 | +| requireNonNull | 检查对象是否为 null, 为 null 则抛出异常, 不为 null 则返回该对象 | +| getOrDefault | 获取非空或默认对象 | +| hashCode | 获取对象哈希值 | +| getObjectTag | 获取一个对象的独一无二的标记 | +| converObj | 获取转换对象 | + + +* **随机工具类 ->** [RandomUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/RandomUtils.java) + +| 方法 | 注释 | +| :- | :- | +| nextBoolean | 获取伪随机 boolean 值 | +| nextBytes | 获取伪随机 byte[] | +| nextDouble | 获取伪随机 double 值 | +| nextGaussian | 获取伪随机高斯分布值 | +| nextFloat | 获取伪随机 float 值 | +| nextInt | 获取伪随机 int 值 | +| nextLong | 获取伪随机 long 值 | +| getRandomNumbers | 获取数字自定义长度的随机数 | +| getRandomLowerCaseLetters | 获取小写字母自定义长度的随机数 | +| getRandomCapitalLetters | 获取大写字母自定义长度的随机数 | +| getRandomLetters | 获取大小写字母自定义长度的随机数 | +| getRandomNumbersAndLetters | 获取数字、大小写字母自定义长度的随机数 | +| getRandom | 获取自定义数据自定义长度的随机数 | +| shuffle | 洗牌算法 ( 第一种 ), 随机置换指定的数组使用的默认源的随机性 ( 随机数据源小于三个, 则无效 ) | +| shuffle2 | 洗牌算法 ( 第二种 ), 随机置换指定的数组使用的默认源的随机性 | +| nextIntRange | 获取指定范围 int 值 | +| nextLongRange | 获取指定范围 long 值 | +| nextDoubleRange | 获取指定范围 double 值 | +| ints | 获取随机 int[] | +| longs | 获取随机 long[] | +| doubles | 获取随机 double[] | + + +* **反射相关工具类 ->** [Reflect2Utils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/Reflect2Utils.java) + +| 方法 | 注释 | +| :- | :- | +| setProperty | 设置某个对象变量值 ( 可设置静态变量 ) | +| getProperty | 获取某个对象的变量 ( 可获取静态变量 ) | +| getStaticProperty | 获取某个类的静态变量 ( 只能获取静态变量 ) | +| invokeMethod | 执行某个对象方法 ( 可执行静态方法 ) | +| invokeStaticMethod | 执行某个类的静态方法 ( 只能执行静态方法 ) | +| newInstance | 新建实例 ( 构造函数创建 ) | +| isInstance | 是不是某个类的实例 | +| getArgsClass | 获取参数类型 | +| getPropertyByParent | 获取父类中的变量对象 | +| getDeclaredFieldParent | 获取父类中的变量对象 ( 循环向上转型, 获取对象的 DeclaredField) | + + +* **反射相关工具类 ->** [ReflectUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/ReflectUtils.java) + +| 方法 | 注释 | +| :- | :- | +| reflect | 设置要反射的类 | +| newInstance | 实例化反射对象 | +| field | 设置反射的字段 | +| setEnumVal | 设置枚举值 | +| method | 设置反射的方法 | +| proxy | 根据类, 代理创建并返回对象 | +| type | 获取类型 | +| get | 获取反射想要获取的 | +| hashCode | 获取 HashCode | +| equals | 判断反射的两个对象是否一样 | +| toString | 获取反射获取的对象 | + + +* **计算比例工具类 ->** [ScaleUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/ScaleUtils.java) + +| 方法 | 注释 | +| :- | :- | +| calcScaleToWidth | 计算缩放比例 - 根据宽度比例转换高度 | +| calcScaleToHeight | 计算缩放比例 - 根据高度比例转换宽度 | +| calcWidthHeightToScale | 通过宽度、高度根据对应的比例, 转换成对应的比例宽度高度 - 智能转换 | +| calcWidthToScale | 以宽度为基准, 转换对应比例的高度 | +| calcHeightToScale | 以高度为基准, 转换对应比例的宽度 | + + +* **流操作工具类 ->** [StreamUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/StreamUtils.java) + +| 方法 | 注释 | +| :- | :- | +| inputToOutputStream | 输入流转输出流 | +| outputToInputStream | 输出流转输入流 | +| inputStreamToBytes | 输入流转 byte[] | +| bytesToInputStream | byte[] 转输出流 | +| outputStreamToBytes | 输出流转 byte[] | +| bytesToOutputStream | byte[] 转 输出流 | +| inputStreamToString | 输入流转 String | +| stringToInputStream | String 转换输入流 | +| outputStreamToString | 输出流转 String | +| stringToOutputStream | String 转 输出流 | + + +* **字符串工具类 ->** [StringUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/StringUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断字符串是否为 null | +| isNotEmpty | 判断字符串是否不为 null | +| length | 获取字符串长度 | +| isLength | 获取字符串长度 是否等于期望长度 | +| equals | 判断两个值是否一样 | +| isEquals | 判断多个字符串是否相等, 只有全相等才返回 true - 对比大小写 | +| isOrEquals | 判断多个字符串, 只要有一个符合条件则通过 | +| countMatches | 统计字符串匹配个数 | +| countMatches2 | 统计字符串匹配个数 | +| isContains | 判断一堆值中, 是否存在符合该条件的 ( 包含 ) | +| isStartsWith | 判断内容, 是否属于特定字符串开头 - 对比大小写 | +| isEndsWith | 判断内容, 是否属于特定字符串结尾 - 对比大小写 | +| isSpace | 判断字符串是否为 null 或全为空白字符 | +| toClearSpace | 清空字符串全部空格 | +| toClearSpaceTrim | 清空字符串前后所有空格 | +| appendSpace | 追加空格 | +| appendTab | 追加 Tab | +| appendLine | 追加换行 | +| colonSplit | 冒号分割处理 | +| toCheckValue | 检查字符串 | +| toCheckValues | 检查字符串 - 多个值 | +| toCheckValuesSpace | 检查字符串 - 多个值 ( 删除前后空格对比判断 ) | +| getFormatString | 获取格式化后的字符串 | +| getAutoFormatString | 获取自动数量格式化后的字符串 ( 可变参数 ) | +| getAutoFormatString2 | 获取自动数量格式化后的字符串 ( 可变参数 ) | +| appends | StringBuilder 拼接处理 | +| toGBKEncode | 字符串进行 GBK 编码 | +| toGBK2312Encode | 字符串进行 GBK2312 编码 | +| toUTF8Encode | 字符串进行 UTF-8 编码 | +| toStrEncode | 进行字符串编码 | +| toUrlEncode | 进行 URL 编码, 默认 UTF-8 | +| toUrlDecode | 进行 URL 解码, 默认 UTF-8 | +| toASCII | 将字符串转移为 ASCII 码 | +| toUnicode | 将字符串转移为 Unicode 码 | +| toUnicodeString | 将字符数组转移为 Unicode 码 | +| toDBC | 转化为半角字符 | +| toSBC | 转化为全角字符 如: a = a, A = A | +| checkChineseToString | 检测字符串是否全是中文 | +| isChinese | 判定输入汉字 | +| upperFirstLetter | 首字母大写 | +| lowerFirstLetter | 首字母小写 | +| reverse | 反转字符串 | +| concat | 字符串连接, 将参数列表拼接为一个字符串 | +| concatSpiltWith | 字符串连接, 将参数列表拼接为一个字符串 | +| underScoreCaseToCamelCase | 下划线命名转为驼峰命名 | +| camelCaseToUnderScoreCase | 驼峰命名法转为下划线命名 | +| sqliteEscape | 字符串数据库字符转义 | + + +* **异常处理工具类 ->** [ThrowableUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/ThrowableUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getThrowable | 获取异常信息 | +| getThrowableStackTrace | 获取异常栈信息 | + + +* **压缩相关工具类 ->** [ZipUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/ZipUtils.java) + +| 方法 | 注释 | +| :- | :- | +| zipFiles | 批量压缩文件 | +| zipFile | 压缩文件 | +| unzipFile | 解压文件 | +| unzipFileByKeyword | 解压带有关键字的文件 | +| getFilesPath | 获取压缩文件中的文件路径链表 | +| getComments | 获取压缩文件中的注释链表 | + + +## **`dev.utils.common.assist`** + + +* **均值计算 ( 用以统计平均数 ) 辅助类 ->** [Averager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/assist/Averager.java) + +| 方法 | 注释 | +| :- | :- | +| add | 添加一个数字 | +| clear | 清除全部 | +| size | 获取参与均值计算的数字个数 | +| getAverage | 获取平均数 | +| print | 输出参与均值计算的数字 | + + +* **时间均值计算辅助类 ->** [TimeAverager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/assist/TimeAverager.java) + +| 方法 | 注释 | +| :- | :- | +| start | 开始计时 ( 毫秒 ) | +| end | 结束计时 ( 毫秒 ) | +| endAndRestart | 结束计时, 并重新启动新的计时 | +| average | 求全部计时均值 | +| print | 输出全部时间值 | +| clear | 清除计时数据 | + + +* **时间计时辅助类 ->** [TimeCounter.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/assist/TimeCounter.java) + +| 方法 | 注释 | +| :- | :- | +| start | 开始计时 ( 毫秒 ) | +| durationRestart | 获取持续的时间并重新启动 ( 毫秒 ) | +| duration | 获取持续的时间 ( 毫秒 ) | +| getStartTime | 获取开始时间 ( 毫秒 ) | + + +* **堵塞时间辅助类 ->** [TimeKeeper.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/assist/TimeKeeper.java) + +| 方法 | 注释 | +| :- | :- | +| waitForEndAsyn | 设置等待一段时间后, 通知方法 ( 异步 ) | +| waitForEnd | 设置等待一段时间后, 通知方法 ( 同步 ) | + + +## **`dev.utils.common.assist.search`** + + +* **文件广度优先搜索算法 ( 多线程 + 队列, 搜索某个目录下的全部文件 ) ->** [FileBreadthFirstSearchUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setSearchHandler | 设置搜索处理接口 | +| getQueueSameTimeNumber | 获取任务队列同时进行数量 | +| setQueueSameTimeNumber | 任务队列同时进行数量 | +| isRunning | 是否搜索中 | +| stop | 停止搜索 | +| isStop | 是否停止搜索 | +| getStartTime | 获取开始搜索时间 ( 毫秒 ) | +| getEndTime | 获取结束搜索时间 ( 毫秒 ) | +| getDelayTime | 获取延迟校验时间 ( 毫秒 ) | +| setDelayTime | 设置延迟校验时间 ( 毫秒 ) | +| query | 搜索目录 | + + +* **文件深度优先搜索算法 ( 递归搜索某个目录下的全部文件 ) ->** [FileDepthFirstSearchUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setSearchHandler | 设置搜索处理接口 | +| isRunning | 是否搜索中 | +| stop | 停止搜索 | +| isStop | 是否停止搜索 | +| getStartTime | 获取开始搜索时间 ( 毫秒 ) | +| getEndTime | 获取结束搜索时间 ( 毫秒 ) | +| query | 搜索目录 | + + +## **`dev.utils.common.cipher`** + + +* **Base64 工具类 ->** [Base64.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/cipher/Base64.java) + +| 方法 | 注释 | +| :- | :- | +| decode | Decode the Base64-encoded data in input and return the data in | +| encodeToString | Base64-encode the given data and return a newly allocated | +| encode | Base64-encode the given data and return a newly allocated | + + +* **Base64 编解码 ( 并进行 ) 加解密 ->** [Base64Cipher.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/cipher/Base64Cipher.java) + +| 方法 | 注释 | +| :- | :- | +| decrypt | 解码 | +| encrypt | 编码 | + + +* **加密工具类 ->** [CipherUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/cipher/CipherUtils.java) + +| 方法 | 注释 | +| :- | :- | +| encrypt | 加密方法 | +| decrypt | 解密方法 | + + +## **`dev.utils.common.encrypt`** + + +* **AES 对称加密工具类 ->** [AESUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/encrypt/AESUtils.java) + +| 方法 | 注释 | +| :- | :- | +| initKey | 生成密钥 | +| encrypt | AES 加密 | +| decrypt | AES 解密 | + + +* **CRC 工具类 ->** [CRCUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/encrypt/CRCUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getCRC32 | 获取 CRC32 值 | +| getCRC32ToHexString | 获取 CRC32 值 | +| getFileCRC32 | 获取文件 CRC32 值 | + + +* **DES 对称加密工具类 ->** [DESUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/encrypt/DESUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getDESKey | 获取可逆算法 DES 的密钥 | +| encrypt | DES 加密 | +| decrypt | DES 解密 | + + +* **加解密通用工具类 ->** [EncryptUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/encrypt/EncryptUtils.java) + +| 方法 | 注释 | +| :- | :- | +| encryptMD2 | MD2 加密 | +| encryptMD2ToHexString | MD2 加密 | +| encryptMD5 | MD5 加密 | +| encryptMD5ToHexString | MD5 加密 | +| encryptMD5File | 获取文件 MD5 值 | +| encryptMD5FileToHexString | 获取文件 MD5 值 | +| encryptSHA1 | SHA1 加密 | +| encryptSHA1ToHexString | SHA1 加密 | +| encryptSHA224 | SHA224 加密 | +| encryptSHA224ToHexString | SHA224 加密 | +| encryptSHA256 | SHA256 加密 | +| encryptSHA256ToHexString | SHA256 加密 | +| encryptSHA384 | SHA384 加密 | +| encryptSHA384ToHexString | SHA384 加密 | +| encryptSHA512 | SHA512 加密 | +| encryptSHA512ToHexString | SHA512 加密 | +| encryptHmacMD5 | HmacMD5 加密 | +| encryptHmacMD5ToHexString | HmacMD5 加密 | +| encryptHmacSHA1 | HmacSHA1 加密 | +| encryptHmacSHA1ToHexString | HmacSHA1 加密 | +| encryptHmacSHA224 | HmacSHA224 加密 | +| encryptHmacSHA224ToHexString | HmacSHA224 加密 | +| encryptHmacSHA256 | HmacSHA256 加密 | +| encryptHmacSHA256ToHexString | HmacSHA256 加密 | +| encryptHmacSHA384 | HmacSHA384 加密 | +| encryptHmacSHA384ToHexString | HmacSHA384 加密 | +| encryptHmacSHA512 | HmacSHA512 加密 | +| encryptHmacSHA512ToHexString | HmacSHA512 加密 | +| encryptDES | DES 加密 | +| encryptDESToBase64 | DES 加密 | +| encryptDESToHexString | DES 加密 | +| decryptDES | DES 解密 | +| decryptDESToBase64 | DES 解密 | +| decryptDESToHexString | DES 解密 | +| encrypt3DES | 3DES 加密 | +| encrypt3DESToBase64 | 3DES 加密 | +| encrypt3DESToHexString | 3DES 加密 | +| decrypt3DES | 3DES 解密 | +| decrypt3DESToBase64 | 3DES 解密 | +| decrypt3DESToHexString | 3DES 解密 | +| encryptAES | AES 加密 | +| encryptAESToBase64 | AES 加密 | +| encryptAESToHexString | AES 加密 | +| decryptAES | AES 解密 | +| decryptAESToBase64 | AES 解密 | +| decryptAESToHexString | AES 解密 | +| encryptRSA | RSA 加密 | +| encryptRSAToBase64 | RSA 加密 | +| encryptRSAToHexString | RSA 加密 | +| decryptRSA | RSA 解密 | +| decryptRSAToBase64 | RSA 解密 | +| decryptRSAToHexString | RSA 解密 | + + +* **字符串 ( 编解码 ) 工具类 ->** [EscapeUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/encrypt/EscapeUtils.java) + +| 方法 | 注释 | +| :- | :- | +| escape | 编码 | +| unescape | 解码 - 本方法不论参数 data 是否经过 escape() 编码, 均能获取正确的 ( 解码 ) 结果 | + + +* **MD5 加密工具类 ->** [MD5Utils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/encrypt/MD5Utils.java) + +| 方法 | 注释 | +| :- | :- | +| md5 | 加密内容 (32 位小写 MD5) | +| md5Upper | 加密内容 (32 位大写 MD5) | +| getFileMD5 | 获取文件 MD5 值 | +| getFileMD5ToHexString | 获取文件 MD5 值 | + + +* **SHA 加密工具类 ->** [SHAUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/encrypt/SHAUtils.java) + +| 方法 | 注释 | +| :- | :- | +| sha1 | 加密内容 SHA1 | +| sha224 | 加密内容 SHA224 | +| sha256 | 加密内容 SHA256 | +| sha384 | 加密内容 SHA384 | +| sha512 | 加密内容 SHA512 | +| getFileSHA1 | 获取文件 SHA1 值 | +| getFileSHA256 | 获取文件 SHA256 值 | +| shaHex | 加密内容 SHA 模板 | +| getFileSHA | 获取文件 SHA 值 | + + +* **3DES 对称加密工具类 ->** [TripleDESUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java) + +| 方法 | 注释 | +| :- | :- | +| initKey | 生成密钥 | +| encrypt | 3DES 加密 | +| decrypt | 3DES 解密 | + + +* **异或 ( 加密 ) 工具类 ->** [XorUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/encrypt/XorUtils.java) + +| 方法 | 注释 | +| :- | :- | +| encryptAsFix | 加解密 ( 固定 Key 方式 ) 这种方式 加解密 方法共用 | +| encrypt | 加密 ( 非固定 Key 方式 ) | +| decrypt | 解密 ( 非固定 Key 方式 ) | + + +## **`dev.utils.common.random`** + + +* **随机概率采样算法 ->** [AliasMethod.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/random/AliasMethod.java) + +| 方法 | 注释 | +| :- | :- | +| next | 获取随机索引 ( 对应几率索引 ) | + + +## **`dev.utils.common.thread`** + + +* **线程池管理工具类 ->** [DevThreadManager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadManager.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 DevThreadManager 实例 | +| initConfig | 初始化配置信息 | +| putConfig | 添加配置信息 | +| removeConfig | 移除配置信息 | + + +* **线程池 ( 构建类 ) ->** [DevThreadPool.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadPool.java) + +| 方法 | 注释 | +| :- | :- | +| getThreads | 获取线程数 | +| getCalcThreads | 获取线程数 | +| execute | 加入到线程池任务队列 | +| shutdown | shutdown 会等待所有提交的任务执行完成, 不管是正在执行还是保存在任务队列中的已提交任务 | +| shutdownNow | shutdownNow 会尝试中断正在执行的任务 ( 其主要是中断一些指定方法如 sleep 方法 ), 并且停止执行等待队列中提交的任务 | +| isShutdown | 判断线程池是否已关闭 - isShutDown 当调用 shutdown() 方法后返回为 true | +| isTerminated | 若关闭后所有任务都已完成, 则返回 true | +| awaitTermination | 请求关闭、发生超时或者当前线程中断 | +| submit | 提交一个 Callable 任务用于执行 | +| invokeAll | 执行给定的任务 | +| invokeAny | 执行给定的任务 | +| schedule | 延迟执行 Runnable 命令 | +| scheduleWithFixedRate | 延迟并循环执行命令 | +| scheduleWithFixedDelay | 延迟并以固定休息时间循环执行命令 | + + +## **`dev.utils.common.validator`** + + +* **银行卡管理工具类 ->** [BankCheckUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/validator/BankCheckUtils.java) + +| 方法 | 注释 | +| :- | :- | +| checkBankCard | 校验银行卡卡号是否合法 | +| getBankCardCheckCode | 从不含校验位的银行卡卡号采用 Luhm 校验算法获取校验位 | +| getNameOfBank | 通过银行卡的 前六位确定 判断银行开户行及卡种 | + + +* **居民身份证工具类 ->** [IDCardUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/validator/IDCardUtils.java) + +| 方法 | 注释 | +| :- | :- | +| validateIdCard15 | 身份证校验规则, 验证 15 位身份编码是否合法 | +| validateIdCard18 | 身份证校验规则, 验证 18 位身份编码是否合法 | +| convert15CardTo18 | 将 15 位身份证号码转换为 18 位 | +| validateTWCard | 验证台湾身份证号码 | +| validateHKCard | 验证香港身份证号码 ( 部份特殊身份证无法检查 ) | +| validateIdCard10 | 判断 10 位数的身份证号, 是否合法 | +| validateCard | 验证身份证是否合法 | +| getAgeByIdCard | 根据身份编号获取年龄 | +| getBirthByIdCard | 根据身份编号获取生日 | +| getBirthdayByIdCard | 根据身份编号获取生日 | +| getYearByIdCard | 根据身份编号获取生日 ( 年份 ) | +| getMonthByIdCard | 根据身份编号获取生日 ( 月份 ) | +| getDateByIdCard | 根据身份编号获取生日 ( 天数 ) | +| getGenderByIdCard | 根据身份编号获取性别 | +| getProvinceByIdCard | 根据身份编号获取户籍省份 | +| getPowerSum | 将身份证的每位和对应位的加权因子相乘之后, 再获取和值 | +| getCheckCode18 | 将 POWER 和值与 11 取模获取余数进行校验码判断 | + + +* **校验工具类 ->** [ValidatorUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/validator/ValidatorUtils.java) + +| 方法 | 注释 | +| :- | :- | +| match | 通用匹配函数 | +| isNumber | 检验数字 | +| isNumberDecimal | 检验数字或包含小数点 | +| isLetter | 判断字符串是不是全是字母 | +| isContainNumber | 判断字符串是不是包含数字 | +| isNumberLetter | 判断字符串是不是只含字母和数字 | +| isSpec | 检验特殊符号 | +| isWx | 检验微信号 | +| isRealName | 检验真实姓名 | +| isNickName | 校验昵称 | +| isUserName | 校验用户名 | +| isPassword | 校验密码 | +| isEmail | 校验邮箱 | +| isUrl | 校验 URL | +| isIPAddress | 校验 IP 地址 | +| isChinese | 校验汉字 ( 无符号, 纯汉字 ) | +| isChineseAll | 判断字符串是不是全是中文 | +| isContainChinese | 判断字符串中包含中文、包括中文字符标点等 | + + +* **检验联系 ( 手机号, 座机 ) 工具类 ->** [ValiToPhoneUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isPhoneCheck | 中国手机号格式验证, 在输入可以调用该方法, 点击发送验证码, 使用 isPhone | +| isPhone | 是否中国手机号 | +| isPhoneToChinaTelecom | 是否中国电信手机号码 | +| isPhoneToChinaUnicom | 是否中国联通手机号码 | +| isPhoneToChinaMobile | 是否中国移动手机号码 | +| isPhoneToHkMobile | 判断是否香港手机号 | +| isPhoneCallNum | 验证电话号码的格式 | \ No newline at end of file diff --git a/lib/DevApp/build.gradle b/lib/DevApp/build.gradle new file mode 100644 index 0000000000..bcea315f74 --- /dev/null +++ b/lib/DevApp/build.gradle @@ -0,0 +1,21 @@ +apply { + from rootProject.file(files.app_lib_gradle) +} + +android.defaultConfig { + versionCode versions.dev_app_versionCode + versionName versions.dev_app_versionName + // DevJava Module Version + buildConfigField "String", "DevJava_Version", "\"${versions.dev_java_version}\"" + buildConfigField "int", "DevJava_VersionCode", "${versions.dev_java_versionCode}" +} + +dependencies { + implementation deps.android.design + compileOnly deps.android.appcompat_v7 +} + +// gradlew clean +// gradlew install +// gradlew bintrayUpload +//apply from : rootProject.file(files.app_bintray_upload) \ No newline at end of file diff --git a/DevLibUtils/proguard-rules.pro b/lib/DevApp/proguard-rules.pro similarity index 63% rename from DevLibUtils/proguard-rules.pro rename to lib/DevApp/proguard-rules.pro index f1b424510d..4bf8de3fd0 100644 --- a/DevLibUtils/proguard-rules.pro +++ b/lib/DevApp/proguard-rules.pro @@ -19,3 +19,22 @@ # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile + +-dontwarn android.support.** + +# Understand the @Keep support annotation. +-keep class android.support.annotation.Keep + +-keep @android.support.annotation.Keep class * {*;} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep ; +} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep ; +} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep (...); +} \ No newline at end of file diff --git a/lib/DevApp/project.properties b/lib/DevApp/project.properties new file mode 100644 index 0000000000..dbe940dab7 --- /dev/null +++ b/lib/DevApp/project.properties @@ -0,0 +1,10 @@ +#project +project.name=DevApp +project.groupId=com.afkt +project.artifactId=DevApp +project.packaging=aar +project.siteUrl=https://github.com/afkT/DevUtils +project.gitUrl=https://github.com/afkT/DevUtils.git + +#javadoc +javadoc.name=DevApp \ No newline at end of file diff --git a/lib/DevApp/src/main/AndroidManifest.xml b/lib/DevApp/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..07951be857 --- /dev/null +++ b/lib/DevApp/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/lib/DevApp/src/main/java/dev/DevUtils.java b/lib/DevApp/src/main/java/dev/DevUtils.java new file mode 100644 index 0000000000..49eed610d4 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/DevUtils.java @@ -0,0 +1,968 @@ +package dev; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Application; +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.content.FileProvider; +import android.text.TextUtils; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import dev.utils.BuildConfig; +import dev.utils.JCLogUtils; +import dev.utils.LogPrintUtils; +import dev.utils.app.AnalysisRecordUtils; +import dev.utils.app.FileRecordUtils; +import dev.utils.app.HandlerUtils; +import dev.utils.app.KeyBoardUtils; +import dev.utils.app.UriUtils; +import dev.utils.app.cache.DevCache; +import dev.utils.app.logger.DevLoggerUtils; +import dev.utils.app.share.SharedUtils; +import dev.utils.app.toast.toaster.DevToast; +import dev.utils.common.FileUtils; + +/** + * detail: 开发工具类 + * @author Ttt + *
+ *     GitHub
+ *     @see 
+ *     DevApp Api
+ *     @see 
+ *     DevJava Api
+ *     @see 
+ *     DevAssist Api
+ *     @see 
+ *     DevOther Api
+ *     @see 
+ * 
+ */ +public final class DevUtils { + + private DevUtils() { + } + + // 日志 TAG + private static final String TAG = DevUtils.class.getSimpleName(); + // 全局 Application 对象 + private static Application sApplication; + // 全局 Context - getApplicationContext() + private static Context sContext; + // 是否内部 Debug 模式 + private static boolean sDebug = false; + + /** + * 初始化方法 ( 必须调用 ) + * @param context {@link Context} + */ + public static void init(final Context context) { + // 初始化全局 Context + initContext(context); + // 初始化全局 Application + initApplication(context); + // 注册 Activity 生命周期监听 + registerActivityLifecycleCallbacks(sApplication); + + // ==================== + // = 初始化工具类相关 = + // ==================== + + // 初始化 Cache + DevCache.get(context); + // 初始化 SharedPreferences + SharedUtils.init(context); + // 初始化 File Record + FileRecordUtils.init(); + // 初始化 Record + AnalysisRecordUtils.init(); + // 初始化 DevLogger + DevLoggerUtils.init(); + // 初始化 Toast + DevToast.init(sApplication); + + // ============ + // = Java Log = + // ============ + + // 设置 Java 模块日志信息在 logcat 输出 + JCLogUtils.setPrint(new JCLogUtils.Print() { + @Override + public void printLog(int logType, String tag, String message) { + switch (logType) { + case JCLogUtils.INFO: + LogPrintUtils.iTag(tag, message); + case JCLogUtils.ERROR: + LogPrintUtils.eTag(tag, message); + break; + case JCLogUtils.DEBUG: + default: + LogPrintUtils.dTag(tag, message); + break; + } + } + }); + } + + /** + * 初始化全局 Context + * @param context {@link Context} + */ + private static void initContext(final Context context) { + if (DevUtils.sContext == null && context != null) { + DevUtils.sContext = context.getApplicationContext(); + } + } + + /** + * 初始化全局 Application + * @param context {@link Context} + */ + private static void initApplication(final Context context) { + if (DevUtils.sApplication == null && context != null) { + try { + DevUtils.sApplication = (Application) context.getApplicationContext(); + } catch (Exception e) { + } + } + } + + /** + * 获取全局 Context + * @return {@link Context} + */ + public static Context getContext() { + return DevUtils.sContext; + } + + /** + * 获取 Context ( 判断 null, 视情况返回全局 Context) + * @param context {@link Context} + * @return {@link Context} + */ + public static Context getContext(final Context context) { + // 进行判断 + if (context != null) { + return context; + } + return DevUtils.sContext; + } + + /** + * 获取全局 Application + * @return {@link Application} + */ + public static Application getApplication() { + if (DevUtils.sApplication != null) return DevUtils.sApplication; + try { + Application application = getApplicationByReflect(); + if (application != null) { + init(application); // 初始化操作 + } + return application; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getApplication"); + } + return null; + } + + // = + + /** + * 反射获取 Application + * @return {@link Application} + * @throws NullPointerException + */ + private static Application getApplicationByReflect() { + try { + @SuppressLint("PrivateApi") + Class activityThread = Class.forName("android.app.ActivityThread"); + Object thread = activityThread.getMethod("currentActivityThread").invoke(null); + Object app = activityThread.getMethod("getApplication").invoke(thread); + if (app == null) { + throw new NullPointerException("u should init first"); + } + return (Application) app; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getApplicationByReflect"); + } + throw new NullPointerException("u should init first"); + } + + // = + + /** + * 获取 Handler + * @return {@link Handler} + */ + public static Handler getHandler() { + return HandlerUtils.getMainHandler(); + } + + /** + * 执行 UI 线程任务 + * @param runnable 线程任务 + */ + public static void runOnUiThread(final Runnable runnable) { + HandlerUtils.postRunnable(runnable); + } + + /** + * 执行 UI 线程任务 - 延时执行 + * @param runnable 线程任务 + * @param delayMillis 延时执行时间 ( 毫秒 ) + */ + public static void runOnUiThread(final Runnable runnable, final long delayMillis) { + HandlerUtils.postRunnable(runnable, delayMillis); + } + + /** + * 开启日志开关 + */ + public static void openLog() { + // 专门打印 Android 日志信息 + LogPrintUtils.setPrintLog(true); + // 专门打印 Java 日志信息 + JCLogUtils.setPrintLog(true); + } + + /** + * 标记 Debug 模式 + */ + public static void openDebug() { + DevUtils.sDebug = true; + } + + /** + * 判断是否 Debug 模式 + * @return {@code true} yes, {@code false} no + */ + public static boolean isDebug() { + return sDebug; + } + + // ============== + // = 工具类版本 = + // ============== + + /** + * 获取 DevApp 工具类版本 + * @return DevApp versionName + */ + public static String getDevAppUtilsVersion() { + return BuildConfig.VERSION_NAME; + } + + /** + * 获取 DevApp 工具类版本号 + * @return DevApp versionCode + */ + public static int getDevAppUtilsVersionCode() { + return BuildConfig.VERSION_CODE; + } + + /** + * 获取 DevJava 工具类版本 + * @return DevJava version + */ + public static String getDevJavaUtilsVersion() { + return BuildConfig.DevJava_Version; + } + + /** + * 获取 DevJava 工具类版本号 + * @return DevJava version + */ + public static int getDevJavaUtilsVersionCode() { + return BuildConfig.DevJava_VersionCode; + } + + // ================= + // = Activity 监听 = + // ================= + + // ActivityLifecycleCallbacks 实现类, 监听 Activity + private static final ActivityLifecycleImpl ACTIVITY_LIFECYCLE = new ActivityLifecycleImpl(); + // Activity 过滤判断接口 + private static ActivityLifecycleFilter sActivityLifecycleFilter; + // 权限 Activity.class name + public static final String PERMISSION_ACTIVITY_CLASS_NAME = "dev.utils.app.permission.PermissionUtils$PermissionActivity"; + + /** + * 注册绑定 Activity 生命周期事件处理 + * @param application {@link Application} + */ + private static void registerActivityLifecycleCallbacks(final Application application) { + // 先移除监听 + unregisterActivityLifecycleCallbacks(application); + // 防止为 null + if (application != null) { + try { + // 绑定新的监听 + application.registerActivityLifecycleCallbacks(ACTIVITY_LIFECYCLE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "registerActivityLifecycleCallbacks"); + } + } + } + + /** + * 解除注册 Activity 生命周期事件处理 + * @param application {@link Application} + */ + private static void unregisterActivityLifecycleCallbacks(final Application application) { + if (application != null) { + try { + // 先移除旧的监听 + application.unregisterActivityLifecycleCallbacks(ACTIVITY_LIFECYCLE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "unregisterActivityLifecycleCallbacks"); + } + } + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 获取 Activity 生命周期 相关信息获取接口类 + * @return {@link ActivityLifecycleGet} + */ + public static ActivityLifecycleGet getActivityLifecycleGet() { + return ACTIVITY_LIFECYCLE; + } + + /** + * 获取 Activity 生命周期 事件监听接口类 + * @return {@link ActivityLifecycleNotify} + */ + public static ActivityLifecycleNotify getActivityLifecycleNotify() { + return ACTIVITY_LIFECYCLE; + } + + /** + * 获取 Top Activity + * @return {@link Activity} + */ + public static Activity getTopActivity() { + return ACTIVITY_LIFECYCLE.getTopActivity(); + } + + /** + * 设置 Activity 生命周期 过滤判断接口 + * @param activityLifecycleFilter Activity 过滤判断接口 + */ + public static void setActivityLifecycleFilter(final ActivityLifecycleFilter activityLifecycleFilter) { + DevUtils.sActivityLifecycleFilter = activityLifecycleFilter; + } + + // ============ + // = 接口相关 = + // ============ + + /** + * detail: 对 Activity 的生命周期事件进行集中处理, ActivityLifecycleCallbacks 实现方法 + * @author Ttt + */ + private static class ActivityLifecycleImpl implements Application.ActivityLifecycleCallbacks, ActivityLifecycleGet, ActivityLifecycleNotify { + + // 保存未销毁的 Activity + private final LinkedList mActivityLists = new LinkedList<>(); + // APP 状态改变事件 + private final Map mStatusListenerMaps = new ConcurrentHashMap<>(); + // Activity 销毁事件 + private final Map> mDestroyedListenerMaps = new ConcurrentHashMap<>(); + + // 前台 Activity 总数 + private int mForegroundCount = 0; + // Activity Configuration 改变次数 + private int mConfigCount = 0; + // 是否后台 Activity + private boolean mIsBackground = false; + + // ============================== + // = ActivityLifecycleCallbacks = + // ============================== + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + setTopActivity(activity); + + if (DevUtils.sAbstractActivityLifecycle != null) { + DevUtils.sAbstractActivityLifecycle.onActivityCreated(activity, savedInstanceState); + } + } + + @Override + public void onActivityStarted(Activity activity) { + if (!mIsBackground) { + setTopActivity(activity); + } + if (mConfigCount < 0) { + ++mConfigCount; + } else { + ++mForegroundCount; + } + + if (DevUtils.sAbstractActivityLifecycle != null) { + DevUtils.sAbstractActivityLifecycle.onActivityStarted(activity); + } + } + + @Override + public void onActivityResumed(Activity activity) { + setTopActivity(activity); + // Activity 准备可见, 设置为非后台 Activity + if (mIsBackground) { + mIsBackground = false; + postStatus(true); + } + + if (DevUtils.sAbstractActivityLifecycle != null) { + DevUtils.sAbstractActivityLifecycle.onActivityResumed(activity); + } + } + + @Override + public void onActivityPaused(Activity activity) { + if (DevUtils.sAbstractActivityLifecycle != null) { + DevUtils.sAbstractActivityLifecycle.onActivityPaused(activity); + } + } + + @Override + public void onActivityStopped(Activity activity) { + // 检测当前的 Activity 是否因为 Configuration 的改变被销毁了 + if (activity.isChangingConfigurations()) { + --mConfigCount; + } else { + --mForegroundCount; + if (mForegroundCount <= 0) { + mIsBackground = true; + postStatus(false); + } + } + + if (DevUtils.sAbstractActivityLifecycle != null) { + DevUtils.sAbstractActivityLifecycle.onActivityStopped(activity); + } + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + if (DevUtils.sAbstractActivityLifecycle != null) { + DevUtils.sAbstractActivityLifecycle.onActivitySaveInstanceState(activity, outState); + } + } + + @Override + public void onActivityDestroyed(Activity activity) { + mActivityLists.remove(activity); + // 通知 Activity 销毁 + consumeOnActivityDestroyedListener(activity); + // 修复软键盘内存泄漏 在 Activity.onDestroy() 中使用 + KeyBoardUtils.fixSoftInputLeaks(activity); + + if (DevUtils.sAbstractActivityLifecycle != null) { + DevUtils.sAbstractActivityLifecycle.onActivityDestroyed(activity); + } + } + + // ==================== + // = 内部处理判断方法 = + // ==================== + + /** + * 保存 Activity 栈顶 + * @param activity {@link Activity} + */ + private void setTopActivity(final Activity activity) { + // 判断是否过滤 Activity + if (ACTIVITY_LIFECYCLE_FILTER.filter(activity)) return; + // 判断是否已经包含该 Activity + if (mActivityLists.contains(activity)) { + if (!mActivityLists.getLast().equals(activity)) { + mActivityLists.remove(activity); + mActivityLists.addLast(activity); + } + } else { + mActivityLists.addLast(activity); + } + } + + /** + * 反射获取栈顶 Activity + * @return {@link Activity} + */ + private Activity getTopActivityByReflect() { + try { + @SuppressLint("PrivateApi") + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null); + Field activitiesField = activityThreadClass.getDeclaredField("mActivityLists"); + activitiesField.setAccessible(true); + Map activities = (Map) activitiesField.get(activityThread); + if (activities == null) return null; + for (Object activityRecord : activities.values()) { + Class activityRecordClass = activityRecord.getClass(); + Field pausedField = activityRecordClass.getDeclaredField("paused"); + pausedField.setAccessible(true); + if (!pausedField.getBoolean(activityRecord)) { + Field activityField = activityRecordClass.getDeclaredField("activity"); + activityField.setAccessible(true); + return (Activity) activityField.get(activityRecord); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTopActivityByReflect"); + } + return null; + } + + // ============================= + // = ActivityLifecycleGet 方法 = + // ============================= + + /** + * 获取最顶部 ( 当前或最后一个显示 ) Activity + * @return {@link Activity} + */ + @Override + public Activity getTopActivity() { + if (!mActivityLists.isEmpty()) { + final Activity topActivity = mActivityLists.getLast(); + if (topActivity != null) { + return topActivity; + } + } + Activity topActivityByReflect = getTopActivityByReflect(); + if (topActivityByReflect != null) { + setTopActivity(topActivityByReflect); + } + return topActivityByReflect; + } + + /** + * 判断某个 Activity 是否 Top Activity + * @param activityClassName Activity.class.getCanonicalName() + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isTopActivity(final String activityClassName) { + if (!TextUtils.isEmpty(activityClassName)) { + Activity activity = getTopActivity(); + // 判断是否类是否一致 + return (activity != null && activity.getClass().getCanonicalName().equals(activityClassName)); + } + return false; + } + + /** + * 判断某个 Class(Activity) 是否 Top Activity + * @param clazz Activity.class or this.getClass() + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isTopActivity(final Class clazz) { + if (clazz != null) { + Activity activity = getTopActivity(); + // 判断是否类是否一致 + return (activity != null && activity.getClass().getCanonicalName().equals(clazz.getCanonicalName())); + } + return false; + } + + /** + * 判断应用是否在后台 ( 不可见 ) + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isBackground() { + return mIsBackground; + } + + /** + * 获取 Activity 总数 + * @return 已打开 Activity 总数 + */ + @Override + public int getActivityCount() { + return mActivityLists.size(); + } + + // =========================== + // = ActivityLifecycleNotify = + // =========================== + + /** + * 添加 APP 状态改变事件监听 + * @param object key + * @param listener APP 状态改变监听事件 + */ + @Override + public void addOnAppStatusChangedListener(final Object object, final OnAppStatusChangedListener listener) { + mStatusListenerMaps.put(object, listener); + } + + /** + * 移除 APP 状态改变事件监听 + * @param object key + */ + @Override + public void removeOnAppStatusChangedListener(final Object object) { + mStatusListenerMaps.remove(object); + } + + /** + * 移除全部 APP 状态改变事件监听 + */ + @Override + public void removeAllOnAppStatusChangedListener() { + mStatusListenerMaps.clear(); + } + + // = + + /** + * 添加 Activity 销毁通知事件 + * @param activity {@link Activity} + * @param listener Activity 销毁通知事件 + */ + @Override + public void addOnActivityDestroyedListener(final Activity activity, final OnActivityDestroyedListener listener) { + if (activity == null || listener == null) return; + Set listeners; + if (!mDestroyedListenerMaps.containsKey(activity)) { + listeners = new HashSet<>(); + mDestroyedListenerMaps.put(activity, listeners); + } else { + listeners = mDestroyedListenerMaps.get(activity); + if (listeners.contains(listener)) return; + } + listeners.add(listener); + } + + /** + * 移除 Activity 销毁通知事件 + * @param activity {@link Activity} + */ + @Override + public void removeOnActivityDestroyedListener(final Activity activity) { + if (activity == null) return; + mDestroyedListenerMaps.remove(activity); + } + + /** + * 移除全部 Activity 销毁通知事件 + */ + @Override + public void removeAllOnActivityDestroyedListener() { + mDestroyedListenerMaps.clear(); + } + + // ================ + // = 事件通知相关 = + // ================ + + /** + * 发送状态改变通知 + * @param isForeground 是否在前台 + */ + private void postStatus(final boolean isForeground) { + if (mStatusListenerMaps.isEmpty()) return; + // 保存到新的集合, 防止 ConcurrentModificationException + List lists = new ArrayList<>(mStatusListenerMaps.values()); + // 遍历通知 + for (OnAppStatusChangedListener listener : lists) { + if (listener != null) { + if (isForeground) { + listener.onForeground(); + } else { + listener.onBackground(); + } + } + } + } + + /** + * 通知 Activity 销毁, 并且消费 ( 移除 ) 监听事件 + * @param activity {@link Activity} + */ + private void consumeOnActivityDestroyedListener(final Activity activity) { + try { + // 保存到新的集合, 防止 ConcurrentModificationException + Set sets = new HashSet<>(mDestroyedListenerMaps.get(activity)); + // 遍历通知 + for (OnActivityDestroyedListener listener : sets) { + if (listener != null) { + listener.onActivityDestroyed(activity); + } + } + } catch (Exception e) { + } + // 移除已消费的事件 + removeOnActivityDestroyedListener(activity); + } + } + + /** + * detail: Activity 生命周期 相关信息获取接口 + * @author Ttt + */ + public interface ActivityLifecycleGet { + + /** + * 获取最顶部 ( 当前或最后一个显示 ) Activity + * @return {@link Activity} + */ + Activity getTopActivity(); + + /** + * 判断某个 Activity 是否 Top Activity + * @param activityClassName Activity.class.getCanonicalName() + * @return {@code true} yes, {@code false} no + */ + boolean isTopActivity(String activityClassName); + + /** + * 判断某个 Class(Activity) 是否 Top Activity + * @param clazz Activity.class or this.getClass() + * @return {@code true} yes, {@code false} no + */ + boolean isTopActivity(Class clazz); + + /** + * 判断应用是否在后台 ( 不可见 ) + * @return {@code true} yes, {@code false} no + */ + boolean isBackground(); + + /** + * 获取 Activity 总数 + * @return 已打开 Activity 总数 + */ + int getActivityCount(); + } + + /** + * detail: Activity 生命周期 过滤判断接口 + * @author Ttt + */ + public interface ActivityLifecycleFilter { + + /** + * 判断是否过滤该类 ( 不进行添加等操作 ) + * @param activity {@link Activity} + * @return {@code true} yes, {@code false} no + */ + boolean filter(Activity activity); + } + + /** + * detail: Activity 生命周期 通知接口 + * @author Ttt + */ + public interface ActivityLifecycleNotify { + + /** + * 添加 APP 状态改变事件监听 + * @param object key + * @param listener APP 状态改变监听事件 + */ + void addOnAppStatusChangedListener(Object object, OnAppStatusChangedListener listener); + + /** + * 移除 APP 状态改变事件监听 + * @param object key + */ + void removeOnAppStatusChangedListener(Object object); + + /** + * 移除全部 APP 状态改变事件监听 + */ + void removeAllOnAppStatusChangedListener(); + + // = + + /** + * 添加 Activity 销毁通知事件 + * @param activity {@link Activity} + * @param listener Activity 销毁通知事件 + */ + void addOnActivityDestroyedListener(Activity activity, OnActivityDestroyedListener listener); + + /** + * 移除 Activity 销毁通知事件 + * @param activity {@link Activity} + */ + void removeOnActivityDestroyedListener(Activity activity); + + /** + * 移除全部 Activity 销毁通知事件 + */ + void removeAllOnActivityDestroyedListener(); + } + + /** + * detail: APP 状态改变事件 + * @author Ttt + */ + public interface OnAppStatusChangedListener { + + /** + * 切换到前台 + */ + void onForeground(); + + /** + * 切换到后台 + */ + void onBackground(); + } + + /** + * detail: Activity 销毁事件 + * @author Ttt + */ + public interface OnActivityDestroyedListener { + + /** + * Activity 销毁通知 + * @param activity {@link Activity} + */ + void onActivityDestroyed(Activity activity); + } + + // ============ + // = 接口实现 = + // ============ + + // 内部 Activity 生命周期过滤处理 + private static ActivityLifecycleFilter ACTIVITY_LIFECYCLE_FILTER = new ActivityLifecycleFilter() { + @Override + public boolean filter(Activity activity) { + if (activity != null) { + if (PERMISSION_ACTIVITY_CLASS_NAME.equals(activity.getClass().getName())) { + // 如果相同则不处理 ( 该页面为内部权限框架, 申请权限页面 ) + return true; + } else { + if (sActivityLifecycleFilter != null) { + return sActivityLifecycleFilter.filter(activity); + } + } + } + return false; + } + }; + + // = + + // ActivityLifecycleCallbacks 抽象类 + private static AbstractActivityLifecycle sAbstractActivityLifecycle; + + /** + * 设置 ActivityLifecycle 监听回调 + * @param abstractActivityLifecycle Activity 生命周期监听类 + */ + public static void setAbstractActivityLifecycle(final AbstractActivityLifecycle abstractActivityLifecycle) { + DevUtils.sAbstractActivityLifecycle = abstractActivityLifecycle; + } + + /** + * detail: ActivityLifecycleCallbacks 抽象类 + * @author Ttt + */ + public static abstract class AbstractActivityLifecycle implements Application.ActivityLifecycleCallbacks { + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + } + + @Override + public void onActivityStarted(Activity activity) { + } + + @Override + public void onActivityResumed(Activity activity) { + } + + @Override + public void onActivityPaused(Activity activity) { + } + + @Override + public void onActivityStopped(Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + } + + @Override + public void onActivityDestroyed(Activity activity) { + } + } + + // ================ + // = FileProvider = + // ================ + + // 获取 lib utils fileProvider + public static final String LIB_FILE_PROVIDER = "devapp.provider"; + + /** + * 获取 FileProvider Authority + * @return FileProvider Authority + */ + public static String getAuthority() { + try { + return DevUtils.getContext().getPackageName() + "." + LIB_FILE_PROVIDER; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAuthority"); + } + return null; + } + + /** + * 获取 FileProvider File Uri + * @param file 文件 + * @return 指定文件 {@link Uri} + */ + public static Uri getUriForFile(final File file) { + return UriUtils.getUriForFile(file, getAuthority()); + } + + /** + * 获取 FileProvider File Path Uri + * @param filePath 文件路径 + * @return 指定文件 {@link Uri} + */ + public static Uri getUriForPath(final String filePath) { + return getUriForFile(FileUtils.getFileByPath(filePath)); + } + + /** + * detail: FileProvider + * @author Ttt + */ + public static final class FileProviderDevApp extends FileProvider { + @Override + public boolean onCreate() { + init(getContext()); + return true; + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/JCLogUtils.java b/lib/DevApp/src/main/java/dev/utils/JCLogUtils.java new file mode 100644 index 0000000000..91dc60871e --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/JCLogUtils.java @@ -0,0 +1,276 @@ +package dev.utils; + +import java.io.StringReader; +import java.io.StringWriter; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * detail: Java Common 日志打印工具类 ( 简化版 ) - 项目内部使用 ( 主要打印 Java 日志 ) + * @author Ttt + */ +public final class JCLogUtils { + + private JCLogUtils() { + } + + // 是否打印日志 线上 (release) = false, 开发 (debug) = true + private static boolean JUDGE_PRINT_LOG = false; + // 判断是否控制台打印信息 + private static boolean JUDGE_CONTROL_PRINT_LOG = false; + // 默认 DEFAULT_TAG + private static final String DEFAULT_TAG = JCLogUtils.class.getSimpleName(); + + // ============ + // = 日志类型 = + // ============ + + // INFO 模式 + public static final int INFO = 0; + // DEBUG 模式 + public static final int DEBUG = 1; + // ERROR 模式 + public static final int ERROR = 2; + + /** + * 判断是否打印日志 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPrintLog() { + return JUDGE_PRINT_LOG; + } + + /** + * 设置是否打印日志 + * @param judgePrintLog 是否允许打印日志 + */ + public static void setPrintLog(final boolean judgePrintLog) { + JUDGE_PRINT_LOG = judgePrintLog; + } + + /** + * 设置是否在控制台打印日志 + * @param judgeControlPrintLog 是否允许控制台打印日志 + */ + public static void setControlPrintLog(final boolean judgeControlPrintLog) { + JUDGE_CONTROL_PRINT_LOG = judgeControlPrintLog; + } + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + // = + + /** + * 最终打印日志方法 ( 全部调用此方法 ) + * @param logType 日志类型 + * @param tag 打印 Tag + * @param message 日志信息 + */ + private static void printLog(final int logType, final String tag, final String message) { + if (JCLogUtils.print != null) { + JCLogUtils.print.printLog(logType, tag, message); + } + + if (JUDGE_CONTROL_PRINT_LOG) { + // 打印信息 + if (isEmpty(tag)) { + System.out.println(message); + } else { + System.out.println(tag + " : " + message); + } + } + } + + /** + * 处理信息 + * @param message 日志信息 + * @param args 占位符替换 + * @return 处理 ( 格式化 ) 后准备打印的日志信息 + */ + private static String createMessage(final String message, final Object... args) { + String result; + try { + if (message != null) { + if (args == null) { + // 动态参数为 null + result = "params is null"; + } else { + // 格式化字符串 + result = (args.length == 0 ? message : String.format(message, args)); + } + } else { + // 打印内容为 null + result = "message is null"; + } + } catch (Exception e) { + // 出现异常 + result = e.toString(); + } + return result; + } + + /** + * 拼接错误信息 + * @param throwable 错误异常 + * @param message 需要打印的消息 + * @param args 动态参数 + * @return 处理 ( 格式化 ) 后准备打印的日志信息 + */ + private static String splitErrorMessage(final Throwable throwable, final String message, final Object... args) { + String result; + try { + if (throwable != null) { + if (message != null) { + result = createMessage(message, args) + " : " + throwable.toString(); + } else { + result = throwable.toString(); + } + } else { + result = createMessage(message, args); + } + } catch (Exception e) { + result = e.toString(); + } + return result; + } + + // =============================== + // = 对外公开方法 - 使用默认 TAG = + // =============================== + + public static void d(final String message, final Object... args) { + dTag(DEFAULT_TAG, message, args); + } + + public static void e(final Throwable throwable) { + eTag(DEFAULT_TAG, throwable); + } + + public static void e(final String message, final Object... args) { + e(null, message, args); + } + + public static void e(final Throwable throwable, final String message, final Object... args) { + eTag(DEFAULT_TAG, throwable, message, args); + } + + public static void i(final String message, final Object... args) { + iTag(DEFAULT_TAG, message, args); + } + + public static void xml(final String xml) { + xmlTag(DEFAULT_TAG, xml); + } + + // =============================== + // = 对外公开方法 - 日志打印方法 = + // =============================== + + public static void dTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(DEBUG, tag, createMessage(message, args)); + } + } + + public static void eTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(ERROR, tag, createMessage(message, args)); + } + } + + public static void eTag(final String tag, final Throwable throwable) { + if (JUDGE_PRINT_LOG) { + printLog(ERROR, tag, splitErrorMessage(throwable, null)); + } + } + + public static void eTag(final String tag, final Throwable throwable, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(ERROR, tag, splitErrorMessage(throwable, message, args)); + } + } + + public static void iTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(INFO, tag, createMessage(message, args)); + } + } + + public static void xmlTag(final String tag, final String xml) { + if (JUDGE_PRINT_LOG) { + // 判断传入 XML 格式信息是否为 null + if (isEmpty(xml)) { + printLog(ERROR, tag, "Empty/Null xml content"); + return; + } + try { + Source xmlInput = new StreamSource(new StringReader(xml)); + StreamResult xmlOutput = new StreamResult(new StringWriter()); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.transform(xmlInput, xmlOutput); + // 获取打印消息 + String message = xmlOutput.getWriter().toString().replaceFirst(">", ">\n"); + // 打印信息 + printLog(DEBUG, tag, message); + } catch (Exception e) { + String errorInfo = "null"; + if (e != null) { + Throwable throwable = e.getCause(); + if (throwable != null) { + errorInfo = throwable.toString(); + } else { + try { + errorInfo = e.toString(); + } catch (Exception e1) { + errorInfo = e1.toString(); + } + } + } + printLog(ERROR, tag, errorInfo + "\n" + xml); + } + } + } + + // ============ + // = 通知输出 = + // ============ + + private static Print print; + + /** + * 设置日志输出接口 + * @param print 日志输出接口 + */ + public static void setPrint(final Print print) { + JCLogUtils.print = print; + } + + /** + * detail: 日志输出接口 + * @author Ttt + */ + public interface Print { + + /** + * 日志打印 + * @param logType 日志类型 + * @param tag 打印 Tag + * @param message 日志信息 + */ + void printLog(final int logType, final String tag, final String message); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/LogPrintUtils.java b/lib/DevApp/src/main/java/dev/utils/LogPrintUtils.java new file mode 100644 index 0000000000..1a4263a402 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/LogPrintUtils.java @@ -0,0 +1,326 @@ +package dev.utils; + +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.StringReader; +import java.io.StringWriter; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * detail: Android 日志打印工具类 ( 简化版 ) - 项目内部使用 ( 主要打印 Android 日志 ) + * @author Ttt + */ +public final class LogPrintUtils { + + private LogPrintUtils() { + } + + // JSON 格式内容缩进 + private static final int JSON_INDENT = 4; + // 是否打印日志 线上 (release) = false, 开发 (debug) = true + private static boolean JUDGE_PRINT_LOG = false; + // 默认 DEFAULT_TAG + private static final String DEFAULT_TAG = LogPrintUtils.class.getSimpleName(); + + /** + * 判断是否打印日志 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPrintLog() { + return JUDGE_PRINT_LOG; + } + + /** + * 设置是否打印日志 + * @param judgePrintLog 是否允许打印日志 + */ + public static void setPrintLog(final boolean judgePrintLog) { + JUDGE_PRINT_LOG = judgePrintLog; + } + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + // = + + /** + * 最终打印日志方法 ( 全部调用此方法 ) + * @param logType 日志类型 + * @param tag 打印 Tag + * @param message 日志信息 + */ + private static void printLog(final int logType, final String tag, final String message) { + // 防止 null 处理 + if (message == null) return; + // 获取日志类型 + switch (logType) { + case Log.ERROR: + Log.e(tag, message); + break; + case Log.INFO: + Log.i(tag, message); + break; + case Log.VERBOSE: + Log.v(tag, message); + break; + case Log.WARN: + Log.w(tag, message); + break; + case Log.ASSERT: + Log.wtf(tag, message); + break; + case Log.DEBUG: + Log.d(tag, message); + break; + default: + Log.d(tag, message); + break; + } + } + + /** + * 处理信息 + * @param message 日志信息 + * @param args 占位符替换 + * @return 处理 ( 格式化 ) 后准备打印的日志信息 + */ + private static String createMessage(final String message, final Object... args) { + String result; + try { + if (message != null) { + if (args == null) { + // 动态参数为 null + result = "params is null"; + } else { + // 格式化字符串 + result = (args.length == 0 ? message : String.format(message, args)); + } + } else { + // 打印内容为 null + result = "message is null"; + } + } catch (Exception e) { + // 出现异常 + result = e.toString(); + } + return result; + } + + /** + * 拼接错误信息 + * @param throwable 错误异常 + * @param message 需要打印的消息 + * @param args 动态参数 + * @return 处理 ( 格式化 ) 后准备打印的日志信息 + */ + private static String splitErrorMessage(final Throwable throwable, final String message, final Object... args) { + String result; + try { + if (throwable != null) { + if (message != null) { + result = createMessage(message, args) + " : " + throwable.toString(); + } else { + result = throwable.toString(); + } + } else { + result = createMessage(message, args); + } + } catch (Exception e) { + result = e.toString(); + } + return result; + } + + // =============================== + // = 对外公开方法 - 使用默认 TAG = + // =============================== + + public static void d(final String message, final Object... args) { + dTag(DEFAULT_TAG, message, args); + } + + public static void e(final Throwable throwable) { + eTag(DEFAULT_TAG, throwable, null); + } + + public static void e(final String message, final Object... args) { + e(null, message, args); + } + + public static void e(final Throwable throwable, final String message, final Object... args) { + eTag(DEFAULT_TAG, throwable, message, args); + } + + public static void w(final String message, final Object... args) { + wTag(DEFAULT_TAG, message, args); + } + + public static void i(final String message, final Object... args) { + iTag(DEFAULT_TAG, message, args); + } + + public static void v(final String message, final Object... args) { + vTag(DEFAULT_TAG, message, args); + } + + public static void wtf(final String message, final Object... args) { + wtfTag(DEFAULT_TAG, message, args); + } + + public static void json(final String json) { + jsonTag(DEFAULT_TAG, json); + } + + public static void xml(final String xml) { + xmlTag(DEFAULT_TAG, xml); + } + + // =============================== + // = 对外公开方法 - 日志打印方法 = + // =============================== + + public static void dTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(Log.DEBUG, tag, createMessage(message, args)); + } + } + + public static void eTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(Log.ERROR, tag, createMessage(message, args)); + } + } + + public static void eTag(final String tag, final Throwable throwable) { + if (JUDGE_PRINT_LOG) { + printLog(Log.ERROR, tag, splitErrorMessage(throwable, null)); + } + } + + public static void eTag(final String tag, final Throwable throwable, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(Log.ERROR, tag, splitErrorMessage(throwable, message, args)); + } + } + + public static void wTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(Log.WARN, tag, createMessage(message, args)); + } + } + + public static void iTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(Log.INFO, tag, createMessage(message, args)); + } + } + + public static void vTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(Log.VERBOSE, tag, createMessage(message, args)); + } + } + + public static void wtfTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(Log.ASSERT, tag, createMessage(message, args)); + } + } + + public static void jsonTag(final String tag, final String json) { + if (JUDGE_PRINT_LOG) { + // 判断传入 JSON 格式信息是否为 null + if (isEmpty(json)) { + printLog(Log.ERROR, tag, "Empty/Null json content"); + return; + } + try { + // 属于对象的 JSON 格式信息 + if (json.startsWith("{")) { + JSONObject jsonObject = new JSONObject(json); + // 进行缩进 + String message = jsonObject.toString(JSON_INDENT); + // 打印信息 + printLog(Log.DEBUG, tag, message); + } else if (json.startsWith("[")) { + // 属于数据的 JSON 格式信息 + JSONArray jsonArray = new JSONArray(json); + // 进行缩进 + String message = jsonArray.toString(JSON_INDENT); + // 打印信息 + printLog(Log.DEBUG, tag, message); + } else { + // 打印信息 + printLog(Log.DEBUG, tag, "json content format error"); + } + } catch (Exception e) { + String errorInfo = "null"; + if (e != null) { + Throwable throwable = e.getCause(); + if (throwable != null) { + errorInfo = throwable.toString(); + } else { + try { + errorInfo = e.toString(); + } catch (Exception e1) { + errorInfo = e1.toString(); + } + } + } + printLog(Log.ERROR, tag, errorInfo + "\n" + json); + } + } + } + + public static void xmlTag(final String tag, final String xml) { + if (JUDGE_PRINT_LOG) { + // 判断传入 XML 格式信息是否为 null + if (isEmpty(xml)) { + printLog(Log.ERROR, tag, "Empty/Null xml content"); + return; + } + try { + Source xmlInput = new StreamSource(new StringReader(xml)); + StreamResult xmlOutput = new StreamResult(new StringWriter()); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.transform(xmlInput, xmlOutput); + // 获取打印消息 + String message = xmlOutput.getWriter().toString().replaceFirst(">", ">\n"); + // 打印信息 + printLog(Log.DEBUG, tag, message); + } catch (Exception e) { + String errorInfo = "null"; + if (e != null) { + Throwable throwable = e.getCause(); + if (throwable != null) { + errorInfo = throwable.toString(); + } else { + try { + errorInfo = e.toString(); + } catch (Exception e1) { + errorInfo = e1.toString(); + } + } + } + printLog(Log.ERROR, tag, errorInfo + "\n" + xml); + } + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ADBUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ADBUtils.java new file mode 100644 index 0000000000..fb9fe004e8 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ADBUtils.java @@ -0,0 +1,2294 @@ +package dev.utils.app; + +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.PowerManager; +import android.support.annotation.IntRange; +import android.text.TextUtils; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.common.DevCommonUtils; + +/** + * detail: ADB shell 工具类 + * @author Ttt + *
+ *     Awesome ADB 一份超全超详细的 ADB 用法大全
+ *     @see 
+ *     

+ * 获取 APP 列表 ( 包名 ) + * @see
+ *

+ * adb shell input + * @see
+ *

+ * grep 是 linux 下的命令, windows 用 findstr + * 开启 Thread 执行, 非主线程, 否则无响应并无效 + *
+ */ +public final class ADBUtils { + + private ADBUtils() { + } + + // 日志 TAG + private static final String TAG = ADBUtils.class.getSimpleName(); + // 正则表达式: 空格 + private static final String REGEX_SPACE = "\\s"; + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + + /** + * 判断设备是否 root + * @return {@code true} yes, {@code false} no + */ + public static boolean isDeviceRooted() { + String su = "su"; + String[] locations = {"/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/", + "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/"}; + for (String location : locations) { + if (new File(location + su).exists()) { + return true; + } + } + return false; + } + + /** + * 请求 Root 权限 + */ + public static void requestRoot() { + ShellUtils.execCmd("exit", true); + } + + /** + * 判断 APP 是否授权 Root 权限 + * @return {@code true} yes, {@code false} no + */ + public static boolean isGrantedRoot() { + ShellUtils.CommandResult result = ShellUtils.execCmd("exit", true); + return result.isSuccess2(); + } + + // ============ + // = 应用管理 = + // ============ + + // ============ + // = 应用列表 = + // ============ + + /** + * 获取 APP 列表 ( 包名 ) + * @param type options + * @return 对应选项的应用包名列表 + */ + public static List getAppList(final String type) { + // adb shell pm list packages [options] + String typeStr = isSpace(type) ? "" : " " + type; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("pm list packages" + typeStr, false); + if (result.isSuccess3()) { + try { + String[] arrays = result.successMsg.split(NEW_LINE_STR); + return Arrays.asList(arrays); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppList type: " + typeStr); + } + } + return null; + } + + /** + * 获取 APP 安装列表 ( 包名 ) + * @return APP 安装列表 ( 包名 ) + */ + public static List getInstallAppList() { + return getAppList(null); + } + + /** + * 获取用户安装的应用列表 ( 包名 ) + * @return 用户安装的应用列表 ( 包名 ) + */ + public static List getUserAppList() { + return getAppList("-3"); + } + + /** + * 获取系统应用列表 ( 包名 ) + * @return 系统应用列表 ( 包名 ) + */ + public static List getSystemAppList() { + return getAppList("-s"); + } + + /** + * 获取启用的应用列表 ( 包名 ) + * @return 启用的应用列表 ( 包名 ) + */ + public static List getEnableAppList() { + return getAppList("-e"); + } + + /** + * 获取禁用的应用列表 ( 包名 ) + * @return 禁用的应用列表 ( 包名 ) + */ + public static List getDisableAppList() { + return getAppList("-d"); + } + + /** + * 获取包名包含字符串 xxx 的应用列表 + * @param filter 过滤获取字符串 + * @return 包名包含字符串 xxx 的应用列表 + */ + public static List getAppListToFilter(final String filter) { + if (isSpace(filter)) return null; + return getAppList("| grep " + filter.trim()); + } + + /** + * 判断是否安装应用 + * @param packageName 应用包名 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInstalledApp(final String packageName) { + if (isSpace(packageName)) return false; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("pm path " + packageName, false); + return result.isSuccess3(); + } + + /** + * 查看应用安装路径 + * @param packageName 应用包名 + * @return 应用安装路径 + */ + public static String getAppInstallPath(final String packageName) { + if (isSpace(packageName)) return null; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("pm path " + packageName, false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 清除应用数据与缓存 - 相当于在设置里的应用信息界面点击了「清除缓存」和「清除数据」 + * @param packageName 应用包名 + * @return {@code true} success, {@code false} fail + */ + public static boolean clearAppDataCache(final String packageName) { + if (isSpace(packageName)) return false; + // adb shell pm clear + String cmd = "pm clear %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, packageName), true); + return result.isSuccess4("success"); + } + + // ============ + // = 应用信息 = + // ============ + + /** + * 查看应用详细信息 + *
+     *     输出中包含很多信息, 包括 Activity Resolver Table、Registered ContentProviders、
+     *     包名、userId、安装后的文件资源代码等路径、版本信息、权限信息和授予状态、签名版本信息等
+     * 
+ * @param packageName 应用包名 + * @return 应用详细信息 + */ + public static String getAppMessage(final String packageName) { + if (isSpace(packageName)) return null; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("dumpsys package " + packageName, true); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取 APP versionCode + * @param packageName 应用包名 + * @return versionCode + */ + public static int getVersionCode(final String packageName) { + if (isSpace(packageName)) return 0; + try { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("dumpsys package " + packageName + " | grep version", true); + if (result.isSuccess3()) { + String[] arrays = result.successMsg.split(REGEX_SPACE); + for (String str : arrays) { + if (!TextUtils.isEmpty(str)) { + try { + String[] datas = str.split("="); + if (datas.length == 2) { + if (datas[0].toLowerCase().equals("versionCode".toLowerCase())) { + return Integer.parseInt(datas[1]); + } + } + } catch (Exception e) { + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getVersionCode"); + } + return 0; + } + + /** + * 获取 APP versionName + * @param packageName 应用包名 + * @return versionName + */ + public static String getVersionName(final String packageName) { + if (isSpace(packageName)) return null; + try { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("dumpsys package " + packageName + " | grep version", true); + if (result.isSuccess3()) { + String[] arrays = result.successMsg.split(REGEX_SPACE); + for (String str : arrays) { + if (!TextUtils.isEmpty(str)) { + try { + String[] datas = str.split("="); + if (datas.length == 2) { + if (datas[0].toLowerCase().equals("versionName".toLowerCase())) { + return datas[1]; + } + } + } catch (Exception e) { + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getVersionName"); + } + return null; + } + + // ============== + // = 安装、卸载 = + // ============== + + /** + * 安装应用 + * @param filePath /sdcard/xxx/x.apk + * @return {@code true} success, {@code false} fail + */ + public static boolean installApp(final String filePath) { + return installApp(filePath, "-rtsd"); + } + + /** + * 安装应用 + *
+     *     -l 将应用安装到保护目录 /mnt/asec
+     *     -r 允许覆盖安装
+     *     -t 允许安装 AndroidManifest.xml 里 application 指定 android:testOnly="true" 的应用
+     *     -s 将应用安装到 sdcard
+     *     -d 允许降级覆盖安装
+     *     -g 授予所有运行时权限
+     * 
+ * @param filePath /sdcard/xxx/x.apk + * @param params 安装选项 + * @return {@code true} success, {@code false} fail + */ + public static boolean installApp(final String filePath, final String params) { + if (isSpace(params)) return false; + boolean isRoot = isDeviceRooted(); + // adb install [-lrtsdg] + String cmd = "adb install %s %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, params, filePath), isRoot); + // 判断是否成功 + return result.isSuccess4("success"); + } + + /** + * 静默安装应用 + * @param filePath 文件路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean installAppSilent(final String filePath) { + return installAppSilent(getFileByPath(filePath), null); + } + + /** + * 静默安装应用 + * @param file 文件 + * @return {@code true} success, {@code false} fail + */ + public static boolean installAppSilent(final File file) { + return installAppSilent(file, null); + } + + /** + * 静默安装应用 + * @param filePath 文件路径 + * @param params 安装选项 + * @return {@code true} success, {@code false} fail + */ + public static boolean installAppSilent(final String filePath, final String params) { + return installAppSilent(getFileByPath(filePath), params, isDeviceRooted()); + } + + /** + * 静默安装应用 + * @param file 文件 + * @param params 安装选项 + * @return {@code true} success, {@code false} fail + */ + public static boolean installAppSilent(final File file, final String params) { + return installAppSilent(file, params, isDeviceRooted()); + } + + /** + * 静默安装应用 + * @param file 文件 + * @param params 安装选项 + * @param isRooted 是否 root + * @return {@code true} success, {@code false} fail + */ + public static boolean installAppSilent(final File file, final String params, final boolean isRooted) { + if (!isFileExists(file)) return false; + String filePath = '"' + file.getAbsolutePath() + '"'; + String command = "LD_LIBRARY_PATH=/vendor/lib*:/system/lib* pm install " + (params == null ? "" : params + " ") + filePath; + ShellUtils.CommandResult result = ShellUtils.execCmd(command, isRooted); + return result.isSuccess4("success"); + } + + // = + + /** + * 卸载应用 + * @param packageName 应用包名 + * @return {@code true} success, {@code false} fail + */ + public static boolean uninstallApp(final String packageName) { + return uninstallApp(packageName, false); + } + + /** + * 卸载应用 + * @param packageName 应用包名 + * @param isKeepData -k 参数可选, 表示卸载应用但保留数据和缓存目录 + * @return {@code true} success, {@code false} fail + */ + public static boolean uninstallApp(final String packageName, final boolean isKeepData) { + if (isSpace(packageName)) return false; + boolean isRoot = isDeviceRooted(); + // adb uninstall [-k] + String cmd = "adb uninstall "; + if (isKeepData) { + cmd += " -k "; + } + cmd += packageName; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, isRoot); + // 判断是否成功 + return result.isSuccess4("success"); + } + + /** + * 静默卸载应用 + * @param packageName 应用包名 + * @return {@code true} success, {@code false} fail + */ + public static boolean uninstallAppSilent(final String packageName) { + return uninstallAppSilent(packageName, false, isDeviceRooted()); + } + + /** + * 静默卸载应用 + * @param packageName 应用包名 + * @param isKeepData -k 参数可选, 表示卸载应用但保留数据和缓存目录 + * @return {@code true} success, {@code false} fail + */ + public static boolean uninstallAppSilent(final String packageName, final boolean isKeepData) { + return uninstallAppSilent(packageName, isKeepData, isDeviceRooted()); + } + + /** + * 静默卸载应用 + * @param packageName 应用包名 + * @param isKeepData -k 参数可选, 表示卸载应用但保留数据和缓存目录 + * @param isRooted 是否 root + * @return {@code true} success, {@code false} fail + */ + public static boolean uninstallAppSilent(final String packageName, final boolean isKeepData, final boolean isRooted) { + if (isSpace(packageName)) return false; + String command = "LD_LIBRARY_PATH=/vendor/lib*:/system/lib* pm uninstall " + (isKeepData ? "-k " : "") + packageName; + ShellUtils.CommandResult result = ShellUtils.execCmd(command, isRooted); + return result.isSuccess4("success"); + } + + // =========== + // = dumpsys = + // =========== + + /** + * 获取对应包名应用启动的 Activity + *
+     *     android.intent.category.LAUNCHER (android.intent.action.MAIN)
+     * 
+ * @param packageName 应用包名 + * @return package.xx.Activity.className + */ + public static String getActivityToLauncher(final String packageName) { + if (isSpace(packageName)) return null; + String cmd = "dumpsys package %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, packageName), true); + if (result.isSuccess3()) { + String mainStr = "android.intent.action.MAIN:"; + int start = result.successMsg.indexOf(mainStr); + // 防止都为 null + if (start != -1) { + try { + // 进行裁剪字符串 + String subData = result.successMsg.substring(start + mainStr.length()); + // 进行拆分 + String[] arrays = subData.split(NEW_LINE_STR); + for (String str : arrays) { + if (!TextUtils.isEmpty(str)) { + // 存在包名才处理 + if (str.indexOf(packageName) != -1) { + String[] splitArys = str.split(REGEX_SPACE); + for (String strData : splitArys) { + if (!TextUtils.isEmpty(strData)) { + // 属于 packageName/ 前缀的 + if (strData.indexOf(packageName + "/") != -1) { + // 防止属于 packageName/.xx.Main_Activity + if (strData.indexOf("/.") != -1) { + // packageName/.xx.Main_Activity + // packageName/packageName.xx.Main_Activity + strData = strData.replace("/", "/" + packageName); + } + return strData; + } + } + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivityToLauncher " + packageName); + } + } + } + return null; + } + + // =================== + // = 获取当前 Window = + // =================== + + /** + * 获取当前显示的 Window + *
+     *     adb shell dumpsys window -h
+     * 
+ * @return package.xx.Activity.className + */ + public static String getWindowCurrent() { + String cmd = "dumpsys window w | grep \\/ | grep name="; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, true); + if (result.isSuccess3()) { + try { + String nameStr = "name="; + String[] arrays = result.successMsg.split(NEW_LINE_STR); + for (String str : arrays) { + if (!TextUtils.isEmpty(str)) { + int start = str.indexOf(nameStr); + if (start != -1) { + try { + String subData = str.substring(start + nameStr.length()); + if (subData.indexOf(")") != -1) { + return subData.substring(0, subData.length() - 1); + } + return subData; + } catch (Exception e) { + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWindowCurrent"); + } + } + return null; + } + + /** + * 获取当前显示的 Window + * @return package/package.xx.Activity.className + */ + public static String getWindowCurrent2() { + String cmd = "dumpsys window windows | grep Current"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, true); + if (result.isSuccess3()) { + try { + // 拆分换行, 并循环 + String[] arrays = result.successMsg.split(NEW_LINE_STR); + for (String str : arrays) { + if (!TextUtils.isEmpty(str)) { + String[] splitArys = str.split(REGEX_SPACE); + if (splitArys != null && splitArys.length != 0) { + for (String splitStr : splitArys) { + if (!TextUtils.isEmpty(splitStr)) { + int start = splitStr.indexOf("/"); + int lastIndex = splitStr.lastIndexOf("}"); + if (start != -1 && lastIndex != -1) { + // 获取裁剪数据 + String strData = splitStr.substring(0, lastIndex); + // 防止属于 packageName/.xx.Main_Activity + if (strData.indexOf("/.") != -1) { + // packageName/.xx.Main_Activity + // packageName/packageName.xx.Main_Activity + strData = strData.replace("/", "/" + splitStr.substring(0, start)); + } + return strData; + } + } + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWindowCurrent2"); + } + } + return null; + } + + /** + * 获取对应包名显示的 Window + * @param packageName 应用包名 + * @return package/package.xx.Activity.className + */ + public static String getWindowCurrentToPackage(final String packageName) { + if (isSpace(packageName)) return null; + String cmd = "dumpsys window windows | grep %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, packageName), true); + if (result.isSuccess3()) { + try { + // 拆分换行, 并循环 + String[] arrays = result.successMsg.split(NEW_LINE_STR); + for (String str : arrays) { + if (!TextUtils.isEmpty(str)) { + String[] splitArys = str.split(REGEX_SPACE); + if (splitArys != null && splitArys.length != 0) { + for (String splitStr : splitArys) { + if (!TextUtils.isEmpty(splitStr)) { + int start = splitStr.indexOf("/"); + int lastIndex = splitStr.lastIndexOf("}"); + if (start != -1 && lastIndex != -1 && splitStr.indexOf(packageName) == 0) { + // 获取裁剪数据 + String strData = splitStr.substring(0, lastIndex); + // 防止属于 packageName/.xx.Main_Activity + if (strData.indexOf("/.") != -1) { + // packageName/.xx.Main_Activity + // packageName/packageName.xx.Main_Activity + strData = strData.replace("/", "/" + packageName); + } + return strData; + } + } + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWindowCurrentToPackage"); + } + } + return null; + } + + // ===================== + // = 获取当前 Activity = + // ===================== + + /** + * 获取当前显示的 Activity + * @return package.xx.Activity.className + */ + public static String getActivityCurrent() { + String cmd = "dumpsys activity activities | grep mFocusedActivity"; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + cmd = "dumpsys activity activities | grep mResumedActivity"; + } + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, true); + if (result.isSuccess3()) { + try { + // 拆分换行, 并循环 + String[] arrays = result.successMsg.split(NEW_LINE_STR); + for (String str : arrays) { + if (!TextUtils.isEmpty(str)) { + String[] splitArys = str.split(REGEX_SPACE); + if (splitArys != null && splitArys.length != 0) { + for (String splitStr : splitArys) { + if (!TextUtils.isEmpty(splitStr)) { + int start = splitStr.indexOf("/"); + if (start != -1) { + // 获取裁剪数据 + String strData = splitStr; + // 防止属于 packageName/.xx.Main_Activity + if (strData.indexOf("/.") != -1) { + // packageName/.xx.Main_Activity + // packageName/packageName.xx.Main_Activity + strData = strData.replace("/", "/" + splitStr.substring(0, start)); + } + return strData; + } + } + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivityCurrent"); + } + } + return null; + } + + /** + * 获取 Activity 栈 + * @return 当前全部 Activity 栈信息 + */ + public static String getActivitys() { + return getActivitys(null); + } + + /** + * 获取 Activity 栈 + * @param append 追加筛选条件 + * @return 当前全部 Activity 栈信息 + */ + public static String getActivitys(final String append) { + String cmd = "dumpsys activity activities"; + if (!isSpace(append)) { + cmd += " " + append.trim(); + } + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, true); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取对应包名的 Activity 栈 + * @param packageName 应用包名 + * @return 对应包名的 Activity 栈信息 + */ + public static String getActivitysToPackage(final String packageName) { + if (isSpace(packageName)) { + return null; + } + return getActivitys("| grep " + packageName); + } + + /** + * 获取对应包名的 Activity 栈 ( 处理成 List) - 最新的 Activity 越靠后 + * @param packageName 应用包名 + * @return 对应包名的 Activity 栈信息集合 + */ + public static List getActivitysToPackageLists(final String packageName) { + // 获取对应包名的 Activity 数据结果 + String result = getActivitysToPackage(packageName); + // 防止数据为 null + if (!TextUtils.isEmpty(result)) { + try { + List lists = new ArrayList<>(); + String[] dataSplit = result.split(NEW_LINE_STR); + // 拆分后, 数据长度 + int splitLength = dataSplit.length; + // 获取 Activity 栈字符串 + String activities = null; + // 判断最后一行是否符合条件 + if (dataSplit[splitLength - 1].indexOf("Activities=") != -1) { + activities = dataSplit[splitLength - 1]; + } else { + for (String str : dataSplit) { + if (str.indexOf("Activities=") != -1) { + activities = str; + break; + } + } + } + // 进行特殊处理 Activities=[ActivityRecord{xx},ActivityRecord{xx}]; + int startIndex = activities.indexOf("Activities=["); + activities = activities.substring(startIndex + "Activities=[".length(), activities.length() - 1); + // 再次进行拆分 + String[] activityArys = activities.split("ActivityRecord"); + for (String data : activityArys) { + try { + String[] splitArys = data.split(REGEX_SPACE); + if (splitArys != null && splitArys.length != 0) { + for (String splitStr : splitArys) { + int start = splitStr.indexOf(packageName + "/"); + if (start != -1) { + // 获取裁剪数据 + String strData = splitStr; + // 防止属于 packageName/.xx.XxxActivity + if (strData.indexOf("/.") != -1) { + // packageName/.xx.XxxActivity + // packageName/packageName.xx.XxxActivity + strData = strData.replace("/", "/" + splitStr.substring(0, start)); + } + // 保存数据 + lists.add(strData); + } + } + } + } catch (Exception e) { + } + } + return lists; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivitysToPackageLists"); + } + } + return null; + } + + // = + + /** + * 判断 Activity 栈顶是否重复 + * @param packageName 应用包名 + * @param activity Activity Name + * @return {@code true} yes, {@code false} no + */ + public static boolean isActivityTopRepeat(final String packageName, final String activity) { + if (TextUtils.isEmpty(packageName)) { + return false; + } else if (TextUtils.isEmpty(activity)) { + return false; + } + // 判断是否重复 + boolean isRepeat = false; + // 获取 + List lists = ADBUtils.getActivitysToPackageLists(packageName); + // 数据长度 + int length = DevCommonUtils.length(lists); + // 防止数据为 null + if (length >= 2) { // 两个页面以上, 才能够判断是否重复 + try { + if (lists.get(length - 1).endsWith(activity)) { + // 倒序遍历, 越后面是 Activity 栈顶 + for (int i = length - 2; i >= 0; i--) { + String data = lists.get(i); + // 判断是否该页面结尾 + return data.endsWith(activity); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isActivityTopRepeat"); + } + } + return isRepeat; + } + + /** + * 判断 Activity 栈顶是否重复 + * @param packageName 应用包名 + * @param activitys Activity Name 集合 + * @return {@code true} yes, {@code false} no + */ + public static boolean isActivityTopRepeat(final String packageName, final List activitys) { + if (TextUtils.isEmpty(packageName)) { + return false; + } else if (activitys == null || activitys.size() == 0) { + return false; + } + // 判断是否重复 + boolean isRepeat = false; + // 获取 + List lists = ADBUtils.getActivitysToPackageLists(packageName); + // 数据长度 + int length = DevCommonUtils.length(lists); + // 防止数据为 null + if (length >= 2) { // 两个页面以上, 才能够判断是否重复 + // 循环判断 + for (String activity : activitys) { + try { + if (lists.get(length - 1).endsWith(activity)) { + // 倒序遍历, 越后面是 Activity 栈顶 + for (int i = length - 2; i >= 0; i--) { + String data = lists.get(i); + // 判断是否该页面结尾 + return data.endsWith(activity); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isActivityTopRepeat"); + } + } + } + return isRepeat; + } + + // = + + /** + * 获取 Activity 栈顶重复总数 + * @param packageName 应用包名 + * @param activity Activity Name + * @return 指定 Activity 在栈顶重复总数 + */ + public static int getActivityTopRepeatCount(final String packageName, final String activity) { + if (TextUtils.isEmpty(packageName)) { + return 0; + } else if (TextUtils.isEmpty(activity)) { + return 0; + } + // 重复数量 + int number = 0; + // 获取 + List lists = ADBUtils.getActivitysToPackageLists(packageName); + // 数据长度 + int length = DevCommonUtils.length(lists); + // 防止数据为 null + if (length >= 2) { // 两个页面以上, 才能够判断是否重复 + try { + if (lists.get(length - 1).endsWith(activity)) { + // 倒序遍历, 越后面是 Activity 栈顶 + for (int i = length - 2; i >= 0; i--) { + String data = lists.get(i); + // 判断是否该页面结尾 + if (data.endsWith(activity)) { + number++; + } else { + break; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivityTopRepeatCount"); + } + } + return number; + } + + /** + * 获取 Activity 栈顶重复总数 + * @param packageName 应用包名 + * @param activitys Activity Name 集合 + * @return 指定 Activity 在栈顶重复总数 + */ + public static int getActivityTopRepeatCount(final String packageName, final List activitys) { + if (TextUtils.isEmpty(packageName)) { + return 0; + } else if (activitys == null || activitys.size() == 0) { + return 0; + } + // 获取 + List lists = ADBUtils.getActivitysToPackageLists(packageName); + // 数据长度 + int length = DevCommonUtils.length(lists); + // 防止数据为 null + if (length >= 2) { // 两个页面以上, 才能够判断是否重复 + // 循环判断 + for (String activity : activitys) { + try { + // 重复数量 + int number = 0; + // 判断是否对应页面结尾 + if (lists.get(length - 1).endsWith(activity)) { + // 倒序遍历, 越后面是 Activity 栈顶 + for (int i = length - 2; i >= 0; i--) { + String data = lists.get(i); + // 判断是否该页面结尾 + if (data.endsWith(activity)) { + number++; + } else { + break; + } + } + // 进行判断处理 + return number; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivityTopRepeatCount"); + } + } + } + return 0; + } + + // ======================= + // = 正在运行的 Services = + // ======================= + + /** + * 查看正在运行的 Services + * @return 运行中的 Services 信息 + */ + public static String getServices() { + return getServices(null); + } + + /** + * 查看正在运行的 Services + * @param packageName 应用包名, 参数不是必须的, 指定 表示查看与某个包名相关的 Services, + * 不指定表示查看所有 Services, 不一定要给出完整的包名, + * 比如运行 adb shell dumpsys activity services org.mazhuang + * 那么包名 org.mazhuang.demo1、org.mazhuang.demo2 和 org.mazhuang123 等相关的 Services 都会列出来 + * @return 运行中的 Services 信息 + */ + public static String getServices(final String packageName) { + String cmd = "dumpsys activity services" + ((isSpace(packageName) ? "" : " " + packageName)); + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, true); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + // ====== + // = am = + // ====== + + /** + * 启动自身应用 + * @return {@code true} success, {@code false} fail + */ + public static boolean startSelfApp() { + return startSelfApp(false); + } + + /** + * 启动自身应用 + * @param closeActivity 是否关闭 Activity 所属的 APP 进程后再启动 Activity + * @return {@code true} success, {@code false} fail + */ + public static boolean startSelfApp(final boolean closeActivity) { + try { + // 获取包名 + String packageName = DevUtils.getContext().getPackageName(); + // 获取 Launcher Activity + String activity = ActivityUtils.getLauncherActivity(); + // 跳转应用启动页 ( 启动应用 ) + startActivity(packageName + "/" + activity, closeActivity); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startSelfApp"); + } + return false; + } + + /** + * 跳转页面 Activity + * @param packageAndLauncher package/package.xx.Activity.className + * @param closeActivity 是否关闭 Activity 所属的 APP 进程后再启动 Activity + * @return {@code true} success, {@code false} fail + */ + public static boolean startActivity(final String packageAndLauncher, final boolean closeActivity) { + return startActivity(packageAndLauncher, null, closeActivity); + } + + /** + * 跳转页面 Activity + * @param packageAndLauncher package/package.xx.Activity.className + * @param append 追加的信息, 例如传递参数等 + * @param closeActivity 是否关闭 Activity 所属的 APP 进程后再启动 Activity + * @return {@code true} success, {@code false} fail + */ + public static boolean startActivity(final String packageAndLauncher, final String append, final boolean closeActivity) { + if (isSpace(packageAndLauncher)) return false; + try { + // am start [options] + String cmd = "am start %s"; + if (closeActivity) { + cmd = String.format(cmd, "-S " + packageAndLauncher); + } else { + cmd = String.format(cmd, packageAndLauncher); + } + // 判断是否追加 + if (!isSpace(append)) { + cmd += " " + append.trim(); + } + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startActivity"); + } + return false; + } + + /** + * 启动服务 + * @param packageAndService package/package.xx.Service.className + * @return {@code true} success, {@code false} fail + */ + public static boolean startService(final String packageAndService) { + return startService(packageAndService, null); + } + + /** + * 启动服务 + * @param packageAndService package/package.xx.Service.className + * @param append 追加的信息, 例如传递参数等 + * @return {@code true} success, {@code false} fail + */ + public static boolean startService(final String packageAndService, final String append) { + if (isSpace(packageAndService)) return false; + try { + // am startservice [options] + String cmd = "am startservice %s"; + // 进行格式化 + cmd = String.format(cmd, packageAndService); + // 判断是否追加 + if (!isSpace(append)) { + cmd += " " + append.trim(); + } + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startService"); + } + return false; + } + + /** + * 停止服务 + * @param packageAndService package/package.xx.Service.className + * @return {@code true} success, {@code false} fail + */ + public static boolean stopService(final String packageAndService) { + return stopService(packageAndService, null); + } + + /** + * 停止服务 + * @param packageAndService package/package.xx.Service.className + * @param append 追加的信息, 例如传递参数等 + * @return {@code true} success, {@code false} fail + */ + public static boolean stopService(final String packageAndService, final String append) { + if (isSpace(packageAndService)) return false; + try { + // am stopservice [options] + String cmd = "am stopservice %s"; + // 进行格式化 + cmd = String.format(cmd, packageAndService); + // 判断是否追加 + if (!isSpace(append)) { + cmd += " " + append.trim(); + } + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, true); + return result.isSuccess3(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopService"); + } + return false; + } + + /** + * 发送广播 ( 向所有组件发送 ) + *
+     *     向所有组件广播 BOOT_COMPLETED
+     *     adb shell am broadcast -a android.intent.action.BOOT_COMPLETED
+     * 
+ * @param broadcast 广播 INTENT + * @return {@code true} success, {@code false} fail + */ + public static boolean sendBroadcastToAll(final String broadcast) { + if (isSpace(broadcast)) return false; + try { + // am broadcast [options] + String cmd = "am broadcast -a %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, broadcast), true); + return result.isSuccess3(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "sendBroadcastAll"); + } + return false; + } + + /** + * 发送广播 + *
+     *     只向 org.mazhuang.boottimemeasure/.BootCompletedReceiver 广播 BOOT_COMPLETED
+     *     adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n org.mazhuang.boottimemeasure/.BootCompletedReceiver
+     * 
+ * @param packageAndBroadcast package/package.xx.Receiver.className + * @param broadcast 广播 INTENT + * @return {@code true} success, {@code false} fail + */ + public static boolean sendBroadcast(final String packageAndBroadcast, final String broadcast) { + if (isSpace(packageAndBroadcast)) return false; + if (isSpace(broadcast)) return false; + try { + // am broadcast [options] + String cmd = "am broadcast -a %s -n %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, broadcast, packageAndBroadcast), true); + return result.isSuccess3(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "sendBroadcast"); + } + return false; + } + + // = + + /** + * 销毁进程 + * @param packageName 应用包名 + * @return {@code true} success, {@code false} fail + */ + public static boolean kill(final String packageName) { + if (isSpace(packageName)) return false; + try { + String cmd = "am force-stop %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, packageName), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "kill"); + } + return false; + } + + /** + * 收紧内存 + * @param pid 进程 ID + * @param level HIDDEN、RUNNING_MODERATE、BACKGROUND、RUNNING_LOW、MODERATE、RUNNING_CRITICAL、COMPLETE + * @return {@code true} success, {@code false} fail + */ + public static boolean sendTrimMemory(final int pid, final String level) { + if (isSpace(level)) return false; + try { + String cmd = "am send-trim-memory %s %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, pid, level), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "sendTrimMemory"); + } + return false; + } + +// // ============ +// // = 文件管理 = +// // ============ +// +// /** +// * 复制设备里的文件到电脑 +// * @param remote 设备里的文件路径 +// * @param local 电脑上的目录 +// * @return {@code true} success, {@code false} fail +// */ +// public static boolean pull(final String remote, final String local) { +// if (isSpace(remote)) return false; +// try { +// // adb pull <设备里的文件路径> [电脑上的目录] +// String cmd = "adb pull %s"; +// // 判断是否存到默认地址 +// if (!isSpace(local)) { +// cmd += " " + local; +// } +// // 执行 shell +// ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, remote), true); +// return result.isSuccess2(); +// } catch (Exception e) { +// LogPrintUtils.eTag(TAG, e, "pull"); +// } +// return false; +// } +// +// /** +// * 复制电脑里的文件到设备 +// * @param local 电脑上的文件路径 +// * @param remote 设备里的目录 +// * @return {@code true} success, {@code false} fail +// */ +// public static boolean push(final String local, final String remote) { +// if (isSpace(local)) return false; +// if (isSpace(remote)) return false; +// try { +// // adb push <电脑上的文件路径> <设备里的目录> +// String cmd = "adb push %s %s"; +// // 执行 shell +// ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, local, remote), true); +// return result.isSuccess2(); +// } catch (Exception e) { +// LogPrintUtils.eTag(TAG, e, "push"); +// } +// return false; +// } + + // ========= + // = Input = + // ========= + + // =============================== + // = tap - 模拟 touch 屏幕的事件 = + // =============================== + + /** + * 点击某个区域 + * @param x X 轴坐标 + * @param y Y 轴坐标 + * @return {@code true} success, {@code false} fail + */ + public static boolean tap(final float x, final float y) { + try { + // input [touchscreen|touchpad|touchnavigation] tap + // input [ 屏幕、触摸板、导航键 ] tap + String cmd = "input touchscreen tap %s %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, (int) x, (int) y), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "tap"); + } + return false; + } + + // ==================== + // = swipe - 滑动事件 = + // ==================== + + /** + * 按压某个区域 ( 点击 ) + * @param x X 轴坐标 + * @param y Y 轴坐标 + * @return {@code true} success, {@code false} fail + */ + public static boolean swipeClick(final float x, final float y) { + return swipe(x, y, x, y, 100L); + } + + /** + * 按压某个区域 time 大于一定时间变成长按 + * @param x X 轴坐标 + * @param y Y 轴坐标 + * @param time 按压时间 + * @return {@code true} success, {@code false} fail + */ + public static boolean swipeClick(final float x, final float y, final long time) { + return swipe(x, y, x, y, time); + } + + /** + * 滑动到某个区域 + * @param x X 轴坐标 + * @param y Y 轴坐标 + * @param toX 滑动到 X 轴坐标 + * @param toY 滑动到 Y 轴坐标 + * @param time 滑动时间 ( 毫秒 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean swipe(final float x, final float y, final float toX, final float toY, final long time) { + try { + // input [touchscreen|touchpad|touchnavigation] swipe [duration(ms)] + String cmd = "input touchscreen swipe %s %s %s %s %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, (int) x, (int) y, (int) toX, (int) toY, time), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "swipe"); + } + return false; + } + + // =================== + // = text - 模拟输入 = + // =================== + + /** + * 输入文本 - 不支持中文 + * @param txt 文本内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean text(final String txt) { + if (isSpace(txt)) return false; + try { + // input text + String cmd = "input text %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, txt), true); // false 可以执行 + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "text"); + } + return false; + } + + // ======================= + // = keyevent - 按键操作 = + // ======================= + + /** + * 触发某些按键 + * @param keyCode KeyEvent.xxx 如: KeyEvent.KEYCODE_BACK ( 返回键 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean keyevent(final int keyCode) { + try { + // input keyevent + String cmd = "input keyevent %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, keyCode), true); // false 可以执行 + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "keyevent"); + } + return false; + } + + // ============ + // = 实用功能 = + // ============ + + /** + * 屏幕截图 + * @param path 保存路径 /sdcard/xxx/x.png + * @return {@code true} success, {@code false} fail + */ + public static boolean screencap(final String path) { + return screencap(path, 0); + } + + /** + * 屏幕截图 + * @param path 保存路径 /sdcard/xxx/x.png + * @param displayId -d display-id 指定截图的显示屏编号 ( 有多显示屏的情况下 ) 默认 0 + * @return {@code true} success, {@code false} fail + */ + public static boolean screencap(final String path, final int displayId) { + if (isSpace(path)) return false; + try { + String cmd = "screencap -p -d %s %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, Math.max(displayId, 0), path), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "screencap"); + } + return false; + } + + /** + * 录制屏幕 ( 以 mp4 格式保存到 /sdcard) + * @param path 保存路径 /sdcard/xxx/x.mp4 + * @return {@code true} success, {@code false} fail + */ + public static boolean screenrecord(final String path) { + return screenrecord(path, null, -1, -1); + } + + /** + * 录制屏幕 ( 以 mp4 格式保存到 /sdcard) + * @param path 保存路径 /sdcard/xxx/x.mp4 + * @param time 录制时长, 单位秒 ( 默认 / 最长 180 秒 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean screenrecord(final String path, final int time) { + return screenrecord(path, null, -1, time); + } + + /** + * 录制屏幕 ( 以 mp4 格式保存到 /sdcard) + * @param path 保存路径 /sdcard/xxx/x.mp4 + * @param size 视频的尺寸, 比如 1280x720, 默认是屏幕分辨率 + * @param time 录制时长, 单位秒 ( 默认 / 最长 180 秒 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean screenrecord(final String path, final String size, final int time) { + return screenrecord(path, size, -1, time); + } + + /** + * 录制屏幕 ( 以 mp4 格式保存到 /sdcard) + * @param path 保存路径 /sdcard/xxx/x.mp4 + * @param size 视频的尺寸, 比如 1280x720, 默认是屏幕分辨率 + * @param bitRate 视频的比特率, 默认是 4Mbps + * @param time 录制时长, 单位秒 ( 默认 / 最长 180 秒 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean screenrecord(final String path, final String size, final int bitRate, final int time) { + if (isSpace(path)) return false; + try { + StringBuilder builder = new StringBuilder(); + builder.append("screenrecord"); + if (!isSpace(size)) { + builder.append(" --size " + size); + } + if (bitRate > 0) { + builder.append(" --bit-rate " + bitRate); + } + if (time > 0) { + builder.append(" --time-limit " + time); + } + builder.append(" " + path); + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(builder.toString(), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "screenrecord"); + } + return false; + } + + /** + * 查看连接过的 Wifi 密码 + * @return 连接过的 Wifi 密码 + */ + public static String wifiConf() { + try { + String cmd = "cat /data/misc/wifi/*.conf"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, true); + if (result.isSuccess3()) { + return result.successMsg; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "wifiConf"); + } + return null; + } + + /** + * 开启 / 关闭 Wifi + * @param open 是否开启 + * @return {@code true} success, {@code false} fail + */ + public static boolean wifiSwitch(final boolean open) { + String cmd = "svc wifi %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, open ? "enable" : "disable"), true); + return result.isSuccess2(); + } + + /** + * 设置系统时间 + * @param time yyyyMMdd.HHmmss 20160823.131500 + * 表示将系统日期和时间更改为 2016 年 08 月 23 日 13 点 15 分 00 秒 + * @return {@code true} success, {@code false} fail + */ + public static boolean setSystemTime(final String time) { + if (isSpace(time)) return false; + try { + String cmd = "date -s %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, time), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setSystemTime"); + } + return false; + } + + /** + * 设置系统时间 + * @param time MMddHHmmyyyy.ss 082313152016.00 + * 表示将系统日期和时间更改为 2016 年 08 月 23 日 13 点 15 分 00 秒 + * @return {@code true} success, {@code false} fail + */ + public static boolean setSystemTime2(final String time) { + if (isSpace(time)) return false; + try { + String cmd = "date %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, time), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setSystemTime2"); + } + return false; + } + + /** + * 设置系统时间 + * @param time 时间戳转换 MMddHHmmyyyy.ss + * @return {@code true} success, {@code false} fail + */ + public static boolean setSystemTime2(final long time) { + if (time < 0) return false; + try { + String cmd = "date %s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, new SimpleDateFormat("MMddHHmmyyyy.ss").format(time)), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setSystemTime2"); + } + return false; + } + + // ================ + // = 刷机相关命令 = + // ================ + + /** + * 关机 ( 需要 root 权限 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean shutdown() { + try { + ShellUtils.execCmd("reboot -p", true); + Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN"); + intent.putExtra("android.intent.extra.KEY_CONFIRM", false); + DevUtils.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "shutdown"); + } + return false; + } + + /** + * 重启设备 ( 需要 root 权限 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean reboot() { + try { + ShellUtils.execCmd("reboot", true); + Intent intent = new Intent(Intent.ACTION_REBOOT); + intent.putExtra("nowait", 1); + intent.putExtra("interval", 1); + intent.putExtra("window", 0); + DevUtils.getContext().sendBroadcast(intent); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "reboot"); + } + return false; + } + + /** + * 重启设备 ( 需要 root 权限 ) - 并进行特殊的引导模式 (recovery、Fastboot) + * @param reason 传递给内核来请求特殊的引导模式, 如 "recovery" + * 重启到 Fastboot 模式 bootloader + */ + public static void reboot(final String reason) { + if (isSpace(reason)) return; + try { + PowerManager mPowerManager = (PowerManager) DevUtils.getContext().getSystemService(Context.POWER_SERVICE); + if (mPowerManager == null) + return; + mPowerManager.reboot(reason); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "reboot"); + } + } + + /** + * 重启引导到 recovery ( 需要 root 权限 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean rebootToRecovery() { + ShellUtils.CommandResult result = ShellUtils.execCmd("reboot recovery", true); + return result.isSuccess2(); + } + + /** + * 重启引导到 bootloader ( 需要 root 权限 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean rebootToBootloader() { + ShellUtils.CommandResult result = ShellUtils.execCmd("reboot bootloader", true); + return result.isSuccess2(); + } + + // ============ + // = 滑动方法 = + // ============ + + /** + * 发送事件滑动 + * @param x X 轴坐标 + * @param y Y 轴坐标 + * @param toX 滑动到 X 轴坐标 + * @param toY 滑动到 Y 轴坐标 + * @param number 循环次数 + */ + public static void sendEventSlide(final float x, final float y, final float toX, final float toY, final int number) { + List lists = new ArrayList<>(); + // = 开头 = + lists.add("sendevent /dev/input/event1 3 57 109"); + lists.add("sendevent /dev/input/event1 3 53 " + x); + lists.add("sendevent /dev/input/event1 3 54 " + y); + // 发送 touch 事件 ( 必须使用 0 0 0 配对 ) + lists.add("sendevent /dev/input/event1 1 330 1"); + lists.add("sendevent /dev/input/event1 0 0 0"); + + // 判断方向 ( 手势是否从左到右 ) - View 往左滑, 手势操作往右滑 + boolean isLeftToRight = toX > x; + // 判断方向 ( 手势是否从上到下 ) - View 往上滑, 手势操作往下滑 + boolean isTopToBottom = toY > y; + + // 计算差数 + float diffX = isLeftToRight ? (toX - x) : (x - toX); + float diffY = isTopToBottom ? (toY - y) : (y - toY); + + if (!isLeftToRight) { + diffX = -diffX; + } + + if (!isTopToBottom) { + diffY = -diffY; + } + + // 平均值 + float averageX = diffX / number; + float averageY = diffY / number; + // 上次位置 + int oldX = (int) x; + int oldY = (int) y; + + // 循环处理 + for (int i = 0; i <= number; i++) { + if (averageX != 0f) { + // 进行判断处理 + int calcX = (int) (x + averageX * i); + if (oldX != calcX) { + oldX = calcX; + lists.add("sendevent /dev/input/event1 3 53 " + calcX); + } + } + + if (averageY != 0f) { + // 进行判断处理 + int calcY = (int) (y + averageY * i); + if (oldY != calcY) { + oldY = calcY; + lists.add("sendevent /dev/input/event1 3 54 " + calcY); + } + } + // 每次操作结束发送 + lists.add("sendevent /dev/input/event1 0 0 0"); + } + // = 结尾 = + lists.add("sendevent /dev/input/event1 3 57 4294967295"); + // 释放 touch 事件 ( 必须使用 0 0 0 配对 ) + lists.add("sendevent /dev/input/event1 1 330 0"); + lists.add("sendevent /dev/input/event1 0 0 0"); + + // 执行 shell + ShellUtils.execCmd(lists, true); + } + + // ================ + // = 查看设备信息 = + // ================ + + /** + * 获取 SDK 版本 + * @return SDK 版本 + */ + public static String getSDKVersion() { + ShellUtils.CommandResult result = ShellUtils.execCmd("getprop ro.build.version.sdk", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取 Android 系统版本 + * @return Android 系统版本 + */ + public static String getAndroidVersion() { + ShellUtils.CommandResult result = ShellUtils.execCmd("getprop ro.build.version.release", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取设备型号 - 如 RedmiNote4X + * @return 设备型号 + */ + public static String getModel() { + // android.os.Build 内部有信息 android.os.Build.MODEL + ShellUtils.CommandResult result = ShellUtils.execCmd("getprop ro.product.model", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取设备品牌 + * @return 设备品牌 + */ + public static String getBrand() { + ShellUtils.CommandResult result = ShellUtils.execCmd("getprop ro.product.brand", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取设备名 + * @return 设备名 + */ + public static String getDeviceName() { + ShellUtils.CommandResult result = ShellUtils.execCmd("getprop ro.product.name", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取 CPU 支持的 abi 列表 + * @return CPU 支持的 abi 列表 + */ + public static String getCpuAbiList() { + ShellUtils.CommandResult result = ShellUtils.execCmd("cat /system/build.prop | grep ro.product.cpu.abi", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取每个应用程序的内存上限 + * @return 每个应用程序的内存上限 + */ + public static String getAppHeapsize() { + ShellUtils.CommandResult result = ShellUtils.execCmd("getprop dalvik.vm.heapsize", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取电池状况 + * @return 电池状况 + */ + public static String getBattery() { + ShellUtils.CommandResult result = ShellUtils.execCmd("dumpsys battery", true); + if (result.isSuccess3()) { // scale 代表最大电量, level 代表当前电量 + return result.successMsg; + } + return null; + } + + /** + * 获取屏幕密度 + * @return 屏幕密度 + */ + public static String getDensity() { + ShellUtils.CommandResult result = ShellUtils.execCmd("getprop ro.sf.lcd_density", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取屏幕分辨率 + * @return 屏幕分辨率 + */ + public static String getScreenSize() { + ShellUtils.CommandResult result = ShellUtils.execCmd("wm size", true); + if (result.isSuccess3()) { + // 正常返回 Physical size: 1080 x 1920 + // 如果使用命令修改过, 那输出可能是 + // Physical size: 1080 x 1920 + // Override size: 480 x 1024 + // 表明设备的屏幕分辨率原本是 1080px * 1920px, 当前被修改为 480px * 1024px + return result.successMsg; + } + return null; + } + + /** + * 获取显示屏参数 + * @return 显示屏参数 + */ + public static String getDisplays() { + ShellUtils.CommandResult result = ShellUtils.execCmd("dumpsys window displays", true); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取 Android id + * @return Android id + */ + public static String getAndroidId() { + ShellUtils.CommandResult result = ShellUtils.execCmd("settings get secure android_id", true); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取 IMEI 码 + * @return IMEI 码 + */ + public static String getIMEI() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ShellUtils.CommandResult result = ShellUtils.execCmd("service call iphonesubinfo 1", true); + if (result.isSuccess3()) { + try { + int index = 0; + StringBuilder builder = new StringBuilder(); + String subStr = result.successMsg.replaceAll("\\.", ""); + subStr = subStr.substring(subStr.indexOf("'") + 1, subStr.indexOf("')")); + // 添加数据 + builder.append(subStr.substring(0, subStr.indexOf("'"))); + // 从指定索引开始 + index = subStr.indexOf("'", builder.toString().length() + 1); + // 再次裁剪 + subStr = subStr.substring(index + 1); + // 添加数据 + builder.append(subStr.substring(0, subStr.indexOf("'"))); + // 从指定索引开始 + index = subStr.indexOf("'", builder.toString().length() + 1); + // 再次裁剪 + subStr = subStr.substring(index + 1); + // 最后进行添加 + builder.append(subStr.split(REGEX_SPACE)[0]); + // 返回对应的数据 + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIMEI"); + } + } + } else { + // 在 Android 4.4 及以下版本可通过如下命令获取 IMEI + ShellUtils.CommandResult result = ShellUtils.execCmd("dumpsys iphonesubinfo", true); + if (result.isSuccess3()) { // 返回值中的 Device ID 就是 IMEI + try { + String[] splitArys = result.successMsg.split(NEW_LINE_STR); + for (String str : splitArys) { + if (!TextUtils.isEmpty(str)) { + if (str.toLowerCase().indexOf("device") != -1) { + // 进行拆分 + String[] arrays = str.split(REGEX_SPACE); + return arrays[arrays.length - 1]; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIMEI"); + } + } + } + return null; + } + + /** + * 获取 IP 地址 + * @return IP 地址 + */ + public static String getIPAddress() { + boolean isRoot = false; + ShellUtils.CommandResult result = ShellUtils.execCmd("ifconfig | grep Mask", isRoot); + if (result.isSuccess3()) { + return result.successMsg; + } else { // 如果设备连着 Wifi, 可以使用如下命令来查看局域网 IP + result = ShellUtils.execCmd("ifconfig wlan0", isRoot); + if (result.isSuccess3()) { + return result.successMsg; + } else { + // 可以看到网络连接名称、启用状态、IP 地址和 Mac 地址等信息 + result = ShellUtils.execCmd("netcfg", isRoot); + if (result.isSuccess3()) { + return result.successMsg; + } + } + } + return null; + } + + /** + * 获取 Mac 地址 + * @return Mac 地址 + */ + public static String getMac() { + ShellUtils.CommandResult result = ShellUtils.execCmd("cat /sys/class/net/wlan0/address", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取 CPU 信息 + * @return CPU 信息 + */ + public static String getCPU() { + ShellUtils.CommandResult result = ShellUtils.execCmd("cat /proc/cpuinfo", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取内存信息 + * @return 内存信息 + */ + public static String getMeminfo() { + ShellUtils.CommandResult result = ShellUtils.execCmd("cat /proc/meminfo", false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + // ============ + // = 修改设置 = + // ============ + + /** + * 设置屏幕大小 + * @param width 屏幕宽度 + * @param height 屏幕高度 + * @return {@code true} success, {@code false} fail + */ + public static boolean setScreenSize(final int width, final int height) { + String cmd = "wm size %sx%s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, width, height), true); + return result.isSuccess2(); + } + + /** + * 恢复原分辨率命令 + * @return {@code true} success, {@code false} fail + */ + public static boolean resetScreen() { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("wm size reset", true); + return result.isSuccess2(); + } + + /** + * 设置屏幕密度 + * @param density 屏幕密度 + * @return {@code true} success, {@code false} fail + */ + public static boolean setDensity(final int density) { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("wm density " + density, true); + return result.isSuccess2(); + } + + /** + * 恢复原屏幕密度 + * @return {@code true} success, {@code false} fail + */ + public static boolean resetDensity() { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("wm density reset", true); + return result.isSuccess2(); + } + + /** + * 显示区域 ( 设置留白边距 ) + * @param left left padding + * @param top top padding + * @param right right padding + * @param bottom bottom padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setOverscan(final int left, final int top, final int right, final int bottom) { + String cmd = "wm overscan %s,%s,%s,%s"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, left, top, right, bottom), true); + return result.isSuccess2(); + } + + /** + * 恢复原显示区域 + * @return {@code true} success, {@code false} fail + */ + public static boolean resetOverscan() { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("wm overscan reset", true); + return result.isSuccess2(); + } + + /** + * 获取亮度是否为自动获取 ( 自动调节亮度 ) + * @return 1 开启、0 未开启、-1 未知 + */ + public static int getScreenBrightnessMode() { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("settings get system screen_brightness_mode", true); + if (result.isSuccess3()) { + try { + return Integer.parseInt(result.successMsg); + } catch (Exception e) { + } + } + return -1; + } + + /** + * 设置亮度是否为自动获取 ( 自动调节亮度 ) + * @param isAuto 是否自动调节 + * @return {@code true} success, {@code false} fail + */ + public static boolean setScreenBrightnessMode(final boolean isAuto) { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("settings put system screen_brightness_mode " + (isAuto ? 1 : 0), true); + return result.isSuccess3(); + } + + /** + * 获取屏幕亮度值 + * @return 屏幕亮度值 + */ + public static String getScreenBrightness() { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("settings get system screen_brightness", true); + if (result.isSuccess3()) { + String suc = result.successMsg; + if (suc.startsWith("\"")) { + suc = suc.substring(1); + } + if (suc.endsWith("\"")) { + suc = suc.substring(0, suc.length() - 1); + } + return suc; + } + return null; + } + + /** + * 更改屏幕亮度值 ( 亮度值在 0-255 之间 ) + * @param brightness 亮度值 + * @return {@code true} success, {@code false} fail + */ + public static boolean setScreenBrightness(@IntRange(from = 0, to = 255) final int brightness) { + if (brightness < 0) { + return false; + } else if (brightness > 255) { + return false; + } + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("settings put system screen_brightness " + brightness, true); + return result.isSuccess2(); + } + + /** + * 获取自动锁屏休眠时间 ( 单位毫秒 ) + * @return 自动锁屏休眠时间 + */ + public static String getScreenOffTimeout() { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("settings get system screen_off_timeout", true); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 设置自动锁屏休眠时间 ( 单位毫秒 ) + *
+     *     设置永不休眠 Integer.MAX_VALUE
+     * 
+ * @param time 休眠时间 ( 单位毫秒 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean setScreenOffTimeout(final long time) { + if (time <= 0) { + return false; + } + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("settings put system screen_off_timeout " + time, true); + return result.isSuccess2(); + } + + /** + * 获取日期时间选项中通过网络获取时间的状态 + * @return 1 允许、0 不允许、-1 未知 + */ + public static int getGlobalAutoTime() { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("settings get global auto_time", true); + if (result.isSuccess3()) { + try { + return Integer.parseInt(result.successMsg); + } catch (Exception e) { + } + } + return -1; + } + + /** + * 修改日期时间选项中通过网络获取时间的状态, 设置是否开启 + * @param isOpen 是否设置通过网络获取时间 + * @return {@code true} success, {@code false} fail + */ + public static boolean setGlobalAutoTime(final boolean isOpen) { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("settings put global auto_time " + (isOpen ? 1 : 0), true); + return result.isSuccess3(); + } + + /** + * 关闭 USB 调试模式 + * @return {@code true} success, {@code false} fail + */ + public static boolean disableADB() { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("settings put global adb_enabled 0", true); + return result.isSuccess2(); + } + + /** + * 允许访问非 SDK API + *
+     *     不需要设备获得 Root 权限
+     * 
+ * @return 执行结果 + */ + public static int putHiddenApi() { + String[] cmds = new String[2]; + cmds[0] = "settings put global hidden_api_policy_pre_p_apps 1"; + cmds[1] = "settings put global hidden_api_policy_p_apps 1"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmds, true); + return result.result; + } + + /** + * 禁止访问非 SDK API + *
+     *     不需要设备获得 Root 权限
+     * 
+ * @return 执行结果 + */ + public static int deleteHiddenApi() { + String[] cmds = new String[2]; + cmds[0] = "settings delete global hidden_api_policy_pre_p_apps"; + cmds[1] = "settings delete global hidden_api_policy_p_apps"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmds, true); + return result.result; + } + + /** + * 开启无障碍辅助功能 + * @param packageName 应用包名 + * @param accessibilityServiceName 无障碍服务名 + * @return {@code true} success, {@code false} fail + */ + public static boolean openAccessibility(final String packageName, final String accessibilityServiceName) { + if (isSpace(packageName)) return false; + if (isSpace(accessibilityServiceName)) return false; + + String cmd = "settings put secure enabled_accessibility_services %s/%s"; + // 格式化 shell 命令 + String[] cmds = new String[2]; + cmds[0] = String.format(cmd, packageName, accessibilityServiceName); + cmds[1] = "settings put secure accessibility_enabled 1"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmds, true); + return result.isSuccess2(); + } + + /** + * 关闭无障碍辅助功能 + * @param packageName 应用包名 + * @param accessibilityServiceName 无障碍服务名 + * @return {@code true} success, {@code false} fail + */ + public static boolean closeAccessibility(final String packageName, final String accessibilityServiceName) { + if (isSpace(packageName)) return false; + if (isSpace(accessibilityServiceName)) return false; + + String cmd = "settings put secure enabled_accessibility_services %s/%s"; + // 格式化 shell 命令 + String[] cmds = new String[2]; + cmds[0] = String.format(cmd, packageName, accessibilityServiceName); + cmds[1] = "settings put secure accessibility_enabled 0"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmds, true); + return result.isSuccess2(); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = FileUtils = + // ============= + + /** + * 检查是否存在某个文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + private static boolean isFileExists(final File file) { + return file != null && file.exists(); + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/AccessibilityUtils.java b/lib/DevApp/src/main/java/dev/utils/app/AccessibilityUtils.java new file mode 100644 index 0000000000..674f19f38a --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/AccessibilityUtils.java @@ -0,0 +1,744 @@ +package dev.utils.app; + +import android.accessibilityservice.AccessibilityService; +import android.content.Intent; +import android.os.Build; +import android.provider.Settings; +import android.support.annotation.RequiresApi; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; + +import java.util.ArrayList; +import java.util.List; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 无障碍功能工具类 + * @author Ttt + *
+ *     @see 
+ *     @see 
+ *     @see 
+ *     @see 
+ *     

+ * AccessibilityService 在 API < 18 的时候使用 AccessibilityService + * 所需权限 + * + *
+ */ +public final class AccessibilityUtils { + + private AccessibilityUtils() { + } + + // 日志 TAG + private static final String TAG = AccessibilityUtils.class.getSimpleName(); + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + // AccessibilityService 对象 + private static AccessibilityService sService = null; + + /** + * 获取 AccessibilityService 对象 + * @return {@link AccessibilityService} + */ + public static AccessibilityService getService() { + return sService; + } + + /** + * 设置 AccessibilityService 对象 + * @param service {@link AccessibilityService} + */ + public static void setService(final AccessibilityService service) { + AccessibilityUtils.sService = service; + } + + // = + + /** + * 检查是否开启无障碍功能 + *
+     *     未开启则跳转至辅助功能设置页面
+     * 
+ * @return {@code true} open, {@code false} close + */ + public static boolean checkAccessibility() { + return checkAccessibility(DevUtils.getContext().getPackageName()); + } + + /** + * 检查是否开启无障碍功能 + *
+     *     未开启则跳转至辅助功能设置页面
+     * 
+ * @param packageName 应用包名 + * @return {@code true} open, {@code false} close + */ + public static boolean checkAccessibility(final String packageName) { + if (packageName == null) return false; + // 判断辅助功能是否开启 + if (!AccessibilityUtils.isAccessibilitySettingsOn(packageName)) { + // 跳转至辅助功能设置页面 + DevUtils.getContext().startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + return false; + } + return true; + } + + /** + * 判断是否开启无障碍功能 + * @param packageName 应用包名 + * @return {@code true} open, {@code false} close + */ + public static boolean isAccessibilitySettingsOn(final String packageName) { + if (packageName == null) return false; + // 无障碍功能开启状态 + int accessibilityEnabled = 0; + try { + accessibilityEnabled = Settings.Secure.getInt(DevUtils.getContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED); + } catch (Settings.SettingNotFoundException e) { + LogPrintUtils.eTag(TAG, e, "isAccessibilitySettingsOn - Settings.Secure.ACCESSIBILITY_ENABLED"); + } + if (accessibilityEnabled == 1) { + try { + String services = Settings.Secure.getString(DevUtils.getContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); + if (services != null) { + return services.toLowerCase().contains(packageName.toLowerCase()); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isAccessibilitySettingsOn - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES"); + } + } + return false; + } + + // ============ + // = 打印方法 = + // ============ + + /** + * 打印 AccessibilityEvent 信息日志 + * @param event {@link AccessibilityEvent} + */ + public static void printAccessibilityEvent(final AccessibilityEvent event) { + printAccessibilityEvent(event, TAG); + } + + /** + * 打印 AccessibilityEvent 信息日志 + * @param event {@link AccessibilityEvent} + * @param tag 日志 TAG + */ + public static void printAccessibilityEvent(final AccessibilityEvent event, final String tag) { + if (event == null || !LogPrintUtils.isPrintLog()) return; + + StringBuilder builder = new StringBuilder(); + builder.append("========================="); + builder.append(NEW_LINE_STR); + + int eventType = event.getEventType(); // 事件类型 + builder.append("packageName: " + event.getPackageName() + ""); // 响应事件的应用包名 + builder.append(NEW_LINE_STR); + + builder.append("source: " + event.getSource() + ""); // 事件源信息 + builder.append(NEW_LINE_STR); + + builder.append("source class: " + event.getClassName() + ""); // 事件源的类名, 如 android.widget.TextView + builder.append(NEW_LINE_STR); + + builder.append("event type(int): " + eventType + ""); + builder.append(NEW_LINE_STR); + + switch (eventType) { + case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:// 通知栏事件 + builder.append("event type: TYPE_NOTIFICATION_STATE_CHANGED"); + break; + case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: // 窗体状态改变 + builder.append("event type: TYPE_WINDOW_STATE_CHANGED"); + break; + case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: // View 获取到焦点 + builder.append("event type: TYPE_VIEW_ACCESSIBILITY_FOCUSED"); + break; + case AccessibilityEvent.TYPE_GESTURE_DETECTION_START: + builder.append("event type: TYPE_VIEW_ACCESSIBILITY_FOCUSED"); + break; + case AccessibilityEvent.TYPE_GESTURE_DETECTION_END: + builder.append("event type: TYPE_GESTURE_DETECTION_END"); + break; + case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: + builder.append("event type: TYPE_WINDOW_CONTENT_CHANGED"); + break; + case AccessibilityEvent.TYPE_VIEW_CLICKED: + builder.append("event type: TYPE_VIEW_CLICKED"); + break; + case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: + builder.append("event type: TYPE_VIEW_TEXT_CHANGED"); + break; + case AccessibilityEvent.TYPE_VIEW_SCROLLED: + builder.append("event type: TYPE_VIEW_SCROLLED"); + break; + case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: + builder.append("event type: TYPE_VIEW_TEXT_SELECTION_CHANGED"); + break; + } + builder.append(NEW_LINE_STR); + + for (CharSequence txt : event.getText()) { + // 输出当前事件包含的文本信息 + builder.append("text: " + txt); + builder.append(NEW_LINE_STR); + } + builder.append("========================="); + + // 打印日志 + LogPrintUtils.dTag(tag, builder.toString()); + } + + // ============ + // = 节点获取 = + // ============ + + /** + * 查找符合条件的节点 + * @param focus 焦点类型 + * @return 拥有特定焦点类型的节点 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static AccessibilityNodeInfo findFocus(final int focus) { + return findFocus(sService, focus); + } + + /** + * 查找符合条件的节点 + * @param service {@link AccessibilityService} + * @param focus 焦点类型 + * @return 拥有特定焦点类型的节点 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static AccessibilityNodeInfo findFocus(final AccessibilityService service, final int focus) { + if (service == null) return null; + // 获取根节点 + AccessibilityNodeInfo nodeInfo = service.getRootInActiveWindow(); + // 取得当前激活窗体的根节点 + if (nodeInfo == null) return null; + // 通过指定的焦点类型找到当前的节点 + return nodeInfo.findFocus(focus); + } + + // = + + /** + * 查找符合条件的节点 + * @param focus 焦点类型 + * @param className 节点所属的类 ( 类名 ) + * @return 拥有特定焦点类型的节点 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static AccessibilityNodeInfo findFocus(final int focus, final String className) { + return findFocus(sService, focus, className); + } + + /** + * 查找符合条件的节点 + * @param service {@link AccessibilityService} + * @param focus 焦点类型 + * @param className 节点所属的类 ( 类名 ) + * @return 拥有特定焦点类型的节点 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static AccessibilityNodeInfo findFocus(final AccessibilityService service, final int focus, final String className) { + if (service == null || className == null) return null; + // 获取根节点 + AccessibilityNodeInfo nodeInfo = service.getRootInActiveWindow(); + // 取得当前激活窗体的根节点 + if (nodeInfo == null) return null; + // 通过指定的焦点类型找到当前的节点 + AccessibilityNodeInfo node = nodeInfo.findFocus(focus); + // 防止为 null + if (node != null) { + // 判断是否符合的类型 + if (node.getClassName().equals(className) && node.isEnabled()) { + return node; + } + } + return null; + } + + // = + + /** + * 查找符合条件的节点 + * @param text 文本内容 ( 搜索包含该文本内容的节点 ) + * @return 包含该文本内容的节点集合 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static List findAccessibilityNodeInfosByText(final String text) { + return findAccessibilityNodeInfosByText(sService, text); + } + + /** + * 查找符合条件的节点 + * @param service {@link AccessibilityService} + * @param text 文本内容 ( 搜索包含该文本内容的节点 ) + * @return 包含该文本内容的节点集合 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static List findAccessibilityNodeInfosByText(final AccessibilityService service, final String text) { + if (service == null || text == null) return null; + // 获取根节点 + AccessibilityNodeInfo nodeInfo = service.getRootInActiveWindow(); + // 取得当前激活窗体的根节点 + if (nodeInfo == null) return null; + // 通过文字找到当前的节点 + return nodeInfo.findAccessibilityNodeInfosByText(text); + } + + // = + + /** + * 查找符合条件的节点 + * @param text 文本内容 ( 搜索包含该文本内容的节点 ) + * @param className 节点所属的类 ( 类名 ) + * @return 包含该文本内容, 且属于指定类的节点集合 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static List findAccessibilityNodeInfosByText(final String text, final String className) { + return findAccessibilityNodeInfosByText(sService, text, className); + } + + /** + * 查找符合条件的节点 + * @param service {@link AccessibilityService} + * @param text 文本内容 ( 搜索包含该文本内容的节点 ) + * @param className 节点所属的类 ( 类名 ) + * @return 包含该文本内容, 且属于指定类的节点集合 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static List findAccessibilityNodeInfosByText(final AccessibilityService service, final String text, final String className) { + if (service == null || text == null || className == null) return null; + List lists = new ArrayList<>(); + // 获取根节点 + AccessibilityNodeInfo nodeInfo = service.getRootInActiveWindow(); + // 取得当前激活窗体的根节点 + if (nodeInfo == null) return lists; + // 通过文字找到当前的节点 + List nodes = nodeInfo.findAccessibilityNodeInfosByText(text); + for (int i = 0; i < nodes.size(); i++) { + AccessibilityNodeInfo node = nodes.get(i); + // 判断是否符合的类型 + if (node.getClassName().equals(className) && node.isEnabled()) { + // 保存符合条件 + lists.add(node); + } + } + return lists; + } + + // = + + /** + * 查找符合条件的节点 + * @param id viewId + * @return 等于 viewId 的节点集合 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + public static List findAccessibilityNodeInfosByViewId(final String id) { + return findAccessibilityNodeInfosByViewId(sService, id); + } + + /** + * 查找符合条件的节点 + * @param service {@link AccessibilityService} + * @param id viewId + * @return 等于 viewId 的节点集合 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + public static List findAccessibilityNodeInfosByViewId(final AccessibilityService service, final String id) { + if (service == null || id == null) return null; + // 获取根节点 + AccessibilityNodeInfo nodeInfo = service.getRootInActiveWindow(); + // 取得当前激活窗体的根节点 + if (nodeInfo == null) return null; + // 通过 id 找到当前的节点 + return nodeInfo.findAccessibilityNodeInfosByViewId(id); + } + + // = + + /** + * 查找符合条件的节点 + * @param id viewId + * @param className 节点所属的类 ( 类名 ) + * @return 等于 viewId, 且属于指定类的节点集合 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + public static List findAccessibilityNodeInfosByViewId(final String id, final String className) { + return findAccessibilityNodeInfosByViewId(sService, id, className); + } + + /** + * 查找符合条件的节点 + * @param service {@link AccessibilityService} + * @param id viewId + * @param className 节点所属的类 ( 类名 ) + * @return 等于 viewId, 且属于指定类的节点集合 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + public static List findAccessibilityNodeInfosByViewId(final AccessibilityService service, final String id, final String className) { + if (service == null || id == null || className == null) return null; + List lists = new ArrayList<>(); + // 获取根节点 + AccessibilityNodeInfo nodeInfo = service.getRootInActiveWindow(); + // 取得当前激活窗体的根节点 + if (nodeInfo == null) return lists; + // 通过 id 找到当前的节点 + List nodes = nodeInfo.findAccessibilityNodeInfosByViewId(id); + for (int i = 0; i < nodes.size(); i++) { + AccessibilityNodeInfo node = nodes.get(i); + // 判断是否符合的类型 + if (node.getClassName().equals(className) && node.isEnabled()) { + // 保存符合条件 + lists.add(node); + } + } + return lists; + } + + // ======== + // = 操作 = + // ======== + + /** + * 点击指定的节点 + * @param nodeInfo {@link AccessibilityNodeInfo} + * @return {@code true} success, {@code false} fail + */ + public static boolean performClick(final AccessibilityNodeInfo nodeInfo) { + if (nodeInfo != null && nodeInfo.isClickable()) { + return preformAction(nodeInfo, AccessibilityNodeInfo.ACTION_CLICK); + } + return false; + } + + /** + * 点击指定的节点 + * @param nodeInfo {@link AccessibilityNodeInfo} + * @param clickParent 如果当前节点不可点击, 是否往上追溯点击父节点, 直到点击成功或没有父节点 + */ + public static void performClick(final AccessibilityNodeInfo nodeInfo, final boolean clickParent) { + performClick(nodeInfo, clickParent, false); + } + + /** + * 点击指定的节点 + * @param nodeInfo {@link AccessibilityNodeInfo} + * @param clickParent 如果当前节点不可点击, 是否往上追溯点击父节点, 直到点击成功或没有父节点 + * @param clickAll 判断是否点击全部节点 + */ + public static void performClick(final AccessibilityNodeInfo nodeInfo, final boolean clickParent, final boolean clickAll) { + if (nodeInfo == null) return; + if (clickParent) { + if (nodeInfo.isClickable()) { + nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); + } else { + AccessibilityNodeInfo parent = nodeInfo.getParent(); + while (parent != null) { + if (performClick(parent)) { + if (!clickAll) { + return; + } + } + parent = parent.getParent(); + } + } + } else { + performClick(nodeInfo); + } + } + + // = + + /** + * 长按指定的节点 + * @param nodeInfo {@link AccessibilityNodeInfo} + * @return {@code true} success, {@code false} fail + */ + public static boolean performLongClick(final AccessibilityNodeInfo nodeInfo) { + if (nodeInfo != null && nodeInfo.isClickable()) { + return preformAction(nodeInfo, AccessibilityNodeInfo.ACTION_LONG_CLICK); + } + return false; + } + + /** + * 长按指定的节点 + * @param nodeInfo {@link AccessibilityNodeInfo} + * @param clickParent 如果当前节点不可点击, 是否往上追溯点击父节点, 直到点击成功或没有父节点 + */ + public static void performLongClick(final AccessibilityNodeInfo nodeInfo, final boolean clickParent) { + performLongClick(nodeInfo, clickParent, false); + } + + /** + * 长按指定的节点 + * @param nodeInfo {@link AccessibilityNodeInfo} + * @param clickParent 如果当前节点不可点击, 是否往上追溯点击父节点, 直到点击成功或没有父节点 + * @param clickAll 判断是否点击全部节点 + */ + public static void performLongClick(final AccessibilityNodeInfo nodeInfo, final boolean clickParent, final boolean clickAll) { + if (nodeInfo == null) return; + if (clickParent) { + if (nodeInfo.isClickable()) { + nodeInfo.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); + } else { + AccessibilityNodeInfo parent = nodeInfo.getParent(); + while (parent != null) { + if (performLongClick(parent)) { + if (!clickAll) { + return; + } + } + parent = parent.getParent(); + } + } + } else { + performLongClick(nodeInfo); + } + } + + // = + + /** + * 触发返回键 + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionBack() { + return performGlobalAction(sService, AccessibilityService.GLOBAL_ACTION_BACK); + } + + /** + * 触发返回键 + * @param service {@link AccessibilityService} + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionBack(final AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_BACK); + } + + /** + * 触发 Home 键 + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionHome() { + return performGlobalAction(sService, AccessibilityService.GLOBAL_ACTION_HOME); + } + + /** + * 触发 Home 键 + * @param service {@link AccessibilityService} + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionHome(final AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_HOME); + } + + /** + * 启动长按电源按钮 Dialog + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionPowerDialog() { + return performGlobalAction(sService, AccessibilityService.GLOBAL_ACTION_POWER_DIALOG); + } + + /** + * 启动长按电源按钮 Dialog + * @param service {@link AccessibilityService} + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionPowerDialog(final AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_POWER_DIALOG); + } + + /** + * 锁定屏幕 ( 非锁屏 ) + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionLockScreen() { + return performGlobalAction(sService, AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN); + } + + /** + * 锁定屏幕 ( 非锁屏 ) + * @param service {@link AccessibilityService} + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionLockScreen(final AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN); + } + + /** + * 截屏 + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionTakeScreenshot() { + return performGlobalAction(sService, AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT); + } + + /** + * 截屏 + * @param service {@link AccessibilityService} + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionTakeScreenshot(final AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT); + } + + /** + * 打开通知栏 + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionNotifications() { + return performGlobalAction(sService, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS); + } + + /** + * 打开通知栏 + * @param service {@link AccessibilityService} + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionNotifications(final AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS); + } + + /** + * 最近打开应用列表 + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionRecents() { + return performGlobalAction(sService, AccessibilityService.GLOBAL_ACTION_RECENTS); + } + + /** + * 最近打开应用列表 + * @param service {@link AccessibilityService} + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionRecents(final AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_RECENTS); + } + + /** + * 打开设置 + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionQuickSettings() { + return performGlobalAction(sService, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS); + } + + /** + * 打开设置 + * @param service {@link AccessibilityService} + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionQuickSettings(final AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS); + } + + /** + * 分屏 + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionSplitScreen() { + return performGlobalAction(sService, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN); + } + + /** + * 分屏 + * @param service {@link AccessibilityService} + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionSplitScreen(final AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN); + } + + // ============ + // = 统一调用 = + // ============ + + /** + * 模拟对应 Action 操作 + * @param nodeInfo {@link AccessibilityNodeInfo} + * @param action 操作意图 + * @return {@code true} success, {@code false} fail + */ + public static boolean preformAction(final AccessibilityNodeInfo nodeInfo, final int action) { + if (nodeInfo != null) { + return nodeInfo.performAction(action); + } + return false; + } + + /** + * 模拟全局对应 Action 操作 + * @param service {@link AccessibilityService} + * @param action 操作意图 + * @return {@code true} success, {@code false} fail + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean performGlobalAction(final AccessibilityService service, final int action) { + if (service != null) { + return service.performGlobalAction(action); + } + return false; + } + +// // 获取根节点 +// AccessibilityNodeInfo rootNode = getRootInActiveWindow(); +// // 匹配 Text 获取节点 +// List list1 = rootNode.findAccessibilityNodeInfosByText("match_text"); +// // 匹配 id 获取节点 +// List list2 = rootNode.findAccessibilityNodeInfosByViewId("match_id"); +// // 获取子节点 +// AccessibilityNodeInfo infoNode = rootNode.getChild(index); + +// // 模拟输入内容 +// ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); +// ClipData clip = ClipData.newPlainText("label", ""); +// clipboard.setPrimaryClip(clip); +// target.performAction(AccessibilityNodeInfo.ACTION_PASTE); + +// // 向上滑动 +// performGlobalAction(service, AccessibilityService.GESTURE_SWIPE_UP); + +// disableSelf() // 禁用当前服务, 也就是在服务可以通过该方法停止运行 +// getSystemService(String name) // 获取系统服务 +// onServiceConnected() // 系统成功绑定该服务时被触发, 也就是当你在设置中开启相应的服务, 系统成功的绑定了该服务时会触发, 通常我们可以在这里做一些初始化操作 +// getSeviceInfo() // 获取当前服务的配置信息 +// setServiceInfo(AccessibilityServiceInfo info) // 设置当前服务的配置信息 +// onAccessibilityEvent(AccessibilityEvent event) // 有关 AccessibilityEvent 事件的回调函数, 系统通过 sendAccessibiliyEvent() 不断的发送 AccessibilityEvent 到此处 +// performGlobalAction(int action) // 执行全局操作, 比如回到主页、打开最近等操作 +// findFoucs(int falg) // 查找拥有特定焦点类型的控件 +// getRootInActiveWindow() // 如果配置能够获取窗口内容, 则会返回当前活动窗口的根结点 +// onKeyEvent(KeyEvent event) // 如果允许服务监听按键操作, 该方法是按键事件的回调, 需要注意, 这个过程发生了系统处理按键事件之前 +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ActivityUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ActivityUtils.java new file mode 100644 index 0000000000..cbf82b8ad8 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ActivityUtils.java @@ -0,0 +1,849 @@ +package dev.utils.app; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.ActivityOptionsCompat; +import android.support.v4.util.Pair; +import android.view.View; + +import java.util.Iterator; +import java.util.List; +import java.util.Stack; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Activity 工具类 ( 包含 Activity 控制管理 ) + * @author Ttt + *
+ *     转场动画
+ *     @see 
+ *     @see 
+ *     ActivityOptionsCompat.makeScaleUpAnimation(source, startX, startY, startWidth, startHeight)
+ *     @see 
+ * 
+ */ +public final class ActivityUtils { + + private ActivityUtils() { + } + + // 日志 TAG + private static final String TAG = ActivityUtils.class.getSimpleName(); + + // ===================== + // = Activity 判断处理 = + // ===================== + + /** + * 通过 Context 获取 Activity + * @param context {@link Context} + * @return {@link Activity} + */ + public static Activity getActivity(final Context context) { + if (context != null) { + try { + return (Activity) context; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivity"); + } + } + return null; + } + + /** + * 获取 View context 所属的 Activity + * @param view {@link View} + * @return {@link Activity} + */ + public static Activity getActivity(final View view) { + if (view != null) { + try { + Context context = view.getContext(); + while (context instanceof ContextWrapper) { + if (context instanceof Activity) { + return (Activity) context; + } + context = ((ContextWrapper) context).getBaseContext(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivity"); + } + } + return null; + } + + // = + + /** + * 判断 Activity 是否关闭 + * @param activity {@link Activity} + * @return {@code true} yes, {@code false} no + */ + public static boolean isFinishing(final Activity activity) { + if (activity != null) { + return activity.isFinishing(); + } + return false; + } + + /** + * 判断 Activity 是否关闭 + * @param context {@link Context} + * @return {@code true} yes, {@code false} no + */ + public static boolean isFinishing(final Context context) { + if (context != null) { + try { + return ((Activity) context).isFinishing(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isFinishing"); + } + } + return false; + } + + // = + + /** + * 判断是否存在指定的 Activity + * @param className Activity.class.getCanonicalName() + * @return {@code true} 存在, {@code false} 不存在 + */ + public static boolean isActivityExists(final String className) { + return isActivityExists(DevUtils.getContext(), DevUtils.getContext().getPackageName(), className); + } + + /** + * 判断是否存在指定的 Activity + * @param context {@link Context} + * @param className Activity.class.getCanonicalName() + * @return {@code true} 存在, {@code false} 不存在 + */ + public static boolean isActivityExists(final Context context, final String className) { + return isActivityExists(context, DevUtils.getContext().getPackageName(), className); + } + + /** + * 判断是否存在指定的 Activity + * @param packageName 应用包名 + * @param className Activity.class.getCanonicalName() + * @return {@code true} 存在, {@code false} 不存在 + */ + public static boolean isActivityExists(final String packageName, final String className) { + return isActivityExists(DevUtils.getContext(), packageName, className); + } + + /** + * 判断是否存在指定的 Activity + * @param context {@link Context} + * @param packageName 应用包名 + * @param className Activity.class.getCanonicalName() + * @return {@code true} 存在, {@code false} 不存在 + */ + public static boolean isActivityExists(final Context context, final String packageName, final String className) { + if (context == null || packageName == null || className == null) return false; + boolean result = true; + try { + Intent intent = new Intent(); + intent.setClassName(packageName, className); + if (context.getPackageManager().resolveActivity(intent, 0) == null) { + result = false; + } else if (intent.resolveActivity(context.getPackageManager()) == null) { + result = false; + } else { + List list = context.getPackageManager().queryIntentActivities(intent, 0); + if (list.size() == 0) { + result = false; + } + } + } catch (Exception e) { + result = false; + LogPrintUtils.eTag(TAG, e, "isActivityExists"); + } + return result; + } + + // ===================== + // = Activity 获取操作 = + // ===================== + + /** + * 回到桌面 ( 同点击 Home 键效果 ) + */ + public static void startHomeActivity() { + try { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_HOME); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + DevUtils.getContext().startActivity(intent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startHomeActivity"); + } + } + + /** + * 获取 Launcher activity + * @return package.xx.Activity.className + */ + public static String getLauncherActivity() { + try { + return getLauncherActivity(DevUtils.getContext().getPackageName()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLauncherActivity"); + } + return null; + } + + /** + * 获取 Launcher activity + * @param packageName 应用包名 + * @return package.xx.Activity.className + */ + public static String getLauncherActivity(final String packageName) { + if (packageName == null) return null; + try { + Intent intent = new Intent(Intent.ACTION_MAIN, null); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + List lists = packageManager.queryIntentActivities(intent, 0); + for (ResolveInfo resolveinfo : lists) { + if (resolveinfo != null && resolveinfo.activityInfo != null) { + if (resolveinfo.activityInfo.packageName.equals(packageName)) { + return resolveinfo.activityInfo.name; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLauncherActivity"); + } + return null; + } + + /** + * 获取 Activity 对应的 icon + * @param clazz Activity.class + * @return {@link Drawable} Activity 对应的 icon + */ + public static Drawable getActivityIcon(final Class clazz) { + if (clazz == null) return null; + try { + return getActivityIcon(new ComponentName(DevUtils.getContext(), clazz)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivityIcon"); + } + return null; + } + + /** + * 获取 Activity 对应的 icon + * @param componentName {@link ComponentName} + * @return {@link Drawable} Activity 对应的 icon + */ + public static Drawable getActivityIcon(final ComponentName componentName) { + if (componentName == null) return null; + try { + return DevUtils.getContext().getPackageManager().getActivityIcon(componentName); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivityIcon"); + } + return null; + } + + /** + * 获取 Activity 对应的 logo + * @param clazz Activity.class + * @return {@link Drawable} Activity 对应的 logo + */ + public static Drawable getActivityLogo(final Class clazz) { + if (clazz == null) return null; + try { + return getActivityLogo(new ComponentName(DevUtils.getContext(), clazz)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivityLogo"); + } + return null; + } + + /** + * 获取 Activity 对应的 logo + * @param componentName {@link ComponentName} + * @return {@link Drawable} Activity 对应的 logo + */ + public static Drawable getActivityLogo(final ComponentName componentName) { + if (componentName == null) return null; + try { + return DevUtils.getContext().getPackageManager().getActivityLogo(componentName); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivityLogo"); + } + return null; + } + + /** + * 获取对应包名应用启动的 Activity + *
+     *     android.intent.category.LAUNCHER (android.intent.action.MAIN)
+     * 
+ * @param packageName 应用包名 + * @return package.xx.Activity.className + */ + public static String getActivityToLauncher(final String packageName) { + if (packageName == null) return null; + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + // 获取对应的 PackageInfo + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); + + if (packageInfo == null) return null; + + // 创建一个类别为 CATEGORY_LAUNCHER 的该包名的 Intent + Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null); + resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER); + resolveIntent.setPackage(packageInfo.packageName); + + // 通过 getPackageManager() 的 queryIntentActivities 方法遍历 + List lists = packageManager.queryIntentActivities(resolveIntent, 0); + // 循环返回 + for (ResolveInfo resolveinfo : lists) { + if (resolveinfo != null && resolveinfo.activityInfo != null) { + // resolveinfo.activityInfo.packageName; // packageName + // 这个就是该 APP 的 LAUNCHER 的 Activity [ 组织形式: packageName.mainActivityname ] + return resolveinfo.activityInfo.name; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivityToLauncher"); + } + return null; + } + + /** + * 获取系统桌面信息 + * @return {@link ResolveInfo} + */ + public static ResolveInfo getLauncherCategoryHomeToResolveInfo() { + try { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_HOME); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return DevUtils.getContext().getPackageManager().resolveActivity(intent, 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLauncherCategoryHomeToResolveInfo"); + } + return null; + } + + /** + * 获取系统桌面信息 - packageName + *
+     *     注: 存在多个桌面时且未指定默认桌面时, 该方法返回 Null, 使用时需处理这个情况
+     * 
+ * @return packageName + */ + public static String getLauncherCategoryHomeToPackageName() { + ResolveInfo resolveinfo = getLauncherCategoryHomeToResolveInfo(); + if (resolveinfo != null && resolveinfo.activityInfo != null) { + // 有多个桌面程序存在, 且未指定默认项时 + if (resolveinfo.activityInfo.packageName.equals("android")) { + return null; + } else { + return resolveinfo.activityInfo.packageName; + } + } + return null; + } + + /** + * 获取系统桌面信息 - activityName + * @return activityName + */ + public static String getLauncherCategoryHomeToActivityName() { + ResolveInfo resolveinfo = getLauncherCategoryHomeToResolveInfo(); + if (resolveinfo != null && resolveinfo.activityInfo != null) { + // 有多个桌面程序存在, 且未指定默认项时 + if (resolveinfo.activityInfo.packageName.equals("android")) { + return null; + } else { + return resolveinfo.activityInfo.name; + } + } + return null; + } + + /** + * 获取系统桌面信息 - package/activityName + * @return package/activityName + */ + public static String getLauncherCategoryHomeToPackageAndName() { + ResolveInfo resolveinfo = getLauncherCategoryHomeToResolveInfo(); + if (resolveinfo != null && resolveinfo.activityInfo != null) { + // 有多个桌面程序存在, 且未指定默认项时 + if (resolveinfo.activityInfo.packageName.equals("android")) { + return null; + } else { + // 判断是否. 开头 + String name = resolveinfo.activityInfo.name; + if (name != null) { + // 判断是否 . 开头 + if (name.startsWith(".")) { + name = resolveinfo.activityInfo.packageName + name; + } + return resolveinfo.activityInfo.packageName + "/" + name; + } + } + } + return null; + } + + // ============ + // = 转场动画 = + // ============ + + /** + * 设置跳转动画 + * @param context {@link Context} + * @param enterAnim 进入动画 + * @param exitAnim 退出动画 + * @return {@link Bundle} + */ + public static Bundle getOptionsBundle(final Context context, final int enterAnim, final int exitAnim) { + try { + return ActivityOptionsCompat.makeCustomAnimation(context, enterAnim, exitAnim).toBundle(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getOptionsBundle"); + } + return null; + } + + /** + * 设置跳转动画 + * @param activity {@link Activity} + * @param sharedElements 转场动画 View + * @return {@link Bundle} + */ + public static Bundle getOptionsBundle(final Activity activity, final View[] sharedElements) { + if (activity == null) return null; + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + int len = sharedElements.length; + @SuppressWarnings("unchecked") + Pair[] pairs = new Pair[len]; + for (int i = 0; i < len; i++) { + pairs[i] = Pair.create(sharedElements[i], sharedElements[i].getTransitionName()); + } + return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, pairs).toBundle(); + } + return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, null, null).toBundle(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getOptionsBundle"); + } + return null; + } + + // ===================== + // = Activity 管理控制 = + // ===================== + + // ActivityUtils 实例 + private static final ActivityUtils INSTANCE = new ActivityUtils(); + + /** + * 获取 ActivityUtils 管理实例 + * @return {@link ActivityUtils} + */ + public static ActivityUtils getManager() { + return INSTANCE; + } + + // =================== + // = Activity 栈处理 = + // =================== + + // Activity 栈 ( 后进先出 ) + private final Stack mActivityStacks = new Stack<>(); + + /** + * 获取 Activity 栈 + * @return {@link Stack} + */ + public Stack getActivityStacks() { + return mActivityStacks; + } + + /** + * 添加 Activity + * @param activity {@link Activity} + */ + public void addActivity(final Activity activity) { + if (activity != null) { + synchronized (mActivityStacks) { + if (mActivityStacks.contains(activity)) { + return; + } + mActivityStacks.add(activity); + } + } + } + + /** + * 移除 Activity + * @param activity {@link Activity} + */ + public void removeActivity(final Activity activity) { + if (activity != null) { + synchronized (mActivityStacks) { + int index = mActivityStacks.indexOf(activity); + if (index == -1) { + return; + } + try { + mActivityStacks.remove(index); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "removeActivity"); + } + } + } + } + + /** + * 移除多个 Activity + * @param activitys Activity[] + */ + public void removeActivity(final Activity... activitys) { + if (activitys != null && activitys.length != 0) { + for (int i = 0, len = activitys.length; i < len; i++) { + removeActivity(activitys[i]); + } + } + } + + /** + * 获取最后一个 ( 当前 ) Activity + * @return 最后一个 ( 当前 ) {@link Activity} + */ + public Activity currentActivity() { + return mActivityStacks.lastElement(); + } + + /** + * 关闭最后一个 ( 当前 ) Activity + */ + public void finishActivity() { + finishActivity(mActivityStacks.lastElement()); + } + + /** + * 检测是否包含指定的 Activity + * @param clazzs Class(Activity)[] + * @return {@code true} yes, {@code false} no + */ + public boolean existActivitys(final Class... clazzs) { + if (clazzs != null && clazzs.length != 0) { + synchronized (mActivityStacks) { + // 保存新的堆栈, 防止出现同步问题 + Stack stack = new Stack<>(); + stack.addAll(mActivityStacks); + try { + // 进行遍历判断 + Iterator iterator = stack.iterator(); + while (iterator.hasNext()) { + Activity activity = iterator.next(); + if (activity != null && !activity.isFinishing()) { + for (int i = 0, len = clazzs.length; i < len; i++) { + if (clazzs[i] != null && activity.getClass().getName().equals(clazzs[i].getName())) { + return true; + } + } + } + } + } finally { + // 移除数据, 并且清空内存 + stack.clear(); + stack = null; + } + } + } + return false; + } + + /** + * 关闭指定 Activity + * @param activity {@link Activity} + */ + public void finishActivity(final Activity activity) { + // 先移除 Activity + removeActivity(activity); + // Activity 不为 null, 并且属于未销毁状态 + if (activity != null && !activity.isFinishing()) { + activity.finish(); + } + } + + /** + * 关闭多个 Activity + * @param activitys Activity[] + */ + public void finishActivity(final Activity... activitys) { + if (activitys != null && activitys.length != 0) { + for (int i = 0, len = activitys.length; i < len; i++) { + finishActivity(activitys[i]); + } + } + } + + /** + * 关闭指定类名 Activity + * @param clazz Activity.class + */ + public void finishActivity(final Class clazz) { + if (clazz != null) { + synchronized (mActivityStacks) { + // 保存新的堆栈, 防止出现同步问题 + Stack stack = new Stack<>(); + stack.addAll(mActivityStacks); + // 清空全部, 便于后续操作处理 + mActivityStacks.clear(); + // 进行遍历移除 + Iterator iterator = stack.iterator(); + while (iterator.hasNext()) { + Activity activity = iterator.next(); + // 判断是否想要关闭的 Activity + if (activity != null) { + if (activity.getClass() == clazz) { + // 如果 Activity 没有 finish 则进行 finish + if (!activity.isFinishing()) { + activity.finish(); + } + // 删除对应的 Item + iterator.remove(); + } + } else { + // 删除对应的 Item + iterator.remove(); + } + } + // 把不符合条件的保存回去 + mActivityStacks.addAll(stack); + // 移除数据, 并且清空内存 + stack.clear(); + stack = null; + } + } + } + + /** + * 结束多个类名 Activity + * @param clazzs Class(Activity)[] + */ + public void finishActivity(final Class... clazzs) { + if (clazzs != null && clazzs.length != 0) { + synchronized (mActivityStacks) { + // 保存新的堆栈, 防止出现同步问题 + Stack stack = new Stack<>(); + stack.addAll(mActivityStacks); + // 清空全部, 便于后续操作处理 + mActivityStacks.clear(); + // 判断是否销毁 + boolean isRemove; + // 进行遍历移除 + Iterator iterator = stack.iterator(); + while (iterator.hasNext()) { + Activity activity = iterator.next(); + // 判断是否想要关闭的 Activity + if (activity != null) { + // 默认不需要销毁 + isRemove = false; + // 循环判断 + for (int i = 0, len = clazzs.length; i < len; i++) { + // 判断是否相同 + if (activity.getClass() == clazzs[i]) { + isRemove = true; + break; + } + } + // 判断是否销毁 + if (isRemove) { + // 如果 Activity 没有 finish 则进行 finish + if (!activity.isFinishing()) { + activity.finish(); + } + // 删除对应的 Item + iterator.remove(); + } + } else { + // 删除对应的 Item + iterator.remove(); + } + } + // 把不符合条件的保存回去 + mActivityStacks.addAll(stack); + // 移除数据, 并且清空内存 + stack.clear(); + stack = null; + } + } + } + + /** + * 结束全部 Activity 除忽略的 Activity 外 + * @param clazz Activity.class + */ + public void finishAllActivityToIgnore(final Class clazz) { + if (clazz != null) { + synchronized (mActivityStacks) { + // 保存新的堆栈, 防止出现同步问题 + Stack stack = new Stack<>(); + stack.addAll(mActivityStacks); + // 清空全部, 便于后续操作处理 + mActivityStacks.clear(); + // 进行遍历移除 + Iterator iterator = stack.iterator(); + while (iterator.hasNext()) { + Activity activity = iterator.next(); + // 判断是否想要关闭的 Activity + if (activity != null) { + if (!(activity.getClass() == clazz)) { + // 如果 Activity 没有 finish 则进行 finish + if (!activity.isFinishing()) { + activity.finish(); + } + // 删除对应的 Item + iterator.remove(); + } + } else { + // 删除对应的 Item + iterator.remove(); + } + } + // 把不符合条件的保存回去 + mActivityStacks.addAll(stack); + // 移除数据, 并且清空内存 + stack.clear(); + stack = null; + } + } + } + + /** + * 结束全部 Activity 除忽略的 Activity 外 + * @param clazzs Class(Activity)[] + */ + public void finishAllActivityToIgnore(final Class... clazzs) { + if (clazzs != null && clazzs.length != 0) { + synchronized (mActivityStacks) { + // 保存新的堆栈, 防止出现同步问题 + Stack stack = new Stack<>(); + stack.addAll(mActivityStacks); + // 清空全部, 便于后续操作处理 + mActivityStacks.clear(); + // 判断是否销毁 + boolean isRemove; + // 进行遍历移除 + Iterator iterator = stack.iterator(); + while (iterator.hasNext()) { + Activity activity = iterator.next(); + // 判断是否想要关闭的 Activity + if (activity != null) { + // 默认需要销毁 + isRemove = true; + // 循环判断 + for (int i = 0, len = clazzs.length; i < len; i++) { + // 判断是否相同 + if (activity.getClass() == clazzs[i]) { + isRemove = false; + break; + } + } + // 判断是否销毁 + if (isRemove) { + // 如果 Activity 没有 finish 则进行 finish + if (!activity.isFinishing()) { + activity.finish(); + } + // 删除对应的 Item + iterator.remove(); + } + } else { + // 删除对应的 Item + iterator.remove(); + } + } + // 把不符合条件的保存回去 + mActivityStacks.addAll(stack); + // 移除数据, 并且清空内存 + stack.clear(); + stack = null; + } + } + } + + /** + * 结束所有 Activity + */ + public void finishAllActivity() { + synchronized (mActivityStacks) { + // 保存新的堆栈, 防止出现同步问题 + Stack stack = new Stack<>(); + stack.addAll(mActivityStacks); + // 清空全部, 便于后续操作处理 + mActivityStacks.clear(); + // 进行遍历移除 + Iterator iterator = stack.iterator(); + while (iterator.hasNext()) { + Activity activity = iterator.next(); + if (activity != null && !activity.isFinishing()) { + activity.finish(); + // 删除对应的 Item + iterator.remove(); + } + } + // 移除数据, 并且清空内存 + stack.clear(); + stack = null; + } + } + + // = + + /** + * 退出应用程序 + */ + public void appExit() { + try { + finishAllActivity(); + // 退出 JVM (Java 虚拟机 ) 释放所占内存资源, 0 表示正常退出、非 0 的都为异常退出 + System.exit(0); + // 从操作系统中结束掉当前程序的进程 + android.os.Process.killProcess(android.os.Process.myPid()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "appExit"); + // = + System.exit(-1); + } + } + + /** + * 重启 APP + */ + public void restartApplication() { + try { + Intent intent = DevUtils.getContext().getPackageManager().getLaunchIntentForPackage(DevUtils.getContext().getPackageName()); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + DevUtils.getContext().startActivity(intent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "restartApplication"); + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/AlarmUtils.java b/lib/DevApp/src/main/java/dev/utils/app/AlarmUtils.java new file mode 100644 index 0000000000..919bc56175 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/AlarmUtils.java @@ -0,0 +1,307 @@ +package dev.utils.app; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.support.annotation.RequiresApi; + +import dev.utils.LogPrintUtils; + +/** + * detail: AlarmManager( 全局定时器、闹钟 ) 工具类 + * @author Ttt + *
+ *     指定时长或以周期形式执行某项操作
+ *     @see 
+ *     关于使用 AlarmManager 的注意事项
+ *     @see 
+ * 
+ */ +public final class AlarmUtils { + + private AlarmUtils() { + } + + // 日志 TAG + private static final String TAG = AlarmUtils.class.getSimpleName(); + + // ============ + // = 开启闹钟 = + // ============ + + /** + * 开启一次性闹钟 + * @param context {@link Context} + * @param triggerAtMillis 执行时间 + * @param pendingIntent {@link PendingIntent} 响应动作 + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void startAlarmIntent(final Context context, final long triggerAtMillis, final PendingIntent pendingIntent) { + startAlarmIntent(context, AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent); + } + + /** + * 开启一次性闹钟 + * @param context {@link Context} + * @param type 闹钟类型, 常用的有 5 个值: + * AlarmManager.ELAPSED_REALTIME、 + * AlarmManager.ELAPSED_REALTIME_WAKEUP、 + * AlarmManager.RTC、 + * AlarmManager.RTC_WAKEUP、 + * AlarmManager.POWER_OFF_WAKEUP + * @param triggerAtMillis 执行时间 + * @param pendingIntent {@link PendingIntent} 响应动作 + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void startAlarmIntent(final Context context, final int type, final long triggerAtMillis, final PendingIntent pendingIntent) { + try { + AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + manager.setExactAndAllowWhileIdle(type, triggerAtMillis, pendingIntent); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + manager.setExact(type, triggerAtMillis, pendingIntent); + } else { + manager.set(type, triggerAtMillis, pendingIntent); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startAlarmIntent"); + } + } + + // ============ + // = 关闭闹钟 = + // ============ + + /** + * 关闭闹钟 + * @param context {@link Context} + * @param pendingIntent {@link PendingIntent} 响应动作 + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void stopAlarmIntent(final Context context, final PendingIntent pendingIntent) { + try { + AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + manager.cancel(pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopAlarmIntent"); + } + } + + // ================ + // = Service 闹钟 = + // ================ + + /** + * 开启 Service 闹钟 + * @param context {@link Context} + * @param triggerAtMillis 执行时间 + * @param clazz Class + * @param action Intent Action + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void startAlarmService(final Context context, final long triggerAtMillis, final Class clazz, final String action) { + try { + Intent intent = new Intent(context, clazz); + intent.setAction(action); + startAlarmService(context, triggerAtMillis, intent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startAlarmService"); + } + } + + /** + * 开启 Service 闹钟 + * @param context {@link Context} + * @param triggerAtMillis 执行时间 + * @param intent {@link Intent} + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void startAlarmService(final Context context, final long triggerAtMillis, final Intent intent) { + try { + PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + startAlarmIntent(context, triggerAtMillis, pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startAlarmService"); + } + } + + // = + + /** + * 关闭 Service 闹钟 + * @param context {@link Context} + * @param clazz Class + * @param action Intent Action + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void stopAlarmService(final Context context, final Class clazz, final String action) { + try { + Intent intent = new Intent(context, clazz); + intent.setAction(action); + stopAlarmService(context, intent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopAlarmService"); + } + } + + /** + * 关闭 Service 闹钟 + * @param context {@link Context} + * @param intent {@link Intent} + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void stopAlarmService(final Context context, final Intent intent) { + try { + PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + stopAlarmIntent(context, pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopAlarmService"); + } + } + + // ===================== + // = ForegroundService = + // ===================== + + /** + * 开启 ForegroundService 闹钟 + * @param context {@link Context} + * @param triggerAtMillis 执行时间 + * @param clazz Class + * @param action Intent Action + */ + @RequiresApi(Build.VERSION_CODES.O) + public static void startAlarmForegroundService(final Context context, final long triggerAtMillis, final Class clazz, final String action) { + try { + Intent intent = new Intent(context, clazz); + intent.setAction(action); + startAlarmForegroundService(context, triggerAtMillis, intent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startAlarmForegroundService"); + } + } + + /** + * 开启 ForegroundService 闹钟 + * @param context {@link Context} + * @param triggerAtMillis 执行时间 + * @param intent {@link Intent} + */ + @RequiresApi(Build.VERSION_CODES.O) + public static void startAlarmForegroundService(final Context context, final long triggerAtMillis, final Intent intent) { + try { + PendingIntent pendingIntent = PendingIntent.getForegroundService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + startAlarmIntent(context, triggerAtMillis, pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startAlarmForegroundService"); + } + } + + // = + + /** + * 关闭 ForegroundService 闹钟 + * @param context {@link Context} + * @param clazz Class + * @param action Intent Action + */ + @RequiresApi(Build.VERSION_CODES.O) + public static void stopAlarmForegroundService(final Context context, final Class clazz, final String action) { + try { + Intent intent = new Intent(context, clazz); + intent.setAction(action); + stopAlarmForegroundService(context, intent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopAlarmForegroundService"); + } + } + + /** + * 关闭 ForegroundService 闹钟 + * @param context {@link Context} + * @param intent {@link Intent} + */ + @RequiresApi(Build.VERSION_CODES.O) + public static void stopAlarmForegroundService(final Context context, final Intent intent) { + try { + PendingIntent pendingIntent = PendingIntent.getForegroundService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + stopAlarmIntent(context, pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopAlarmForegroundService"); + } + } + + // =========================== + // = Broadcast Receiver 闹钟 = + // =========================== + + /** + * 开启 Receiver 闹钟 + * @param context {@link Context} + * @param triggerAtMillis 执行时间 + * @param intent {@link Intent} + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void startAlarmBroadcast(final Context context, final long triggerAtMillis, final Intent intent) { + try { + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + startAlarmIntent(context, triggerAtMillis, pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startAlarmBroadcast"); + } + } + + // = + + /** + * 关闭 Receiver 闹钟 + * @param context {@link Context} + * @param intent {@link Intent} + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void stopAlarmBroadcast(final Context context, final Intent intent) { + try { + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + stopAlarmIntent(context, pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopAlarmBroadcast"); + } + } + + // ================= + // = Activity 闹钟 = + // ================= + + /** + * 开启 Activity 闹钟 + * @param context {@link Context} + * @param triggerAtMillis 执行时间 + * @param intent {@link Intent} + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void startAlarmActivity(final Context context, final long triggerAtMillis, final Intent intent) { + try { + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + startAlarmIntent(context, triggerAtMillis, pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startAlarmActivity"); + } + } + + /** + * 关闭 Activity 闹钟 + * @param context {@link Context} + * @param intent {@link Intent} + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void stopAlarmActivity(final Context context, final Intent intent) { + try { + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + stopAlarmIntent(context, pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopAlarmActivity"); + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/AnalysisRecordUtils.java b/lib/DevApp/src/main/java/dev/utils/app/AnalysisRecordUtils.java new file mode 100644 index 0000000000..83d83d3e9a --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/AnalysisRecordUtils.java @@ -0,0 +1,944 @@ +package dev.utils.app; + +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Environment; +import android.support.annotation.IntDef; +import android.text.TextUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 日志记录分析工具类 + * @author Ttt + */ +public final class AnalysisRecordUtils { + + private AnalysisRecordUtils() { + } + + // 日志 TAG + private static final String TAG = AnalysisRecordUtils.class.getSimpleName(); + // 日志文件夹名字 ( 目录名 ) + private static String sLogFolderName = "LogRecord"; + // 日志存储路径 + private static String sLogStoragePath; + // 是否处理保存 + private static boolean sIsHandler = true; + // 判断是否加空格 + private static boolean sAppendSpace = true; + + // ============ + // = 配置信息 = + // ============ + + // APP 版本 ( 如 1.0.01) 显示给用户看的 + private static String APP_VERSION_NAME = ""; + // android:versionCode 整数值, 代表应用程序代码的相对版本 + private static String APP_VERSION_CODE = ""; + // 应用包名 + private static String PACKAGE_NAME = ""; + // 设备信息 + private static String DEVICE_INFO_STR = null; + // 设备信息存储 Map + private static Map DEVICE_INFO_MAPS = new HashMap<>(); + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + // 换行字符串 ( 两行 ) + private static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; + + /** + * 初始化操作 ( 内部已调用 ) + */ + public static void init() { + // 如果为 null, 才设置 + if (TextUtils.isEmpty(sLogStoragePath)) { + // 获取根路径 + sLogStoragePath = getDiskCacheDir(); + } + + // 如果版本信息为 null, 才进行处理 + if (TextUtils.isEmpty(APP_VERSION_CODE) || TextUtils.isEmpty(APP_VERSION_NAME)) { + // 获取 APP 版本信息 + String[] versions = getAppVersion(); + // 防止为 null + if (versions != null && versions.length == 2) { + // 保存 APP 版本信息 + APP_VERSION_NAME = versions[0]; + APP_VERSION_CODE = versions[1]; + } + } + + // 获取包名 + if (TextUtils.isEmpty(PACKAGE_NAME)) { + try { + PACKAGE_NAME = DevUtils.getContext().getPackageName(); + } catch (Exception e) { + } + } + + // 判断是否存在设备信息 + if (DEVICE_INFO_MAPS.size() == 0) { + // 获取设备信息 + getDeviceInfo(DEVICE_INFO_MAPS); + // 转换字符串 + handlerDeviceInfo(""); + } + } + + // ============ + // = 记录方法 = + // ============ + + /** + * 日志记录 + * @param fileInfo {@link FileInfo} + * @param logs 日志内容数组 + * @return 日志内容 + */ + public static String record(final FileInfo fileInfo, final String... logs) { + // 如果不处理, 则直接跳过 + if (!sIsHandler) { + return "do not process records"; + } + if (fileInfo != null) { + if (!fileInfo.isHandler()) { + return "file not recorded"; + } + if (logs != null && logs.length != 0) { + return saveLogRecord(fileInfo, logs); + } + // 无数据记录 + return "no data record"; + } + // 信息为 null + return "fileInfo is null"; + } + + // ================== + // = 判断、获取方法 = + // ================== + + /** + * 判断是否处理日志记录 + * @return {@code true} yes, {@code false} no + */ + public static boolean isHandler() { + return sIsHandler; + } + + /** + * 设置是否处理日志记录 + * @param handler 是否处理日志 + */ + public static void setHandler(final boolean handler) { + AnalysisRecordUtils.sIsHandler = handler; + } + + /** + * 判断是否追加空格 + * @return {@code true} yes, {@code false} no + */ + public static boolean isAppendSpace() { + return sAppendSpace; + } + + /** + * 设置是否追加空格 + * @param appendSpace 是否追加空格 + */ + public static void setAppendSpace(final boolean appendSpace) { + AnalysisRecordUtils.sAppendSpace = appendSpace; + } + + /** + * 获取日志文件名 + * @return 日志文件名 + */ + public static String getLogFolderName() { + return sLogFolderName; + } + + /** + * 设置日志文件夹名 + * @param logFolderName 日志文件夹名 + */ + public static void setLogFolderName(final String logFolderName) { + AnalysisRecordUtils.sLogFolderName = logFolderName; + } + + /** + * 获取日志存储路径 + * @return 日志存储路径 + */ + public static String getLogStoragePath() { + return sLogStoragePath; + } + + /** + * 设置日志存储路径 + * @param logStoragePath 日志存储路径 + */ + public static void setLogStoragePath(final String logStoragePath) { + AnalysisRecordUtils.sLogStoragePath = logStoragePath; + } + + // ============ + // = 内部方法 = + // ============ + + /** + * 最终保存方法 + * @param fileInfo {@link FileInfo} + * @param logs 日志内容数组 + * @return 拼接后的日志内容 + */ + private static String saveLogRecord(final FileInfo fileInfo, final String... logs) { + // 如果不处理, 则直接跳过 + if (!sIsHandler) return "do not process records"; + // 文件信息为 null, 则不处理 + if (fileInfo == null) return "fileInfo is null"; + // 如果文件地址为 null, 则不处理 + if (TextUtils.isEmpty(fileInfo.getFileName())) return "fileName is null"; + // 获取文件名 + String fileName = fileInfo.getFileName(); + // 获取文件提示 + String fileHint = fileInfo.getFileFunction(); + try { + // 获取处理的日志 + String logContent = splitLog(logs); + // 日志保存路径 + String logPath = fileInfo.getLogPath(); + // 获取日志地址 + String logFile = logPath + File.separator + fileName; + // 返回地址 + File file = new File(logFile); + // 判断是否存在 + if (file.exists()) { + appendFile(logFile, logContent); + } else { + // = 首次则保存设备、APP 信息 = + StringBuilder builder = new StringBuilder(); + builder.append(NEW_LINE_STR_X2); + builder.append("【设备信息】"); + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + builder.append(handlerDeviceInfo("failed to get device information")); + builder.append(NEW_LINE_STR); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + + builder.append(NEW_LINE_STR_X2); + builder.append(NEW_LINE_STR_X2); + builder.append("【版本信息】"); + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + builder.append("versionName: " + APP_VERSION_NAME); + builder.append(NEW_LINE_STR); + builder.append("versionCode: " + APP_VERSION_CODE); + builder.append(NEW_LINE_STR); + builder.append("package: " + PACKAGE_NAME); + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + + builder.append(NEW_LINE_STR_X2); + builder.append(NEW_LINE_STR_X2); + builder.append("【文件信息】"); + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + builder.append(fileHint); + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + + builder.append(NEW_LINE_STR_X2); + builder.append(NEW_LINE_STR_X2); + builder.append("【日志内容】"); + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + // 创建文件夹, 并且进行处理 + saveFile(logPath, fileName, builder.toString()); + // 追加内容 + appendFile(logFile, logContent); + } + // 返回打印日志 + return logContent; + } catch (Exception ignore) { + // 捕获异常 + return "catch exception"; + } + } + + /** + * 拼接日志 + * @param logs 日志内容数组 + * @return 拼接后的日志内容 + */ + private static String splitLog(final String... logs) { + // 判断是否追加空格 + boolean isSpace = sAppendSpace; + // = + StringBuilder builder = new StringBuilder(); + // 增加换行 + builder.append(NEW_LINE_STR); + builder.append(NEW_LINE_STR); + // 获取保存时间 + builder.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + // 追加边距 + builder.append(" => "); + // 是否添加空格 ( 第一位不添加空格 ) + boolean isAdd = false; + // 循环追加内容 + for (int i = 0, len = logs.length; i < len; i++) { + if (isSpace && isAdd) { // 判断是否追加空格 + builder.append(" "); + } + // 标记添加空格 ( 第一位不添加空格 ) + isAdd = true; + // 追加保存内容 + builder.append(logs[i]); + } + return builder.toString(); + } + + // ================ + // = 日志保存时间 = + // ================ + + // DEFAULT - 默认天, 在根目录下 + public static final int DEFAULT = 0; + // 小时 + public static final int HH = 1; + // 分钟 + public static final int MM = 2; + // 秒 + public static final int SS = 3; + + @IntDef({DEFAULT, HH, MM, SS}) + @Retention(RetentionPolicy.SOURCE) + public @interface TIME { + } + + /** + * detail: 记录文件信息实体类 + * @author Ttt + */ + public static class FileInfo { + + // 存储路径 + private String storagePath; + + // 文件夹名 + private String folderName; + + // 文件名 如: xxx.txt + private String fileName; + + // 文件记录的功能 + private String fileFunction; + + // 文件记录间隔时间 如: HH + private int fileIntervalTime; + + // 是否处理日志记录 + private boolean handler; + + // ============ + // = 构造函数 = + // ============ + + /** + * 构造函数 + * @param storagePath 存储路径 + * @param folderName 文件夹名 + * @param fileName 文件名 + * @param fileFunction 文件记录的功能 + * @param fileIntervalTime 文件记录间隔时间 + * @param handler 是否处理日志记录 + */ + private FileInfo(final String storagePath, final String folderName, final String fileName, + final String fileFunction, @TIME final int fileIntervalTime, final boolean handler) { + this.storagePath = storagePath; + this.folderName = folderName; + this.fileName = fileName; + this.fileFunction = fileFunction; + this.fileIntervalTime = fileIntervalTime; + this.handler = handler; + } + + // = + + /** + * 获取日志记录分析文件对象 + * @param fileName 文件名 + * @param fileFunction 日志文件记录功能 + * @return {@link FileInfo} + */ + public static FileInfo obtain(final String fileName, final String fileFunction) { + return new FileInfo(null, null, fileName, fileFunction, DEFAULT, true); + } + + /** + * 获取日志记录分析文件对象 + * @param folderName 日志文件名 + * @param fileName 文件名 + * @param fileFunction 日志文件记录功能 + * @return {@link FileInfo} + */ + public static FileInfo obtain(final String folderName, final String fileName, final String fileFunction) { + return new FileInfo(null, folderName, fileName, fileFunction, DEFAULT, true); + } + + /** + * 获取日志记录分析文件对象 + * @param storagePath 存储路径 + * @param folderName 日志文件名 + * @param fileName 文件名 + * @param fileFunction 日志文件记录功能 + * @return {@link FileInfo} + */ + public static FileInfo obtain(final String storagePath, final String folderName, final String fileName, final String fileFunction) { + return new FileInfo(storagePath, folderName, fileName, fileFunction, DEFAULT, true); + } + + // = + + /** + * 获取日志记录分析文件对象 + * @param fileName 文件名 + * @param fileFunction 日志文件记录功能 + * @param fileIntervalTime 日志文件记录间隔时间 + * @return {@link FileInfo} + */ + public static FileInfo obtain(final String fileName, final String fileFunction, @TIME final int fileIntervalTime) { + return new FileInfo(null, null, fileName, fileFunction, fileIntervalTime, true); + } + + /** + * 获取日志记录分析文件对象 + * @param folderName 日志文件名 + * @param fileName 文件名 + * @param fileFunction 日志文件记录功能 + * @param fileIntervalTime 日志文件记录间隔时间 + * @return {@link FileInfo} + */ + public static FileInfo obtain(final String folderName, final String fileName, final String fileFunction, @TIME final int fileIntervalTime) { + return new FileInfo(null, folderName, fileName, fileFunction, fileIntervalTime, true); + } + + /** + * 获取日志记录分析文件对象 + * @param storagePath 存储路径 + * @param folderName 日志文件名 + * @param fileName 文件名 + * @param fileFunction 日志文件记录功能 + * @param fileIntervalTime 日志文件记录间隔时间 + * @return {@link FileInfo} + */ + public static FileInfo obtain(final String storagePath, final String folderName, final String fileName, final String fileFunction, + @TIME final int fileIntervalTime) { + return new FileInfo(storagePath, folderName, fileName, fileFunction, fileIntervalTime, true); + } + + /** + * 获取日志记录分析文件对象 + * @param storagePath 存储路径 + * @param folderName 日志文件名 + * @param fileName 文件名 + * @param fileFunction 日志文件记录功能 + * @param fileIntervalTime 日志文件记录间隔时间 + * @param isHandler 是否处理日志记录 + * @return {@link FileInfo} + */ + public static FileInfo obtain(final String storagePath, final String folderName, final String fileName, final String fileFunction, + @TIME final int fileIntervalTime, final boolean isHandler) { + return new FileInfo(storagePath, folderName, fileName, fileFunction, fileIntervalTime, isHandler); + } + + // ================ + // = get/set 方法 = + // ================ + + /** + * 获取存储路径 + * @return 存储路径 + */ + public String getStoragePath() { + if (TextUtils.isEmpty(storagePath)) { + return storagePath = getLogStoragePath(); + } + return storagePath; + } + + /** + * 获取日志文件名 + * @return 日志文件名 + */ + public String getFileName() { + return fileName; + } + + /** + * 获取日志文件记录功能 + * @return 日志文件记录功能 + */ + public String getFileFunction() { + return fileFunction; + } + + /** + * 获取日志文件记录间隔时间 + * @return 日志文件记录间隔时间 + */ + public int getFileIntervalTime() { + return fileIntervalTime; + } + + /** + * 获取日志文件夹名 + * @return 日志文件夹名 + */ + public String getFolderName() { + if (TextUtils.isEmpty(folderName)) { + return folderName = getLogFolderName(); + } + return folderName; + } + + /** + * 判断是否处理日志记录 + * @return {@code true} yes, {@code false} no + */ + public boolean isHandler() { + return handler; + } + + /** + * 设置是否处理日志记录 + * @param handler 是否处理日志记录 + * @return {@link FileInfo} + */ + public FileInfo setHandler(final boolean handler) { + this.handler = handler; + return this; + } + + // ================ + // = 内部处理方法 = + // ================ + + /** + * 获取日志文件地址 + * @return 日志文件地址 + */ + public String getLogPath() { + // 返回拼接后的路径 + return getFilePathCreateFolder(getStoragePath(), + sLogFolderName + File.separator + getDateNow("yyyy_MM_dd")) + getIntervalTimeFolder(); + } + + /** + * 获取时间间隔所属的文件夹 + * @return 时间间隔所属的文件夹 + */ + public String getIntervalTimeFolder() { + // 文件夹 + String folder = File.separator + getFolderName() + File.separator; + // 获取间隔时间 + int iTime = getFileIntervalTime(); + // 进行判断 + switch (iTime) { + case DEFAULT: + return folder; + case HH: + case MM: + case SS: + // 小时格式 + String hh_Foramt = getDateNow("HH"); + // 判断属于小时格式 + if (iTime == HH) { + // /folder/HH/HH_number/ + // /LogSpace/HH/HH_15/ + return folder + "HH/HH_" + hh_Foramt + File.separator; + } else { + // 分钟格式 + String mm_Foramt = getDateNow("mm"); + // 判断是否属于分钟 + if (iTime == MM) { + // /folder/HH/HH_number/MM/MM_number/ + // /LogSpace/HH/HH_15/MM/MM_55/ + return folder + "HH/HH_" + hh_Foramt + "/MM/MM_" + mm_Foramt + File.separator; + } else { // 属于秒 + // 秒格式 + String ss_Foramt = getDateNow("ss"); + // /folder/HH/HH_number/MM/MM_number/SS_number/ + // /LogSpace/HH/HH_15/MM/MM_55/SS_12/ + return folder + "HH/HH_" + hh_Foramt + "/MM/MM_" + mm_Foramt + "/SS_" + ss_Foramt + File.separator; + } + } + } + // 放到未知目录下 + return "/Unknown/"; + } + } + + // ================ + // = 设备信息处理 = + // ================ + + /** + * 处理设备信息 + * @param errorInfo 错误提示信息, 如获取设备信息失败 + * @return 拼接后的设备信息字符串 + */ + private static String handlerDeviceInfo(final String errorInfo) { + // 如果不为 null, 则直接返回之前的信息 + if (!TextUtils.isEmpty(DEVICE_INFO_STR)) { + return DEVICE_INFO_STR; + } + // 设备信息 + String deviceInfo = handlerDeviceInfo(DEVICE_INFO_MAPS, null); + // 如果为 null + if (deviceInfo == null) { + return errorInfo; + } + // 保存设备信息 + DEVICE_INFO_STR = deviceInfo; + // 返回设备信息 + return DEVICE_INFO_STR; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = SDCardUtils = + // =============== + + /** + * 判断内置 SDCard 是否正常挂载 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSDCardEnable() { + return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); + } + + /** + * 获取 APP Cache 文件夹地址 + * @return APP Cache 文件夹地址 + */ + private static String getDiskCacheDir() { + String cachePath; + if (isSDCardEnable()) { // 判断 SDCard 是否挂载 + cachePath = DevUtils.getContext().getExternalCacheDir().getPath(); + } else { + cachePath = DevUtils.getContext().getCacheDir().getPath(); + } + // 防止不存在目录文件, 自动创建 + createFolder(cachePath); + // 返回文件存储地址 + return cachePath; + } + + // ============= + // = FileUtils = + // ============= + + /** + * 追加文件 ( 使用 FileWriter) + * @param filePath 文件路径 + * @param content 追加内容 + */ + private static void appendFile(final String filePath, final String content) { + if (filePath == null || content == null) return; + File file = new File(filePath); + // 如果文件不存在, 则跳过 + if (!file.exists()) return; + FileWriter writer = null; + try { + // 打开一个写文件器, 构造函数中的第二个参数 true 表示以追加形式写文件 + writer = new FileWriter(file, true); + writer.write(content); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "appendFile"); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + } + } + } + } + + /** + * 保存文件 + * @param filePath 保存路径 + * @param fileName 文件名. 后缀 + * @param content 保存内容 + * @return {@code true} success, {@code false} fail + */ + private static boolean saveFile(final String filePath, final String fileName, final String content) { + if (filePath != null && fileName != null && content != null) { + try { + // 防止文件没创建 + createFolder(filePath); + // 保存路径 + File file = new File(filePath, fileName); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + fos.write(content.getBytes()); + fos.close(); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 获取文件绝对路径 + * @param file 文件 + * @return 文件绝对路径 + */ + private static String getAbsolutePath(final File file) { + return file != null ? file.getAbsolutePath() : null; + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @param fileName 文件名 + * @return 文件 {@link File} + */ + private static File getFile(final String filePath, final String fileName) { + return (filePath != null && fileName != null) ? new File(filePath, fileName) : null; + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 获取路径, 并且进行创建目录 + * @param filePath 保存目录 + * @param fileName 文件名 + * @return 文件 {@link File} + */ + private static String getFilePathCreateFolder(final String filePath, final String fileName) { + // 防止不存在目录文件, 自动创建 + createFolder(filePath); + // 返回处理过后的 File + File file = getFile(filePath, fileName); + // 返回文件路径 + return getAbsolutePath(file); + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param dirPath 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + private static boolean createFolder(final String dirPath) { + return createFolder(getFileByPath(dirPath)); + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param file 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + private static boolean createFolder(final File file) { + if (file != null) { + try { + // 当这个文件夹不存在的时候则创建文件夹 + if (!file.exists()) { + // 允许创建多级目录 + return file.mkdirs(); + // 这个无法创建多级目录 + // rootFile.mkdir(); + } + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createFolder"); + } + } + return false; + } + + // ================== + // = ThrowableUtils = + // ================== + + /** + * 获取异常栈信息 + * @param throwable 异常 + * @param errorInfo 获取失败返回字符串 + * @return 异常栈信息字符串 + */ + private static String getThrowableStackTrace(final Throwable throwable, final String errorInfo) { + if (throwable != null) { + PrintWriter printWriter = null; + try { + Writer writer = new StringWriter(); + printWriter = new PrintWriter(writer); + throwable.printStackTrace(printWriter); + return writer.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getThrowableStackTrace"); + return e.toString(); + } finally { + if (printWriter != null) { + try { + printWriter.close(); + } catch (Exception e) { + } + } + } + } + return errorInfo; + } + + // ============= + // = DateUtils = + // ============= + + /** + * 获取当前日期的字符串 + * @param format 日期格式, 如: yyyy-MM-dd HH:mm:ss + * @return 当前日期指定格式字符串 + */ + private static String getDateNow(final String format) { + if (format == null) return null; + try { + return new SimpleDateFormat(format).format(Calendar.getInstance().getTime()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDateNow"); + } + return null; + } + + // =============== + // = DeviceUtils = + // =============== + + /** + * 获取设备信息 + * @param deviceInfoMap 设备信息 Map + */ + private static void getDeviceInfo(final Map deviceInfoMap) { + // 获取设备信息类的所有申明的字段, 即包括 public、private 和 proteced, 但是不包括父类的申明字段 + Field[] fields = Build.class.getDeclaredFields(); + // 遍历字段 + for (Field field : fields) { + try { + // 取消 Java 的权限控制检查 + field.setAccessible(true); + // 转换当前设备支持的 ABI - CPU 指令集 + if (field.getName().toLowerCase().startsWith("SUPPORTED".toLowerCase())) { + try { + Object object = field.get(null); + // 判断是否数组 + if (object instanceof String[]) { + if (object != null) { + // 获取类型对应字段的数据, 并保存支持的指令集 [arm64-v8a, armeabi-v7a, armeabi] + deviceInfoMap.put(field.getName(), Arrays.toString((String[]) object)); + } + continue; + } + } catch (Exception e) { + } + } + // 获取类型对应字段的数据, 并保存 + deviceInfoMap.put(field.getName(), field.get(null).toString()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); + } + } + } + + /** + * 处理设备信息 + * @param deviceInfoMap 设备信息 Map + * @param errorInfo 错误提示信息, 如获取设备信息失败 + * @return 拼接后的设备信息字符串 + */ + private static String handlerDeviceInfo(final Map deviceInfoMap, final String errorInfo) { + try { + // 初始化 Builder, 拼接字符串 + StringBuilder builder = new StringBuilder(); + // 获取设备信息 + Iterator> mapIter = deviceInfoMap.entrySet().iterator(); + // 遍历设备信息 + while (mapIter.hasNext()) { + // 获取对应的 key - value + Map.Entry rnEntry = mapIter.next(); + String rnKey = rnEntry.getKey(); // key + String rnValue = rnEntry.getValue(); // value + // 保存设备信息 + builder.append(rnKey); + builder.append(" = "); + builder.append(rnValue); + builder.append(NEW_LINE_STR); + } + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "handlerDeviceInfo"); + } + return errorInfo; + } + + // ================= + // = ManifestUtils = + // ================= + + /** + * 获取 APP 版本信息 + * @return 0 = versionName, 1 = versionCode + */ + private static String[] getAppVersion() { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_SIGNATURES); + if (packageInfo != null) { + String versionName = packageInfo.versionName == null ? "null" : packageInfo.versionName; + String versionCode = packageInfo.versionCode + ""; + return new String[]{versionName, versionCode}; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppVersion"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/AppCommonUtils.java b/lib/DevApp/src/main/java/dev/utils/app/AppCommonUtils.java new file mode 100644 index 0000000000..44abf4ccb7 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/AppCommonUtils.java @@ -0,0 +1,310 @@ +package dev.utils.app; + +import android.os.Build; +import android.support.annotation.RequiresPermission; +import android.support.annotation.StringRes; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: APP 通用工具类 + * @author Ttt + *
+ *     所需权限
+ *     
+ * 
+ */ +public final class AppCommonUtils { + + private AppCommonUtils() { + } + + // 日志 TAG + private static final String TAG = AppCommonUtils.class.getSimpleName(); + + /** + * 获取设备唯一 UUID + * @return 设备唯一 UUID + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getUUID() { + return PhoneUtils.getUUID(); + } + + /** + * 获取 R.string 资源的格式化字符串 + * @param resId R.string.id + * @param objs 格式化参数 + * @return 格式化后的字符串 + */ + public static String getFormatRes(@StringRes final int resId, final Object... objs) { + return getFormatRes(false, resId, objs); + } + + /** + * 获取 R.string 资源的格式化字符串 + * @param errorMsg 是否设置异常信息 + * @param resId R.string.id + * @param objs 格式化参数 + * @return 格式化后的字符串 + */ + public static String getFormatRes(final boolean errorMsg, @StringRes final int resId, final Object... objs) { + try { + if (objs != null && objs.length != 0) { + return DevUtils.getContext().getString(resId, objs); + } else { + return DevUtils.getContext().getString(resId); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getFormatRes"); + if (errorMsg && e != null) { + return e.getMessage(); + } + } + return null; + } + + // ================ + // = 版本判断处理 = + // ================ + + /** + * 获取 SDK 版本 + * @return SDK 版本 + */ + public static int getSDKVersion() { + return Build.VERSION.SDK_INT; + } + + // = + + /** + * 是否在 2.2 版本及以上 + * @return 是否在 2.2 版本及以上 + */ + public static boolean isFroyo() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO; + } + + /** + * 是否在 2.3 版本及以上 + * @return 是否在 2.3 版本及以上 + */ + public static boolean isGingerbread() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; + } + + /** + * 是否在 2.3.3 版本及以上 + * @return 是否在 2.3.3 版本及以上 + */ + public static boolean isGingerbreadMR1() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1; + } + + /** + * 是否在 3.0 版本及以上 + * @return 是否在 3.0 版本及以上 + */ + public static boolean isHoneycomb() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + } + + /** + * 是否在 3.1 版本及以上 + * @return 是否在 3.1 版本及以上 + */ + public static boolean isHoneycombMR1() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1; + } + + /** + * 是否在 4.0 版本及以上 + * @return 是否在 4.0 版本及以上 + */ + public static boolean isIceCreamSandwich() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; + } + + /** + * 是否在 4.0.3 版本及以上 + * @return 是否在 4.0.3 版本及以上 + */ + public static boolean isIceCreamSandwichMR1() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1; + } + + /** + * 是否在 4.1 版本及以上 + * @return 是否在 4.1 版本及以上 + */ + public static boolean isJellyBean() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; + } + + /** + * 是否在 4.2 版本及以上 + * @return 是否在 4.2 版本及以上 + */ + public static boolean isJellyBeanMR1() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1; + } + + /** + * 是否在 4.3 版本及以上 + * @return 是否在 4.3 版本及以上 + */ + public static boolean isJellyBeanMR2() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2; + } + + /** + * 是否在 4.4.2 版本及以上 + * @return 是否在 4.4.2 版本及以上 + */ + public static boolean isKitkat() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + } + + /** + * 是否在 5.0.1 版本及以上 + * @return 是否在 5.0.1 版本及以上 + */ + public static boolean isLollipop() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + } + + /** + * 是否在 6.0 版本及以上 + * @return 是否在 6.0 版本及以上 + */ + public static boolean isM() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; + } + + /** + * 是否在 7.0 版本及以上 + * @return 是否在 7.0 版本及以上 + */ + public static boolean isN() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; + } + + /** + * 是否在 7.1.1 版本及以上 + * @return 是否在 7.1.1 版本及以上 + */ + public static boolean isN_MR1() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1; + } + + /** + * 是否在 8.0 版本及以上 + * @return 是否在 8.0 版本及以上 + */ + public static boolean isO() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + } + + /** + * 是否在 8.1 版本及以上 + * @return 是否在 8.1 版本及以上 + */ + public static boolean isO_MR1() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1; + } + + /** + * 是否在 9.0 版本及以上 + * @return 是否在 9.0 版本及以上 + */ + public static boolean isP() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; + } + + /** + * 是否在 10.0 版本及以上 + * @return 是否在 10.0 版本及以上 + */ + public static boolean isQ() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q; + } + + /** + * 转换 SDK 版本 convertSDKVersion(14) = Android 4.0.0-2 + * @return SDK 版本 + */ + public static String convertSDKVersion() { + return convertSDKVersion(Build.VERSION.SDK_INT); + } + + /** + * 转换 SDK 版本 convertSDKVersion(14) = Android 4.0.0-2 + * @param sdkVersion SDK 版本 + * @return SDK 版本 + */ + public static String convertSDKVersion(final int sdkVersion) { + switch (sdkVersion) { + case 1: + return "Android 1.0"; + case 2: + return "Android 1.1"; + case 3: + return "Android 1.5"; + case 4: + return "Android 1.6"; + case 5: + return "Android 2.0"; + case 6: + return "Android 2.0.1"; + case 7: + return "Android 2.1.x"; + case 8: + return "Android 2.2.x"; + case 9: + return "Android 2.3.0-2"; + case 10: + return "Android 2.3.3-4"; + case 11: + return "Android 3.0.x"; + case 12: + return "Android 3.1.x"; + case 13: + return "Android 3.2"; + case 14: + return "Android 4.0.0-2"; + case 15: + return "Android 4.0.3-4"; + case 16: + return "Android 4.1.x"; + case 17: + return "Android 4.2.x"; + case 18: + return "Android 4.3"; + case 19: + return "Android 4.4"; + case 20: + return "Android 4.4W"; + case 21: + return "Android 5.0"; + case 22: + return "Android 5.1"; + case 23: + return "Android 6.0"; + case 24: + return "Android 7.0"; + case 25: + return "Android 7.1.1"; + case 26: + return "Android 8.0"; + case 27: + return "Android 8.1"; + case 28: + return "Android 9.0"; + case 29: + return "Android 10.0"; + } + return "unknown"; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/AppUtils.java b/lib/DevApp/src/main/java/dev/utils/app/AppUtils.java new file mode 100644 index 0000000000..7c563a61c1 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/AppUtils.java @@ -0,0 +1,1110 @@ +package dev.utils.app; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.Signature; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.provider.Settings; +import android.support.annotation.RequiresPermission; +import android.support.v4.content.FileProvider; +import android.view.WindowManager; + +import java.io.File; +import java.security.MessageDigest; +import java.util.List; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: APP (Android) 工具类 + * @author Ttt + *
+ *     MimeType
+ *     @see 
+ *     

+ * 所需权限 + * + *
+ */ +public final class AppUtils { + + private AppUtils() { + } + + // 日志 TAG + private static final String TAG = AppUtils.class.getSimpleName(); + + /** + * 获取 WindowManager + * @return {@link WindowManager} + */ + public static WindowManager getWindowManager() { + try { + return (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWindowManager"); + } + return null; + } + + /** + * 获取 SystemService + * @param name 服务名 + * @param 泛型 + * @return SystemService Object + */ + public static T getSystemService(final String name) { + if (isSpace(name)) return null; + try { + return (T) DevUtils.getContext().getSystemService(name); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSystemService"); + } + return null; + } + + /** + * 获取 PackageManager + * @return {@link PackageManager} + */ + public static PackageManager getPackageManager() { + try { + return DevUtils.getContext().getPackageManager(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPackageManager"); + } + return null; + } + + // ============ + // = APP 相关 = + // ============ + + /** + * 获取 APP 包名 + * @return APP 包名 + */ + public static String getAppPackageName() { + return DevUtils.getContext().getPackageName(); + } + + /** + * 获取 APP 图标 + * @return {@link Drawable} + */ + public static Drawable getAppIcon() { + return getAppIcon(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP 图标 + * @param packageName 应用包名 + * @return {@link Drawable} + */ + public static Drawable getAppIcon(final String packageName) { + if (isSpace(packageName)) return null; + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); + return packageInfo == null ? null : packageInfo.applicationInfo.loadIcon(packageManager); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppIcon"); + return null; + } + } + + /** + * 获取 APP 应用名 + * @return APP 应用名 + */ + public static String getAppName() { + return getAppName(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP 应用名 + * @param packageName 应用包名 + * @return APP 应用名 + */ + public static String getAppName(final String packageName) { + if (isSpace(packageName)) return null; + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); + return packageInfo == null ? null : packageInfo.applicationInfo.loadLabel(packageManager).toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppName"); + return null; + } + } + + /** + * 获取 APP versionName + * @return APP versionName + */ + public static String getAppVersionName() { + return getAppVersionName(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP versionName + * @param packageName 应用包名 + * @return APP versionName + */ + public static String getAppVersionName(final String packageName) { + if (isSpace(packageName)) return null; + try { + PackageInfo packageInfo = DevUtils.getContext().getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + return packageInfo == null ? null : packageInfo.versionName; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppVersionName"); + return null; + } + } + + /** + * 获取 APP versionCode + * @return APP versionCode + */ + public static int getAppVersionCode() { + return getAppVersionCode(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP versionCode + * @param packageName 应用包名 + * @return APP versionCode + */ + public static int getAppVersionCode(final String packageName) { + if (isSpace(packageName)) return -1; + try { + PackageInfo packageInfo = DevUtils.getContext().getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + return packageInfo == null ? -1 : packageInfo.versionCode; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppVersionCode"); + return -1; + } + } + + /** + * 获取 APP 安装包路径 /data/data/packageName/.apk + * @return APP 安装包路径 + */ + public static String getAppPath() { + return getAppPath(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP 安装包路径 /data/data/packageName/.apk + * @param packageName 应用包名 + * @return APP 安装包路径 + */ + public static String getAppPath(final String packageName) { + if (isSpace(packageName)) return null; + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); + return packageInfo == null ? null : packageInfo.applicationInfo.sourceDir; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppPath"); + return null; + } + } + + // = + + /** + * 获取 APP Signature + * @return {@link Signature} 数组 + */ + public static Signature[] getAppSignature() { + return getAppSignature(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP Signature + * @param packageName 应用包名 + * @return {@link Signature} 数组 + */ + public static Signature[] getAppSignature(final String packageName) { + if (isSpace(packageName)) return null; + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + return packageInfo == null ? null : packageInfo.signatures; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppSignature"); + return null; + } + } + + // = + + /** + * 获取 APP 签名 MD5 值 + * @return APP 签名 MD5 值 + */ + public static String getAppSignatureMD5() { + return getAppSignatureMD5(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP 签名 MD5 值 + * @param packageName 应用包名 + * @return APP 签名 MD5 值 + */ + public static String getAppSignatureMD5(final String packageName) { + return getAppSignatureHash(packageName, "MD5"); + } + + /** + * 获取 APP 签名 SHA1 值 + * @return APP 签名 SHA1 值 + */ + public static String getAppSignatureSHA1() { + return getAppSignatureSHA1(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP 签名 SHA1 值 + * @param packageName 应用包名 + * @return APP 签名 SHA1 值 + */ + public static String getAppSignatureSHA1(final String packageName) { + return getAppSignatureHash(packageName, "SHA1"); + } + + /** + * 获取 APP 签名 SHA256 值 + * @return APP 签名 SHA256 值 + */ + public static String getAppSignatureSHA256() { + return getAppSignatureSHA256(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP 签名 SHA256 值 + * @param packageName 应用包名 + * @return APP 签名 SHA256 值 + */ + public static String getAppSignatureSHA256(final String packageName) { + return getAppSignatureHash(packageName, "SHA256"); + } + + /** + * 获取应用签名 Hash 值 + * @param packageName 应用包名 + * @param algorithm 算法 + * @return 对应算法处理后的签名信息 + */ + private static String getAppSignatureHash(final String packageName, final String algorithm) { + if (isSpace(packageName)) return null; + try { + Signature[] signature = getAppSignature(packageName); + if (signature == null || signature.length == 0) return null; + return colonSplit(toHexString(hashTemplate(signature[0].toByteArray(), algorithm), HEX_DIGITS)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppSignatureHash - packageName: " + packageName + ", algorithm: " + algorithm); + return null; + } + } + + // = + + /** + * 判断 APP 是否 debug 模式 + * @return {@code true} yes, {@code false} no + */ + public static boolean isAppDebug() { + return isAppDebug(DevUtils.getContext().getPackageName()); + } + + /** + * 判断 APP 是否 debug 模式 + * @param packageName 应用包名 + * @return {@code true} yes, {@code false} no + */ + public static boolean isAppDebug(final String packageName) { + if (isSpace(packageName)) return false; + try { + ApplicationInfo appInfo = DevUtils.getContext().getPackageManager().getApplicationInfo(packageName, 0); + return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isAppDebug"); + return false; + } + } + + /** + * 判断 APP 是否 release 模式 + * @return {@code true} yes, {@code false} no + */ + public static boolean isAppRelease() { + return isAppRelease(DevUtils.getContext().getPackageName()); + } + + /** + * 判断 APP 是否 release 模式 + * @param packageName 应用包名 + * @return {@code true} yes, {@code false} no + */ + public static boolean isAppRelease(final String packageName) { + if (isSpace(packageName)) return false; + try { + ApplicationInfo appInfo = DevUtils.getContext().getPackageManager().getApplicationInfo(packageName, 0); + return !(appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isAppRelease"); + return false; + } + } + + // = + + /** + * 判断 APP 是否系统 app + * @return {@code true} yes, {@code false} no + */ + public static boolean isAppSystem() { + return isAppSystem(DevUtils.getContext().getPackageName()); + } + + /** + * 判断 APP 是否系统 app + * @param packageName 应用包名 + * @return {@code true} yes, {@code false} no + */ + public static boolean isAppSystem(final String packageName) { + if (isSpace(packageName)) return false; + try { + ApplicationInfo appInfo = DevUtils.getContext().getPackageManager().getApplicationInfo(packageName, 0); + return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isAppSystem"); + return false; + } + } + + /** + * 判断 APP 是否在前台 + * @return {@code true} yes, {@code false} no + */ + @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) + public static boolean isAppForeground() { + return isAppForeground(DevUtils.getContext().getPackageName()); + } + + /** + * 判断 APP 是否在前台 + * @param packageName 应用包名 + * @return {@code true} yes, {@code false} no + */ + @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) + public static boolean isAppForeground(final String packageName) { + if (isSpace(packageName)) return false; + try { + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + if (activityManager == null) return false; + List lists = activityManager.getRunningAppProcesses(); + if (lists != null && lists.size() > 0) { + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { + return appProcess.processName.equals(packageName); + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isAppForeground"); + } + return false; + } + + // = + + /** + * 判断是否安装了 APP + * @param action Action + * @param category Category + * @return {@code true} yes, {@code false} no + */ + public static boolean isInstalledApp(final String action, final String category) { + try { + Intent intent = new Intent(action); + intent.addCategory(category); + ResolveInfo resolveinfo = DevUtils.getContext().getPackageManager().resolveActivity(intent, 0); + return resolveinfo != null; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isInstalledApp"); + return false; + } + } + + /** + * 判断是否安装了 APP + * @param packageName 应用包名 + * @return {@code true} yes, {@code false} no + */ + @SuppressWarnings("unused") + public static boolean isInstalledApp(final String packageName) { + if (isSpace(packageName)) return false; + try { + ApplicationInfo appInfo = DevUtils.getContext().getPackageManager().getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); + return true; + } catch (Exception e) { // 未安装, 则会抛出异常 + LogPrintUtils.eTag(TAG, e, "isInstalledApp"); + return false; + } + } + + /** + * 判断是否安装了 APP + * @param packageName 应用包名 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInstalledApp2(final String packageName) { + return !isSpace(packageName) && IntentUtils.getLaunchAppIntent(packageName) != null; + } + + // ============== + // = 安装、卸载 = + // ============== + + /** + * 安装 APP( 支持 8.0) 的意图 + * @param filePath 文件路径 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean installApp(final String filePath) { + return installApp(getFileByPath(filePath)); + } + + /** + * 安装 APP( 支持 8.0) 的意图 + * @param file 文件 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean installApp(final File file) { + if (!isFileExists(file)) return false; + try { + DevUtils.getContext().startActivity(IntentUtils.getInstallAppIntent(file, true)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "installApp"); + return false; + } + return true; + } + + /** + * 安装 APP( 支持 8.0) 的意图 + * @param activity {@link Activity} + * @param filePath 文件路径 + * @param requestCode 请求 code + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean installApp(final Activity activity, final String filePath, final int requestCode) { + return installApp(activity, getFileByPath(filePath), requestCode); + } + + /** + * 安装 APP( 支持 8.0) 的意图 + * @param activity {@link Activity} + * @param file 文件 + * @param requestCode 请求 code + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean installApp(final Activity activity, final File file, final int requestCode) { + if (!isFileExists(file)) return false; + try { + activity.startActivityForResult(IntentUtils.getInstallAppIntent(file), requestCode); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "installApp"); + return false; + } + return true; + } + + // = + + /** + * 静默安装应用 + * @param filePath 文件路径 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean installAppSilent(final String filePath) { + return installAppSilent(filePath, null); + } + + /** + * 静默安装应用 + * @param file 文件 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean installAppSilent(final File file) { + return installAppSilent(file, null); + } + + /** + * 静默安装应用 + * @param filePath 文件路径 + * @param params 安装参数 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean installAppSilent(final String filePath, final String params) { + return installAppSilent(getFileByPath(filePath), params, isDeviceRooted()); + } + + /** + * 静默安装应用 + * @param file 文件 + * @param params 安装参数 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean installAppSilent(final File file, final String params) { + return installAppSilent(file, params, isDeviceRooted()); + } + + /** + * 静默安装应用 + * @param file 文件 + * @param params 安装参数 + * @param isRooted 是否 root + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean installAppSilent(final File file, final String params, final boolean isRooted) { + if (!isFileExists(file)) return false; + String filePath = '"' + file.getAbsolutePath() + '"'; + String command = "LD_LIBRARY_PATH=/vendor/lib*:/system/lib* pm install " + (params == null ? "" : params + " ") + filePath; + ShellUtils.CommandResult result = ShellUtils.execCmd(command, isRooted); + return result.isSuccess4("success"); + } + + // = + + /** + * 卸载应用 + * @param packageName 应用包名 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean uninstallApp(final String packageName) { + if (isSpace(packageName)) return false; + try { + DevUtils.getContext().startActivity(IntentUtils.getUninstallAppIntent(packageName, true)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "uninstallApp"); + return false; + } + return true; + } + + /** + * 卸载应用 + * @param activity {@link Activity} + * @param packageName 应用包名 + * @param requestCode 请求 code + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean uninstallApp(final Activity activity, final String packageName, final int requestCode) { + if (isSpace(packageName)) return false; + try { + activity.startActivityForResult(IntentUtils.getUninstallAppIntent(packageName), requestCode); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "uninstallApp"); + return false; + } + return true; + } + + /** + * 静默卸载应用 + * @param packageName 应用包名 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean uninstallAppSilent(final String packageName) { + return uninstallAppSilent(packageName, false, isDeviceRooted()); + } + + /** + * 静默卸载应用 + * @param packageName 应用包名 + * @param isKeepData true 表示卸载应用但保留数据和缓存目录 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean uninstallAppSilent(final String packageName, final boolean isKeepData) { + return uninstallAppSilent(packageName, isKeepData, isDeviceRooted()); + } + + /** + * 静默卸载应用 + * @param packageName 应用包名 + * @param isKeepData true 表示卸载应用但保留数据和缓存目录 + * @param isRooted 是否 root + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean uninstallAppSilent(final String packageName, final boolean isKeepData, final boolean isRooted) { + if (isSpace(packageName)) return false; + String command = "LD_LIBRARY_PATH=/vendor/lib*:/system/lib* pm uninstall " + (isKeepData ? "-k " : "") + packageName; + ShellUtils.CommandResult result = ShellUtils.execCmd(command, isRooted); + return result.isSuccess4("success"); + } + + // ============ + // = 操作相关 = + // ============ + + /** + * 打开 APP + * @param packageName 应用包名 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean launchApp(final String packageName) { + if (isSpace(packageName)) return false; + try { + DevUtils.getContext().startActivity(IntentUtils.getLaunchAppIntent(packageName, true)); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "launchApp"); + } + return false; + } + + /** + * 打开 APP + * @param activity {@link Activity} + * @param packageName 应用包名 + * @param requestCode 请求 code + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean launchApp(final Activity activity, final String packageName, final int requestCode) { + if (isSpace(packageName)) return false; + try { + activity.startActivityForResult(IntentUtils.getLaunchAppIntent(packageName), requestCode); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "launchApp"); + } + return false; + } + + // = + + /** + * 跳转到 APP 设置详情页面 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean launchAppDetailsSettings() { + return launchAppDetailsSettings(DevUtils.getContext().getPackageName()); + } + + /** + * 跳转到 APP 设置详情页面 + * @param packageName 应用包名 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean launchAppDetailsSettings(final String packageName) { + if (isSpace(packageName)) return false; + try { + DevUtils.getContext().startActivity(IntentUtils.getLaunchAppDetailsSettingsIntent(packageName, true)); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "launchAppDetailsSettings"); + } + return false; + } + + /** + * 跳转到 APP 应用商城详情页面 + * @param marketPkg 应用商店包名, 如果为 "" 则由系统弹出应用商店列表供用户选择, 否则调转到目标市场的应用详情界面, 某些应用商店可能会失败 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean launchAppDetails(final String marketPkg) { + return launchAppDetails(DevUtils.getContext().getPackageName(), marketPkg); + } + + /** + * 跳转到 APP 应用商城详情页面 + * @param packageName 应用包名 + * @param marketPkg 应用商店包名, 如果为 "" 则由系统弹出应用商店列表供用户选择, 否则调转到目标市场的应用详情界面, 某些应用商店可能会失败 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean launchAppDetails(final String packageName, final String marketPkg) { + if (isSpace(packageName)) return false; + try { + DevUtils.getContext().startActivity(IntentUtils.getLaunchAppDetailIntent(packageName, marketPkg, true)); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "launchAppDetails"); + } + return false; + } + + // ============ + // = 其他功能 = + // ============ + + /** + * 打开文件 + * @param filePath 文件路径 + * @param dataType 数据类型 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean openFile(final String filePath, final String dataType) { + return openFile(getFileByPath(filePath), dataType); + } + + /** + * 打开文件 + * @param file 文件 + * @param dataType 数据类型 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean openFile(final File file, final String dataType) { + if (!isFileExists(file)) return false; + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 临时授权 ( 必须 ) + intent.setDataAndType(getUriForFile(file, DevUtils.getAuthority()), dataType); + DevUtils.getContext().startActivity(intent); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openFile"); + } + return false; + } + + // = + + /** + * 打开文件 - 指定应用 + * @param filePath 文件路径 + * @param packageName 应用包名 + * @param className Activity.class.getCanonicalName() + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean openFileByApp(final String filePath, final String packageName, final String className) { + return openFileByApp(getFileByPath(filePath), packageName, className); + } + + /** + * 打开文件 - 指定应用 + * @param file 文件 + * @param packageName 应用包名 + * @param className Activity.class.getCanonicalName() + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean openFileByApp(final File file, final String packageName, final String className) { + if (!isFileExists(file)) return false; + try { + Intent intent = new Intent(); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setData(Uri.fromFile(file)); + intent.setClassName(packageName, className); + DevUtils.getContext().startActivity(intent); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openFile"); + } + return false; + } + + // = + + /** + * 打开 PDF 文件 + * @param filePath 文件路径 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean openPDFFile(final String filePath) { + return openPDFFile(getFileByPath(filePath)); + } + + /** + * 打开 PDF 文件 + * @param file 文件 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean openPDFFile(final File file) { + return openFile(file, "application/pdf"); + } + + // = + + /** + * 打开 Word 文件 + * @param filePath 文件路径 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean openWordFile(final String filePath) { + return openWordFile(getFileByPath(filePath)); + } + + /** + * 打开 Word 文件 + * @param file 文件 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean openWordFile(final File file) { + return openFile(file, "application/msword"); + } + + // = + + /** + * 调用 WPS 打开 office 文档 + * @param filePath 文件路径 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean openOfficeByWPS(final String filePath) { + return openOfficeByWPS(getFileByPath(filePath)); + } + + /** + * 调用 WPS 打开 office 文档 + * @param file 文件 + * @return {@code true} operation successfully, {@code false} operation failed + */ + public static boolean openOfficeByWPS(final File file) { + String wpsPackage = "cn.wps.moffice_eng"; // 普通版与英文版一样 + // String wpsActivity = "cn.wps.moffice.documentmanager.PreStartActivity"; + String wpsActivity2 = "cn.wps.moffice.documentmanager.PreStartActivity2"; + // 打开文件 + return openFileByApp(file, wpsPackage, wpsActivity2); + } + + // ============ + // = 系统页面 = + // ============ + + /** + * 跳转到系统设置页面 + */ + public static void startSysSetting() { + try { + DevUtils.getContext().startActivity(IntentUtils.getIntent(new Intent(Settings.ACTION_SETTINGS), true)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startSysSetting"); + } + } + + /** + * 跳转到系统设置页面 + * @param activity {@link Activity} + * @param requestCode 请求 code + */ + public static void startSysSetting(final Activity activity, final int requestCode) { + try { + Intent intent = new Intent(Settings.ACTION_SETTINGS); + activity.startActivityForResult(intent, requestCode); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startSysSetting"); + } + } + + /** + * 打开网络设置界面 + */ + public static void openWirelessSettings() { + try { + DevUtils.getContext().startActivity(IntentUtils.getIntent(new Intent(Settings.ACTION_WIRELESS_SETTINGS), true)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openWirelessSettings"); + } + } + + /** + * 打开网络设置界面 + * @param activity {@link Activity} + * @param requestCode 请求 code + */ + public static void openWirelessSettings(final Activity activity, final int requestCode) { + try { + Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS); + activity.startActivityForResult(intent, requestCode); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openWirelessSettings"); + } + } + + /** + * 打开 GPS 设置界面 + */ + public static void openGpsSettings() { + try { + DevUtils.getContext().startActivity(IntentUtils.getIntent(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS), true)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openGpsSettings"); + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = FileUtils = + // ============= + + /** + * 检查是否存在某个文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + private static boolean isFileExists(final File file) { + return file != null && file.exists(); + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + * 冒号分割处理 + * @param str 待处理字符串 + * @return 冒号分割后的字符串 + */ + private static String colonSplit(final String str) { + if (!isEmpty(str)) { + return str.replaceAll("(?<=[0-9A-F]{2})[0-9A-F]{2}", ":$0"); + } + return str; + } + + // =============== + // = DeviceUtils = + // =============== + + /** + * 判断设备是否 root + * @return {@code true} yes, {@code false} no + */ + private static boolean isDeviceRooted() { + String su = "su"; + String[] locations = {"/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/", + "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/"}; + for (String location : locations) { + if (new File(location + su).exists()) { + return true; + } + } + return false; + } + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // ================ + // = EncryptUtils = + // ================ + + /** + * Hash 加密模版方法 + * @param data 待加密数据 + * @param algorithm 算法 + * @return 指定加密算法加密后的数据 + */ + private static byte[] hashTemplate(final byte[] data, final String algorithm) { + if (data == null || data.length == 0) return null; + try { + MessageDigest digest = MessageDigest.getInstance(algorithm); + digest.update(data); + return digest.digest(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "hashTemplate"); + return null; + } + } + + // ============ + // = UriUtils = + // ============ + + /** + * 获取文件 Uri + * @param file 文件 + * @param authority android:authorities + * @return 指定文件 {@link Uri} + */ + private static Uri getUriForFile(final File file, final String authority) { + if (file == null || authority == null) return null; + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return FileProvider.getUriForFile(DevUtils.getContext(), authority, file); + } else { + return Uri.fromFile(file); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getUriForFile"); + return null; + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/AudioManagerUtils.java b/lib/DevApp/src/main/java/dev/utils/app/AudioManagerUtils.java new file mode 100644 index 0000000000..a158237342 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/AudioManagerUtils.java @@ -0,0 +1,724 @@ +package dev.utils.app; + +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.media.AudioManager; +import android.os.Build; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 音频管理工具类 + * @author Ttt + *
+ *     AudioManager 的作用: 调整音量和控制响铃模式
+ *     @see 
+ *     

+ * 声音分类 + * STREAM_VOICE_CALL: 通话声音 + * STREAM_SYSTEM: 系统声音, 包括按键声音等 + * STREAM_RING: 来电响铃 + * STREAM_MUSIC: 媒体声音 ( 包括音乐、视频、游戏声音 ) + * STREAM_ALARM: 闹钟声音 + * STREAM_NOTIFICATION: 通知声音 + *

+ * 声音模式分类 + * RINGER_MODE_NORMAL: 正常模式 + * 所有声音都正常, 包括系统声音、来电响铃、媒体声音、闹钟、通知声音都有 + * RINGER_MODE_SILENT: 静音模式 + * 该模式下, 来电响铃、通知、系统声音和震动都没有, 闹钟、通话声音保持, 大部分手机媒体声音依然有 + * 但是小米和少部分 oppo 手机在设置静音的同时会将媒体声音自动调整为 0, 此时没有媒体声音 + * RINGER_MODE_VIBRATE: 震动模式 + * 该模式下, 来电、通知保持震动没有声音, 但是媒体、闹钟依然有声音, 不过小米手机只有正常模式和静音模式, 没有震动模式 + *

+ * 所需权限 + * + *
+ */ +public final class AudioManagerUtils { + + private AudioManagerUtils() { + } + + // 日志 TAG + private static final String TAG = AudioManagerUtils.class.getSimpleName(); + + /** + * 获取 AudioManager + * @return {@link AudioManager} + */ + public static AudioManager getAudioManager() { + try { + return (AudioManager) DevUtils.getContext().getSystemService(Context.AUDIO_SERVICE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAudioManager"); + } + return null; + } + + // ============ + // = 音量大小 = + // ============ + + /** + * 获取指定声音流最大音量大小 + * @param streamType 流类型 + * @return 最大音量大小 + */ + public static int getStreamMaxVolume(final int streamType) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.getStreamMaxVolume(streamType); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getStreamMaxVolume"); + } + } + return 0; + } + + /** + * 获取指定声音流音量大小 + * @param streamType 流类型 + * @return 音量大小 + */ + public static int getStreamVolume(final int streamType) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.getStreamVolume(streamType); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getStreamVolume"); + } + } + return 0; + } + + /** + * 设置指定声音流音量大小 + * @param streamType 流类型 + * @param index 音量大小 + */ + public static void setStreamVolume(final int streamType, final int index) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.setStreamVolume(streamType, index, 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setStreamVolume"); + } + } + } + + // = + + /** + * 控制手机音量, 调小一个单位 + */ + public static void adjustVolumeLower() { + adjustVolume(AudioManager.ADJUST_LOWER); + } + + /** + * 控制手机音量, 调大一个单位 + */ + public static void adjustVolumeRaise() { + adjustVolume(AudioManager.ADJUST_RAISE); + } + + /** + * 控制手机音量, 调大或者调小一个单位 + *
+     *     AudioManager.ADJUST_LOWER 可调小一个单位
+     *     AudioManager.ADJUST_RAISE 可调大一个单位
+     * 
+ * @param direction 音量方向 ( 调大、调小 ) + */ + public static void adjustVolume(final int direction) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.adjustVolume(direction, 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "adjustVolume"); + } + } + } + + // = + + /** + * 控制指定声音流音量, 调小一个单位 + * @param streamType 流类型 + */ + public static void adjustStreamVolumeLower(final int streamType) { + adjustStreamVolume(streamType, AudioManager.ADJUST_LOWER); + } + + /** + * 控制指定声音流音量, 调大一个单位 + * @param streamType 流类型 + */ + public static void adjustStreamVolumeRaise(final int streamType) { + adjustStreamVolume(streamType, AudioManager.ADJUST_RAISE); + } + + /** + * 控制指定声音流音量, 调大或者调小一个单位 + *
+     *     AudioManager.ADJUST_LOWER 可调小一个单位
+     *     AudioManager.ADJUST_RAISE 可调大一个单位
+     * 
+ * @param streamType 流类型 + * @param direction 音量方向 ( 调大、调小 ) + */ + public static void adjustStreamVolume(final int streamType, final int direction) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.adjustStreamVolume(streamType, direction, 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "adjustStreamVolume"); + } + } + } + + // ============ + // = 静音状态 = + // ============ + + /** + * 设置媒体声音静音状态 + * @param state {@code true} 静音, {@code false} 非静音 + */ + public static void setStreamMuteByMusic(final boolean state) { + setStreamMute(AudioManager.STREAM_MUSIC, state); + } + + /** + * 设置通话声音静音状态 + * @param state {@code true} 静音, {@code false} 非静音 + */ + public static void setStreamMuteByVoiceCall(final boolean state) { + setStreamMute(AudioManager.STREAM_VOICE_CALL, state); + } + + /** + * 设置系统声音静音状态 + * @param state {@code true} 静音, {@code false} 非静音 + */ + public static void setStreamMuteBySystem(final boolean state) { + setStreamMute(AudioManager.STREAM_SYSTEM, state); + } + + /** + * 设置来电响铃静音状态 + * @param state {@code true} 静音, {@code false} 非静音 + */ + public static void setStreamMuteByRing(final boolean state) { + setStreamMute(AudioManager.STREAM_RING, state); + } + + /** + * 设置闹钟声音静音状态 + * @param state {@code true} 静音, {@code false} 非静音 + */ + public static void setStreamMuteByAlarm(final boolean state) { + setStreamMute(AudioManager.STREAM_ALARM, state); + } + + /** + * 设置通知声音静音状态 + * @param state {@code true} 静音, {@code false} 非静音 + */ + public static void setStreamMuteByNotification(final boolean state) { + setStreamMute(AudioManager.STREAM_NOTIFICATION, state); + } + + /** + * 设置指定声音流静音状态 + * @param streamType 流类型 + * @param state {@code true} 静音, {@code false} 非静音 + */ + public static void setStreamMute(final int streamType, final boolean state) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.setStreamMute(streamType, state); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setStreamMute"); + } + } + } + + // ======== + // = 模式 = + // ======== + + /** + * 获取当前的音频模式 + *
+     *     返回值有下述几种模式:
+     *     MODE_NORMAL( 普通 )
+     *     MODE_RINGTONE( 铃声 )
+     *     MODE_IN_CALL( 打电话 )
+     *     MODE_IN_COMMUNICATION( 通话 )
+     * 
+ * @return 当前的音频模式 + */ + public static int getMode() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.getMode(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMode"); + } + } + return AudioManager.MODE_NORMAL; + } + + /** + * 设置当前的音频模式 + *
+     *     有下述几种模式:
+     *     MODE_NORMAL( 普通 )
+     *     MODE_RINGTONE( 铃声 )
+     *     MODE_IN_CALL( 打电话 )
+     *     MODE_IN_COMMUNICATION( 通话 )
+     * 
+ * @param mode 音频模式 + */ + public static void setMode(final int mode) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.setMode(mode); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMode"); + } + } + } + + // ============ + // = 铃声模式 = + // ============ + + /** + * 获取当前的铃声模式 + *
+     *     返回值有下述几种模式:
+     *     RINGER_MODE_NORMAL( 普通 )
+     *     RINGER_MODE_SILENT( 静音 )
+     *     RINGER_MODE_VIBRATE( 震动 )
+     * 
+ * @return 当前的铃声模式 + */ + public static int getRingerMode() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.getRingerMode(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getRingerMode"); + } + } + return AudioManager.RINGER_MODE_NORMAL; + } + + /** + * 获取当前的铃声模式 + * @param ringerMode 铃声模式 + */ + public static void setRingerMode(final int ringerMode) { + setRingerMode(ringerMode, true); + } + + /** + * 获取当前的铃声模式 + *
+     *     有下述几种模式:
+     *     RINGER_MODE_NORMAL( 普通 )
+     *     RINGER_MODE_SILENT( 静音 )
+     *     RINGER_MODE_VIBRATE( 震动 )
+     * 
+ * @param ringerMode 铃声模式 + * @param setting 如果没授权, 是否跳转到设置页面 + */ + public static void setRingerMode(final int ringerMode, final boolean setting) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + if (isDoNotDisturb(setting)) { + audioManager.setRingerMode(ringerMode); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setRingerMode"); + } + } + } + + // = + + /** + * 设置静音模式 ( 静音, 且无振动 ) + */ + public static void ringerSilent() { + setRingerMode(AudioManager.RINGER_MODE_SILENT); + } + + /** + * 设置震动模式 ( 静音, 但有振动 ) + */ + public static void ringerVibrate() { + setRingerMode(AudioManager.RINGER_MODE_VIBRATE); + } + + /** + * 设置正常模式 ( 正常声音, 振动开关由 setVibrateSetting 决定 ) + */ + public static void ringerNormal() { + setRingerMode(AudioManager.RINGER_MODE_NORMAL); + } + + // = + + /** + * 判断是否授权 Do not disturb 权限 + *
+     *     授权 Do not disturb 权限, 才可进行音量操作
+     * 
+ * @param setting 如果没授权, 是否跳转到设置页面 + * @return {@code true} yes, {@code false} no + */ + public static boolean isDoNotDisturb(final boolean setting) { + try { + NotificationManager notificationManager = + (NotificationManager) DevUtils.getContext().getSystemService(Context.NOTIFICATION_SERVICE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N + && !notificationManager.isNotificationPolicyAccessGranted()) { + if (setting) { + Intent intent = new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + DevUtils.getContext().startActivity(intent); + } + } else { + return true; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isDoNotDisturb"); + } + return false; + } + + // = + + /** + * 设置是否打开扩音器 ( 扬声器 ) + * @param on {@code true} yes, {@code false} no + */ + public static void setSpeakerphoneOn(final boolean on) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.setSpeakerphoneOn(on); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setSpeakerphoneOn"); + } + } + } + + /** + * 设置是否让麦克风静音 + * @param on {@code true} yes, {@code false} no + */ + public static void setMicrophoneMute(final boolean on) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.setMicrophoneMute(on); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMicrophoneMute"); + } + } + } + + /** + * 判断是否打开扩音器 ( 扬声器 ) + * @return {@code true} yes, {@code false} no + */ + public static boolean isSpeakerphoneOn() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.isSpeakerphoneOn(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isSpeakerphoneOn"); + } + } + return false; + } + + /** + * 判断麦克风是否静音 + * @return {@code true} yes, {@code false} no + */ + public static boolean isMicrophoneMute() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.isMicrophoneMute(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isMicrophoneMute"); + } + } + return false; + } + + /** + * 判断是否有音乐处于活跃状态 + * @return {@code true} yes, {@code false} no + */ + public static boolean isMusicActive() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.isMusicActive(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isMusicActive"); + } + } + return false; + } + + /** + * 判断是否插入了耳机 + * @return {@code true} yes, {@code false} no + */ + public static boolean isWiredHeadsetOn() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.isWiredHeadsetOn(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isWiredHeadsetOn"); + } + } + return false; + } + + /** + * 检查蓝牙 A2DP 音频外设是否已连接 + * @return {@code true} yes, {@code false} no + */ + public static boolean isBluetoothA2dpOn() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.isBluetoothA2dpOn(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isBluetoothA2dpOn"); + } + } + return false; + } + + /** + * 检查当前平台是否支持使用 SCO 的关闭调用用例 + * @return {@code true} yes, {@code false} no + */ + public static boolean isBluetoothScoAvailableOffCall() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.isBluetoothScoAvailableOffCall(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isBluetoothScoAvailableOffCall"); + } + } + return false; + } + + /** + * 检查通信是否使用蓝牙 SCO + * @return {@code true} yes, {@code false} no + */ + public static boolean isBluetoothScoOn() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.isBluetoothScoOn(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isBluetoothScoOn"); + } + } + return false; + } + + /** + * 设置是否使用蓝牙 SCO 耳机进行通讯 + * @param on {@code true} yes, {@code false} no + */ + public static void setBluetoothScoOn(final boolean on) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.setBluetoothScoOn(on); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setBluetoothScoOn"); + } + } + } + + /** + * 启动蓝牙 SCO 音频连接 + */ + public static void startBluetoothSco() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.startBluetoothSco(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startBluetoothSco"); + } + } + } + + /** + * 停止蓝牙 SCO 音频连接 + */ + public static void stopBluetoothSco() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.stopBluetoothSco(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopBluetoothSco"); + } + } + } + + // = + + /** + * 加载音效 + */ + public static void loadSoundEffects() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.loadSoundEffects(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "loadSoundEffects"); + } + } + } + + /** + * 卸载音效 + */ + public static void unloadSoundEffects() { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.unloadSoundEffects(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "unloadSoundEffects"); + } + } + } + + /** + * 播放音效 + * @param effectType {@link AudioManager#FX_KEY_CLICK}, + * {@link AudioManager#FX_FOCUS_NAVIGATION_UP}, + * {@link AudioManager#FX_FOCUS_NAVIGATION_DOWN}, + * {@link AudioManager#FX_FOCUS_NAVIGATION_LEFT}, + * {@link AudioManager#FX_FOCUS_NAVIGATION_RIGHT}, + * {@link AudioManager#FX_KEYPRESS_STANDARD}, + * {@link AudioManager#FX_KEYPRESS_SPACEBAR}, + * {@link AudioManager#FX_KEYPRESS_DELETE}, + * {@link AudioManager#FX_KEYPRESS_RETURN}, + * {@link AudioManager#FX_KEYPRESS_INVALID}, + * @param volume 音量大小 + */ + public static void playSoundEffect(final int effectType, final float volume) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.playSoundEffect(effectType, volume); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "playSoundEffect"); + } + } + } + + // = + + /** + * 放弃音频焦点, 使上一个焦点所有者 ( 如果有 ) 接收焦点 + * @param listener 焦点监听事件 + */ + public static void abandonAudioFocus(final AudioManager.OnAudioFocusChangeListener listener) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.abandonAudioFocus(listener); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "abandonAudioFocus"); + } + } + } + + /** + * 调整最相关的流的音量, 或者给定的回退流 + * @param direction 调整参数 + */ + public static void adjustSuggestedStreamVolume(final int direction) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + audioManager.adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "adjustSuggestedStreamVolume"); + } + } + } + + /** + * 获取音频硬件指定 key 的参数值 + * @param keys Key + * @return 音频硬件指定 key 的参数值 + */ + public static String getParameters(final String keys) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.getParameters(keys); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getParameters"); + } + } + return null; + } + + /** + * 获取用户对振动类型的振动设置 + * @param vibrateType {@link AudioManager#VIBRATE_TYPE_NOTIFICATION} or {@link AudioManager#VIBRATE_TYPE_RINGER} + * @return {@link AudioManager#VIBRATE_SETTING_ON}, {@link AudioManager#VIBRATE_SETTING_OFF}, {@link AudioManager#VIBRATE_SETTING_ONLY_SILENT} + */ + public static int getVibrateSetting(final int vibrateType) { + AudioManager audioManager = getAudioManager(); + if (audioManager != null) { + try { + return audioManager.getVibrateSetting(vibrateType); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getVibrateSetting"); + } + } + return -1; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/BarUtils.java b/lib/DevApp/src/main/java/dev/utils/app/BarUtils.java similarity index 57% rename from DevLibUtils/src/main/java/dev/utils/app/BarUtils.java rename to lib/DevApp/src/main/java/dev/utils/app/BarUtils.java index 53b50df982..d488c891c2 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/BarUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/app/BarUtils.java @@ -1,18 +1,24 @@ package dev.utils.app; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.graphics.Color; +import android.graphics.Point; import android.os.Build; import android.support.annotation.ColorInt; import android.support.annotation.IntRange; import android.support.annotation.NonNull; +import android.support.annotation.RequiresApi; +import android.support.annotation.RequiresPermission; import android.support.v4.widget.DrawerLayout; import android.util.TypedValue; +import android.view.Display; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; import android.view.Window; @@ -25,7 +31,12 @@ /** * detail: 状态栏相关工具类 - * Created by Ttt + * @author Blankj + * @author Ttt + *
+ *     所需权限
+ *     
+ * 
*/ public final class BarUtils { @@ -35,21 +46,22 @@ private BarUtils() { private static final int DEFAULT_ALPHA = 112; private static final String TAG_COLOR = "TAG_COLOR"; private static final String TAG_ALPHA = "TAG_ALPHA"; - private static final int TAG_OFFSET = -123; + private static final String TAG_OFFSET = "TAG_OFFSET"; + private static final int KEY_OFFSET = -123; /** * 获取状态栏高度 - * @return the status bar's height + * @return 状态栏高度 */ public static int getStatusBarHeight() { - Resources resources = DevUtils.getContext().getResources(); + Resources resources = Resources.getSystem(); int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android"); return resources.getDimensionPixelSize(resourceId); } /** * 设置状态栏是否显示 - * @param activity The activity. + * @param activity {@link Activity} * @param isVisible True to set status bar visible, false otherwise. */ public static void setStatusBarVisibility(@NonNull final Activity activity, final boolean isVisible) { @@ -58,21 +70,27 @@ public static void setStatusBarVisibility(@NonNull final Activity activity, fina /** * 设置状态栏是否显示 - * @param window The window. + * @param window {@link Window} * @param isVisible True to set status bar visible, false otherwise. */ public static void setStatusBarVisibility(@NonNull final Window window, final boolean isVisible) { if (isVisible) { window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + showColorView(window); + showAlphaView(window); + addMarginTopEqualStatusBarHeight(window); } else { window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + hideColorView(window); + hideAlphaView(window); + subtractMarginTopEqualStatusBarHeight(window); } } /** * 判断状态栏是否显示 - * @param activity The activity. - * @return true : yes, false : no + * @param activity {@link Activity} + * @return {@code true} yes, {@code false} no */ public static boolean isStatusBarVisible(@NonNull final Activity activity) { int flags = activity.getWindow().getAttributes().flags; @@ -81,7 +99,7 @@ public static boolean isStatusBarVisible(@NonNull final Activity activity) { /** * 设置状态是否高亮模式 - * @param activity The activity. + * @param activity {@link Activity} * @param isLightMode True to set status bar light mode, false otherwise. */ public static void setStatusBarLightMode(@NonNull final Activity activity, final boolean isLightMode) { @@ -90,7 +108,7 @@ public static void setStatusBarLightMode(@NonNull final Activity activity, final /** * 设置状态是否高亮模式 - * @param window The window. + * @param window {@link Window} * @param isLightMode True to set status bar light mode, false otherwise. */ public static void setStatusBarLightMode(@NonNull final Window window, final boolean isLightMode) { @@ -110,35 +128,58 @@ public static void setStatusBarLightMode(@NonNull final Window window, final boo } /** - * 添加状态栏同等高度到View的顶部 - * @param view The view. + * 添加状态栏同等高度到 View 的顶部 + * @param view {@link View} */ - public static void addMarginTopEqualStatusBarHeight(@NonNull View view) { + public static void addMarginTopEqualStatusBarHeight(@NonNull final View view) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; - Object haveSetOffset = view.getTag(TAG_OFFSET); + view.setTag(TAG_OFFSET); + Object haveSetOffset = view.getTag(KEY_OFFSET); if (haveSetOffset != null && (Boolean) haveSetOffset) return; MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams(); layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin + getStatusBarHeight(), layoutParams.rightMargin, layoutParams.bottomMargin); - view.setTag(TAG_OFFSET, true); + view.setTag(KEY_OFFSET, true); } /** - * 减去状态栏同等高度 - * @param view The view. + * 添加状态栏同等高度到 View 的顶部 + * @param view {@link View} */ - public static void subtractMarginTopEqualStatusBarHeight(@NonNull View view) { + public static void subtractMarginTopEqualStatusBarHeight(@NonNull final View view) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; - Object haveSetOffset = view.getTag(TAG_OFFSET); + Object haveSetOffset = view.getTag(KEY_OFFSET); if (haveSetOffset == null || !(Boolean) haveSetOffset) return; MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams(); layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin - getStatusBarHeight(), layoutParams.rightMargin, layoutParams.bottomMargin); - view.setTag(TAG_OFFSET, false); + view.setTag(KEY_OFFSET, false); + } + + /** + * 为 view 增加 MarginTop 为状态栏高度 + * @param window {@link Window} + */ + private static void addMarginTopEqualStatusBarHeight(final Window window) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; + View withTag = window.getDecorView().findViewWithTag(TAG_OFFSET); + if (withTag == null) return; + addMarginTopEqualStatusBarHeight(withTag); + } + + /** + * 为 view 减少 MarginTop 为状态栏高度 + * @param window {@link Window} + */ + private static void subtractMarginTopEqualStatusBarHeight(final Window window) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; + View withTag = window.getDecorView().findViewWithTag(TAG_OFFSET); + if (withTag == null) return; + subtractMarginTopEqualStatusBarHeight(withTag); } /** * 设置状态栏颜色 - * @param activity The activity. - * @param color The status bar's color. + * @param activity {@link Activity} + * @param color 状态栏颜色 */ public static void setStatusBarColor(@NonNull final Activity activity, @ColorInt final int color) { setStatusBarColor(activity, color, DEFAULT_ALPHA, false); @@ -146,9 +187,9 @@ public static void setStatusBarColor(@NonNull final Activity activity, @ColorInt /** * 设置状态栏颜色 - * @param activity The activity. - * @param color The status bar's color. - * @param alpha The status bar's alpha which isn't the same as alpha in the color. + * @param activity {@link Activity} + * @param color 状态栏颜色 + * @param alpha 状态栏透明度 */ public static void setStatusBarColor(@NonNull final Activity activity, @ColorInt final int color, @IntRange(from = 0, to = 255) final int alpha) { setStatusBarColor(activity, color, alpha, false); @@ -156,10 +197,10 @@ public static void setStatusBarColor(@NonNull final Activity activity, @ColorInt /** * 设置状态栏颜色 - * @param activity The activity. - * @param color The status bar's color. - * @param alpha The status bar's alpha which isn't the same as alpha in the color. - * @param isDecor True to add fake status bar in DecorView, + * @param activity {@link Activity} + * @param color 状态栏颜色 + * @param alpha 状态栏透明度 + * @param isDecor True to add fake status bar in DecorView, * false to add fake status bar in ContentView. */ public static void setStatusBarColor(@NonNull final Activity activity, @ColorInt final int color, @IntRange(from = 0, to = 255) final int alpha, final boolean isDecor) { @@ -172,7 +213,7 @@ public static void setStatusBarColor(@NonNull final Activity activity, @ColorInt /** * 设置状态栏颜色 * @param fakeStatusBar The fake status bar view. - * @param color The status bar's color. + * @param color 状态栏颜色 */ public static void setStatusBarColor(@NonNull final View fakeStatusBar, @ColorInt final int color) { setStatusBarColor(fakeStatusBar, color, DEFAULT_ALPHA); @@ -181,8 +222,8 @@ public static void setStatusBarColor(@NonNull final View fakeStatusBar, @ColorIn /** * 设置状态栏颜色 * @param fakeStatusBar The fake status bar view. - * @param color The status bar's color. - * @param alpha The status bar's alpha which isn't the same as alpha in the color. + * @param color 状态栏颜色 + * @param alpha 状态栏透明度 */ public static void setStatusBarColor(@NonNull final View fakeStatusBar, @ColorInt final int color, @IntRange(from = 0, to = 255) final int alpha) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; @@ -190,13 +231,13 @@ public static void setStatusBarColor(@NonNull final View fakeStatusBar, @ColorIn transparentStatusBar((Activity) fakeStatusBar.getContext()); ViewGroup.LayoutParams layoutParams = fakeStatusBar.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; - layoutParams.height = BarUtils.getStatusBarHeight(); + layoutParams.height = getStatusBarHeight(); fakeStatusBar.setBackgroundColor(getStatusBarColor(color, alpha)); } /** * 设置状态栏透明度 - * @param activity The activity. + * @param activity {@link Activity} */ public static void setStatusBarAlpha(@NonNull final Activity activity) { setStatusBarAlpha(activity, DEFAULT_ALPHA, false); @@ -204,8 +245,8 @@ public static void setStatusBarAlpha(@NonNull final Activity activity) { /** * 设置状态栏透明度 - * @param activity The activity. - * @param alpha The status bar's alpha. + * @param activity {@link Activity} + * @param alpha 状态栏透明度 */ public static void setStatusBarAlpha(@NonNull final Activity activity, @IntRange(from = 0, to = 255) final int alpha) { setStatusBarAlpha(activity, alpha, false); @@ -213,9 +254,9 @@ public static void setStatusBarAlpha(@NonNull final Activity activity, @IntRange /** * 设置状态栏透明度 - * @param activity The activity. - * @param alpha The status bar's alpha. - * @param isDecor True to add fake status bar in DecorView, + * @param activity {@link Activity} + * @param alpha 状态栏透明度 + * @param isDecor True to add fake status bar in DecorView, * false to add fake status bar in ContentView. */ public static void setStatusBarAlpha(@NonNull final Activity activity, @IntRange(from = 0, to = 255) final int alpha, final boolean isDecor) { @@ -236,7 +277,7 @@ public static void setStatusBarAlpha(@NonNull final View fakeStatusBar) { /** * 设置状态栏透明度 * @param fakeStatusBar The fake status bar view. - * @param alpha The status bar's alpha. + * @param alpha 状态栏透明度 */ public static void setStatusBarAlpha(@NonNull final View fakeStatusBar, @IntRange(from = 0, to = 255) final int alpha) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; @@ -244,34 +285,49 @@ public static void setStatusBarAlpha(@NonNull final View fakeStatusBar, @IntRang transparentStatusBar((Activity) fakeStatusBar.getContext()); ViewGroup.LayoutParams layoutParams = fakeStatusBar.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; - layoutParams.height = BarUtils.getStatusBarHeight(); + layoutParams.height = getStatusBarHeight(); fakeStatusBar.setBackgroundColor(Color.argb(alpha, 0, 0, 0)); } + /** + * 设置自定义状态栏 + * @param fakeStatusBar The fake status bar view. + */ + public static void setStatusBarCustom(@NonNull final View fakeStatusBar) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; + fakeStatusBar.setVisibility(View.VISIBLE); + transparentStatusBar((Activity) fakeStatusBar.getContext()); + ViewGroup.LayoutParams layoutParams = fakeStatusBar.getLayoutParams(); + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; + layoutParams.height = getStatusBarHeight(); + } + /** * 设置状态栏的颜色 * DrawLayout must add android:fitsSystemWindows="true" - * @param activity The activity. - * @param drawer The DrawLayout. + * @param activity {@link Activity} + * @param drawer The DrawLayout. * @param fakeStatusBar The fake status bar view. - * @param color The status bar's color. - * @param isTop True to set DrawerLayout at the top layer, false otherwise. + * @param color 状态栏颜色 + * @param isTop True to set DrawerLayout at the top layer, false otherwise. */ - public static void setStatusBarColor4Drawer(@NonNull final Activity activity, @NonNull final DrawerLayout drawer, @NonNull final View fakeStatusBar, @ColorInt final int color, final boolean isTop) { + public static void setStatusBarColor4Drawer(@NonNull final Activity activity, @NonNull final DrawerLayout drawer, + @NonNull final View fakeStatusBar, @ColorInt final int color, final boolean isTop) { setStatusBarColor4Drawer(activity, drawer, fakeStatusBar, color, DEFAULT_ALPHA, isTop); } /** * 设置状态栏的颜色 * DrawLayout must add android:fitsSystemWindows="true" - * @param activity The activity. - * @param drawer The DrawLayout. + * @param activity {@link Activity} + * @param drawer The DrawLayout. * @param fakeStatusBar The fake status bar view. - * @param color The status bar's color. - * @param alpha The status bar's alpha which isn't the same as alpha in the color. - * @param isTop True to set DrawerLayout at the top layer, false otherwise. + * @param color 状态栏颜色 + * @param alpha 状态栏透明度 + * @param isTop True to set DrawerLayout at the top layer, false otherwise. */ - public static void setStatusBarColor4Drawer(@NonNull final Activity activity, @NonNull final DrawerLayout drawer, @NonNull final View fakeStatusBar, @ColorInt final int color, + public static void setStatusBarColor4Drawer(@NonNull final Activity activity, @NonNull final DrawerLayout drawer, + @NonNull final View fakeStatusBar, @ColorInt final int color, @IntRange(from = 0, to = 255) final int alpha, final boolean isTop) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; drawer.setFitsSystemWindows(false); @@ -290,23 +346,24 @@ public static void setStatusBarColor4Drawer(@NonNull final Activity activity, @N /** * 设置状态栏透明度 * DrawLayout must add android:fitsSystemWindows="true" - * @param activity The activity. - * @param drawer drawerLayout + * @param activity {@link Activity} + * @param drawer drawerLayout * @param fakeStatusBar The fake status bar view. - * @param isTop True to set DrawerLayout at the top layer, false otherwise. + * @param isTop True to set DrawerLayout at the top layer, false otherwise. */ - public static void setStatusBarAlpha4Drawer(@NonNull final Activity activity, @NonNull final DrawerLayout drawer, @NonNull final View fakeStatusBar, final boolean isTop) { + public static void setStatusBarAlpha4Drawer(@NonNull final Activity activity, @NonNull final DrawerLayout drawer, + @NonNull final View fakeStatusBar, final boolean isTop) { setStatusBarAlpha4Drawer(activity, drawer, fakeStatusBar, DEFAULT_ALPHA, isTop); } /** * 设置状态栏透明度 * DrawLayout must add android:fitsSystemWindows="true" - * @param activity The activity. - * @param drawer drawerLayout + * @param activity {@link Activity} + * @param drawer drawerLayout * @param fakeStatusBar The fake status bar view. - * @param alpha The status bar's alpha. - * @param isTop True to set DrawerLayout at the top layer, false otherwise. + * @param alpha 状态栏透明度 + * @param isTop True to set DrawerLayout at the top layer, false otherwise. */ public static void setStatusBarAlpha4Drawer(@NonNull final Activity activity, @NonNull final DrawerLayout drawer, @NonNull final View fakeStatusBar, @IntRange(from = 0, to = 255) final int alpha, final boolean isTop) { @@ -326,12 +383,13 @@ public static void setStatusBarAlpha4Drawer(@NonNull final Activity activity, @N /** * 设置状态栏颜色 - * @param activity - * @param color - * @param alpha - * @param isDecor + * @param activity {@link Activity} + * @param color 状态栏颜色 + * @param alpha 状态栏透明度 + * @param isDecor True to add fake status bar in DecorView, + * false to add fake status bar in ContentView. */ - private static void addStatusBarColor(final Activity activity, final int color, final int alpha, boolean isDecor) { + private static void addStatusBarColor(final Activity activity, @ColorInt final int color, @IntRange(from = 0, to = 255) final int alpha, final boolean isDecor) { ViewGroup parent = isDecor ? (ViewGroup) activity.getWindow().getDecorView() : (ViewGroup) activity.findViewById(android.R.id.content); View fakeStatusBarView = parent.findViewWithTag(TAG_COLOR); if (fakeStatusBarView != null) { @@ -340,17 +398,18 @@ private static void addStatusBarColor(final Activity activity, final int color, } fakeStatusBarView.setBackgroundColor(getStatusBarColor(color, alpha)); } else { - parent.addView(createColorStatusBarView(parent.getContext(), color, alpha)); + parent.addView(createColorStatusBarView(activity, color, alpha)); } } /** * 设置状态栏透明度 - * @param activity - * @param alpha - * @param isDecor + * @param activity {@link Activity} + * @param alpha 状态栏透明度 + * @param isDecor True to add fake status bar in DecorView, + * false to add fake status bar in ContentView. */ - private static void addStatusBarAlpha(final Activity activity, final int alpha, boolean isDecor) { + private static void addStatusBarAlpha(final Activity activity, @IntRange(from = 0, to = 255) final int alpha, final boolean isDecor) { ViewGroup parent = isDecor ? (ViewGroup) activity.getWindow().getDecorView() : (ViewGroup) activity.findViewById(android.R.id.content); View fakeStatusBarView = parent.findViewWithTag(TAG_ALPHA); if (fakeStatusBarView != null) { @@ -359,39 +418,77 @@ private static void addStatusBarAlpha(final Activity activity, final int alpha, } fakeStatusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0)); } else { - parent.addView(createAlphaStatusBarView(parent.getContext(), alpha)); + parent.addView(createAlphaStatusBarView(activity, alpha)); } } /** - * 隐藏颜色View - * @param activity + * 隐藏颜色 View + * @param activity {@link Activity} */ private static void hideColorView(final Activity activity) { - ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); + hideColorView(activity.getWindow()); + } + + /** + * 隐藏 透明度 View + * @param activity {@link Activity} + */ + private static void hideAlphaView(final Activity activity) { + hideAlphaView(activity.getWindow()); + } + + /** + * 隐藏颜色 View + * @param window {@link Window} + */ + private static void hideColorView(final Window window) { + ViewGroup decorView = (ViewGroup) window.getDecorView(); View fakeStatusBarView = decorView.findViewWithTag(TAG_COLOR); if (fakeStatusBarView == null) return; fakeStatusBarView.setVisibility(View.GONE); } /** - * 隐藏透明度 - * @param activity + * 隐藏 透明度 View + * @param window {@link Window} */ - private static void hideAlphaView(final Activity activity) { - ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); + private static void hideAlphaView(final Window window) { + ViewGroup decorView = (ViewGroup) window.getDecorView(); View fakeStatusBarView = decorView.findViewWithTag(TAG_ALPHA); if (fakeStatusBarView == null) return; fakeStatusBarView.setVisibility(View.GONE); } /** - * 获取状态栏颜色 = (传入颜色等,进行生成) - * @param color - * @param alpha - * @return + * 显示 View 颜色 + * @param window {@link Window} + */ + private static void showColorView(final Window window) { + ViewGroup decorView = (ViewGroup) window.getDecorView(); + View fakeStatusBarView = decorView.findViewWithTag(TAG_COLOR); + if (fakeStatusBarView == null) return; + fakeStatusBarView.setVisibility(View.VISIBLE); + } + + /** + * 显示 View 透明度 + * @param window {@link Window} + */ + private static void showAlphaView(final Window window) { + ViewGroup decorView = (ViewGroup) window.getDecorView(); + View fakeStatusBarView = decorView.findViewWithTag(TAG_ALPHA); + if (fakeStatusBarView == null) return; + fakeStatusBarView.setVisibility(View.VISIBLE); + } + + /** + * 获取状态栏颜色 ( 传入颜色等, 进行生成 ) + * @param color 状态栏颜色值 + * @param alpha 状态栏透明度 + * @return 状态栏颜色 */ - private static int getStatusBarColor(final int color, final int alpha) { + private static int getStatusBarColor(@ColorInt final int color, @IntRange(from = 0, to = 255) final int alpha) { if (alpha == 0) return color; float a = 1 - alpha / 255f; int red = (color >> 16) & 0xff; @@ -404,13 +501,13 @@ private static int getStatusBarColor(final int color, final int alpha) { } /** - * 创建对应颜色的状态栏View - * @param context - * @param color - * @param alpha - * @return + * 创建对应颜色的状态栏 View + * @param context {@link Context} + * @param color 状态栏颜色值 + * @param alpha 状态栏透明度 + * @return 对应颜色的状态栏 View */ - private static View createColorStatusBarView(final Context context, final int color, final int alpha) { + private static View createColorStatusBarView(final Context context, @ColorInt final int color, @IntRange(from = 0, to = 255) final int alpha) { View statusBarView = new View(context); statusBarView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight())); statusBarView.setBackgroundColor(getStatusBarColor(color, alpha)); @@ -419,12 +516,12 @@ private static View createColorStatusBarView(final Context context, final int co } /** - * 创建对应透明度的状态栏View - * @param context - * @param alpha - * @return + * 创建对应透明度的状态栏 View + * @param context {@link Context} + * @param alpha 状态栏透明度 + * @return 对应透明度的状态栏 View */ - private static View createAlphaStatusBarView(final Context context, final int alpha) { + private static View createAlphaStatusBarView(final Context context, @IntRange(from = 0, to = 255) final int alpha) { View statusBarView = new View(context); statusBarView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight())); statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0)); @@ -434,7 +531,7 @@ private static View createAlphaStatusBarView(final Context context, final int al /** * 设置透明的状态栏 - * @param activity + * @param activity {@link Activity} */ private static void transparentStatusBar(final Activity activity) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; @@ -449,12 +546,12 @@ private static void transparentStatusBar(final Activity activity) { } } - // ====================== - // ===== action bar ===== - // ====================== + // ============== + // = action bar = + // ============== /** - * 返回 ActionBase 高度 + * 获取 ActionBase 高度 * @return the action bar's height */ public static int getActionBarHeight() { @@ -465,28 +562,28 @@ public static int getActionBarHeight() { return 0; } - // ======================== - // === notification bar === - // ======================== + // ==================== + // = notification bar = + // ==================== /** * 设置通知栏是否显示 - * - * @param isVisible True to set notification bar visible, false otherwise. + * @param isVisible 是否显示通知栏 */ + @RequiresPermission(android.Manifest.permission.EXPAND_STATUS_BAR) public static void setNotificationBarVisibility(final boolean isVisible) { String methodName; if (isVisible) { - methodName = (Build.VERSION.SDK_INT <= 16) ? "expand" : "expandNotificationsPanel"; + methodName = (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) ? "expand" : "expandNotificationsPanel"; } else { - methodName = (Build.VERSION.SDK_INT <= 16) ? "collapse" : "collapsePanels"; + methodName = (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) ? "collapse" : "collapsePanels"; } invokePanels(methodName); } /** * 反射调用面板 - * @param methodName + * @param methodName 方法名 */ private static void invokePanels(final String methodName) { try { @@ -496,20 +593,20 @@ private static void invokePanels(final String methodName) { Class statusBarManager = Class.forName("android.app.StatusBarManager"); Method expand = statusBarManager.getMethod(methodName); expand.invoke(service); - } catch (Exception ignore) { + } catch (Exception e) { } } - // ====================== - // === navigation bar === - // ====================== + // ================== + // = navigation bar = + // ================== /** * 获取 NavigationView 高度 * @return the navigation bar's height */ public static int getNavBarHeight() { - Resources res = DevUtils.getContext().getResources(); + Resources res = Resources.getSystem(); int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId != 0) { return res.getDimensionPixelSize(resourceId); @@ -519,105 +616,108 @@ public static int getNavBarHeight() { } /** - * 设置导航栏是否可见(图标显示) - * @param activity The activity. - * @param isVisible True to set notification bar visible, false otherwise. + * 设置导航栏是否可见 ( 图标显示 ) + * @param activity {@link Activity} + * @param isVisible True to set navigation bar visible, false otherwise. */ - public static void setNavBarVisibility(@NonNull final Activity activity, boolean isVisible) { + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + public static void setNavBarVisibility(@NonNull final Activity activity, final boolean isVisible) { setNavBarVisibility(activity.getWindow(), isVisible); } /** - * 设置导航栏是否可见(图标显示) - * @param window The window. - * @param isVisible True to set notification bar visible, false otherwise. + * 设置导航栏是否可见 ( 图标显示 ) + * @param window {@link Window} + * @param isVisible True to set navigation bar visible, false otherwise. */ - public static void setNavBarVisibility(@NonNull final Window window, boolean isVisible) { + @RequiresApi(Build.VERSION_CODES.KITKAT) + public static void setNavBarVisibility(@NonNull final Window window, final boolean isVisible) { + final int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + final View decorView = window.getDecorView(); if (isVisible) { - window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() & ~uiOptions); } else { - window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); - View decorView = window.getDecorView(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - int visibility = decorView.getSystemUiVisibility(); - decorView.setSystemUiVisibility(visibility & ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } + decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | uiOptions); } } /** - * 设置沉浸模式 - * @param activity The activity. + * 判断导航栏是否可见 + * @param activity {@link Activity} + * @return {@code true} yes, {@code false} no */ - @TargetApi(Build.VERSION_CODES.KITKAT) - public static void setNavBarImmersive(@NonNull final Activity activity) { - setNavBarImmersive(activity.getWindow()); + public static boolean isNavBarVisible(@NonNull final Activity activity) { + return isNavBarVisible(activity.getWindow()); } /** - * 设置顶部沉浸模式 - * @param window The window. + * 判断导航栏是否可见 + * @param window {@link Window} + * @return {@code true} yes, {@code false} no */ - @TargetApi(Build.VERSION_CODES.KITKAT) - public static void setNavBarImmersive(@NonNull final Window window) { + public static boolean isNavBarVisible(@NonNull final Window window) { View decorView = window.getDecorView(); - window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); - int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - decorView.setSystemUiVisibility(uiOptions); + int visibility = decorView.getSystemUiVisibility(); + return (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; } /** - * 判断顶部状态栏(图标)是否显示 - * @param activity The activity. - * @return true : yes, false : no + * 设置导航栏颜色 + * @param activity {@link Activity} + * @param color The navigation bar's color. */ - public static boolean isNavBarVisible(@NonNull final Activity activity) { - return isNavBarVisible(activity.getWindow()); + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + public static void setNavBarColor(@NonNull final Activity activity, @ColorInt final int color) { + setNavBarColor(activity.getWindow(), color); } /** - * 判断顶部状态栏(图标)是否显示 - * @param window The window. - * @return true : yes, false : no + * 设置导航栏颜色 + * @param window {@link Window} + * @param color The navigation bar's color. */ - public static boolean isNavBarVisible(@NonNull final Window window) { - boolean isNoLimits = (window.getAttributes().flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) != 0; - if (isNoLimits) return false; - View decorView = window.getDecorView(); - int visibility = decorView.getSystemUiVisibility(); - return (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + public static void setNavBarColor(@NonNull final Window window, @ColorInt final int color) { + window.setNavigationBarColor(color); } - // == + /** + * 设置导航栏颜色 + * @param activity {@link Activity} + * @return the color of navigation bar + */ + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + public static int getNavBarColor(@NonNull final Activity activity) { + return getNavBarColor(activity.getWindow()); + } /** - * 设置是否显示状态栏图标等 - * @param activity - * @param isVisible + * 获取导航栏颜色 + * @param window {@link Window} + * @return the color of navigation bar */ - public static void setNavBar(Activity activity, boolean isVisible){ - setNavBar(activity.getWindow(), isVisible); + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + public static int getNavBarColor(@NonNull final Window window) { + return window.getNavigationBarColor(); } /** - * 设置是否显示状态栏图标等 - * @param window The window. + * 判断是否支持导航栏 + * @return {@code true} yes, {@code false} no */ - @TargetApi(Build.VERSION_CODES.KITKAT) - public static void setNavBar(@NonNull final Window window, boolean isVisible) { - if (isVisible){ - // 显示状态栏 - WindowManager.LayoutParams params = window.getAttributes(); - params.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN); - window.setAttributes(params); - window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); - } else { - // 隐藏状态栏 - WindowManager.LayoutParams params = window.getAttributes(); - params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; - window.setAttributes(params); - window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + public static boolean isSupportNavBar() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); + Display display = windowManager.getDefaultDisplay(); + Point size = new Point(); + Point realSize = new Point(); + display.getSize(size); + display.getRealSize(realSize); + return realSize.y != size.y || realSize.x != size.x; } + boolean menu = ViewConfiguration.get(DevUtils.getContext()).hasPermanentMenuKey(); + boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK); + return !menu && !back; } - -} +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/BrightnessUtils.java b/lib/DevApp/src/main/java/dev/utils/app/BrightnessUtils.java similarity index 70% rename from DevLibUtils/src/main/java/dev/utils/app/BrightnessUtils.java rename to lib/DevApp/src/main/java/dev/utils/app/BrightnessUtils.java index 052f7bfd5d..5b5ec03da1 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/BrightnessUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/app/BrightnessUtils.java @@ -6,7 +6,7 @@ import android.os.Build; import android.provider.Settings; import android.support.annotation.IntRange; -import android.support.annotation.NonNull; +import android.support.annotation.RequiresPermission; import android.view.Window; import android.view.WindowManager; @@ -15,19 +15,23 @@ /** * detail: 亮度相关工具类 - * Created by Ttt + * @author Ttt + *
+ *     所需权限
+ *     
+ * 
*/ public final class BrightnessUtils { private BrightnessUtils() { } - // 日志TAG + // 日志 TAG private static final String TAG = BrightnessUtils.class.getSimpleName(); /** * 判断是否开启自动调节亮度 - * @return true : 是, false : 否 + * @return {@code true} yes, {@code false} no */ public static boolean isAutoBrightnessEnabled() { try { @@ -41,10 +45,10 @@ public static boolean isAutoBrightnessEnabled() { /** * 设置是否开启自动调节亮度 - * - * @param enabled true : 打开, false : 关闭 - * @return true : 成功, false : 失败 + * @param enabled {@code true} 打开, {@code false} 关闭 + * @return {@code true} success, {@code false} fail */ + @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static boolean setAutoBrightnessEnabled(final boolean enabled) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.System.canWrite(DevUtils.getContext())) { try { @@ -52,7 +56,7 @@ public static boolean setAutoBrightnessEnabled(final boolean enabled) { intent.setData(Uri.parse("package:" + DevUtils.getContext().getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); DevUtils.getContext().startActivity(intent); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setAutoBrightnessEnabled"); } return false; @@ -60,15 +64,15 @@ public static boolean setAutoBrightnessEnabled(final boolean enabled) { try { return Settings.System.putInt(DevUtils.getContext().getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, enabled ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setAutoBrightnessEnabled"); } return false; } /** - * 获取屏幕亮度 - * @return 屏幕亮度 0-255 + * 获取屏幕亮度 0-255 + * @return 屏幕亮度 */ public static int getBrightness() { try { @@ -81,9 +85,10 @@ public static int getBrightness() { /** * 设置屏幕亮度 - * * @param brightness 亮度值 + * @return {@code true} success, {@code false} fail */ + @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static boolean setBrightness(@IntRange(from = 0, to = 255) final int brightness) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.System.canWrite(DevUtils.getContext())) { try { @@ -91,7 +96,7 @@ public static boolean setBrightness(@IntRange(from = 0, to = 255) final int brig intent.setData(Uri.parse("package:" + DevUtils.getContext().getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); DevUtils.getContext().startActivity(intent); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setBrightness"); } return false; @@ -101,7 +106,7 @@ public static boolean setBrightness(@IntRange(from = 0, to = 255) final int brig boolean result = Settings.System.putInt(resolver, Settings.System.SCREEN_BRIGHTNESS, brightness); resolver.notifyChange(Settings.System.getUriFor("screen_brightness"), null); return result; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setBrightness"); } return false; @@ -109,26 +114,35 @@ public static boolean setBrightness(@IntRange(from = 0, to = 255) final int brig /** * 设置窗口亮度 - * @param window 窗口 + * @param window {@link Window} * @param brightness 亮度值 */ - public static void setWindowBrightness(@NonNull final Window window, @IntRange(from = 0, to = 255) final int brightness) { + public static void setWindowBrightness(final Window window, @IntRange(from = 0, to = 255) final int brightness) { if (window == null) return; - WindowManager.LayoutParams lp = window.getAttributes(); - lp.screenBrightness = brightness / 255f; - window.setAttributes(lp); + try { + WindowManager.LayoutParams layoutParams = window.getAttributes(); + layoutParams.screenBrightness = brightness / 255f; + window.setAttributes(layoutParams); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setWindowBrightness"); + } } /** * 获取窗口亮度 - * @param window 窗口 + * @param window {@link Window} * @return 屏幕亮度 0-255 */ public static int getWindowBrightness(final Window window) { if (window == null) return -1; - WindowManager.LayoutParams lp = window.getAttributes(); - float brightness = lp.screenBrightness; - if (brightness < 0) return getBrightness(); - return (int) (brightness * 255); + try { + WindowManager.LayoutParams layoutParams = window.getAttributes(); + float brightness = layoutParams.screenBrightness; + if (brightness < 0) return getBrightness(); + return (int) (brightness * 255); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWindowBrightness"); + } + return 0; } -} +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/CPUUtils.java b/lib/DevApp/src/main/java/dev/utils/app/CPUUtils.java similarity index 66% rename from DevLibUtils/src/main/java/dev/utils/app/CPUUtils.java rename to lib/DevApp/src/main/java/dev/utils/app/CPUUtils.java index 30b6793a03..0d188f9892 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/CPUUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/app/CPUUtils.java @@ -6,7 +6,6 @@ import java.io.File; import java.io.FileFilter; import java.io.FileReader; -import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; @@ -16,46 +15,44 @@ import dev.utils.LogPrintUtils; /** - * detail: 获取CPU信息工具类 - * Created by Ttt + * detail: 获取 CPU 信息工具类 + * @author Ttt */ public final class CPUUtils { - private CPUUtils(){ + private CPUUtils() { } - // 日志TAG + // 日志 TAG private static final String TAG = CPUUtils.class.getSimpleName(); /** - * 获取处理器的Java虚拟机的数量 + * 获取处理器的 Java 虚拟机的数量 + * @return 处理器的 Java 虚拟机的数量 */ public static int getProcessorsCount() { return Runtime.getRuntime().availableProcessors(); } /** - * 获取手机CPU序列号 - * @return String cpu序列号(16位) 读取失败为"0000000000000000" + * 获取手机 CPU 序列号 + * @return CPU 序列号 (16 位 ) 读取失败为 "0000000000000000" */ public static String getSysCPUSerialNum() { - String str = "", strCPU = "", cpuSerialNum = "0000000000000000"; + String str, cpuSerialNum = "0000000000000000"; try { - //读取CPU信息 + // 读取 CPU 信息 Process pp = Runtime.getRuntime().exec("cat/proc/cpuinfo"); - InputStreamReader ir = new InputStreamReader(pp.getInputStream()); - LineNumberReader input = new LineNumberReader(ir); - //查找CPU序列号 + InputStreamReader isr = new InputStreamReader(pp.getInputStream()); + LineNumberReader input = new LineNumberReader(isr); + // 查找 CPU 序列号 for (int i = 1; i < 100; i++) { str = input.readLine(); if (str != null) { - //查找到序列号所在行 + // 查找到序列号所在行 if (str.indexOf("Serial") > -1) { - //提取序列号 - strCPU = str.substring(str.indexOf(":") + 1, - str.length()); - //去空格 - cpuSerialNum = strCPU.trim(); + // 提取序列号 + cpuSerialNum = str.substring(str.indexOf(":") + 1).trim(); break; } } else { @@ -69,8 +66,8 @@ public static String getSysCPUSerialNum() { } /** - * 获取CPU 信息 - * @return + * 获取 CPU 信息 + * @return CPU 信息 */ public static String getCpuInfo() { try { @@ -84,8 +81,8 @@ public static String getCpuInfo() { } /** - * 获取CPU 型号 - * @return + * 获取 CPU 型号 + * @return CPU 型号 */ public static String getCpuModel() { try { @@ -101,32 +98,32 @@ public static String getCpuModel() { } /** - * 获取 CPU 最大频率(单位KHZ) - * @return + * 获取 CPU 最大频率 ( 单位 KHZ) + * @return CPU 最大频率 ( 单位 KHZ) */ public static String getMaxCpuFreq() { String result = ""; ProcessBuilder cmd; - InputStream in = null; + InputStream is = null; try { String[] args = {"/system/bin/cat", "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"}; cmd = new ProcessBuilder(args); Process process = cmd.start(); - in = process.getInputStream(); + is = process.getInputStream(); byte[] re = new byte[24]; - while (in.read(re) != -1) { + while (is.read(re) != -1) { result = result + new String(re); } - in.close(); + is.close(); result = Formatter.formatFileSize(DevUtils.getContext(), Long.parseLong(result.trim()) * 1024) + " Hz"; } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getMaxCpuFreq"); result = "unknown"; } finally { - if (in != null){ + if (is != null) { try { - in.close(); - } catch (IOException e){ + is.close(); + } catch (Exception e) { } } } @@ -134,32 +131,32 @@ public static String getMaxCpuFreq() { } /** - * 获取 CPU 最小频率(单位KHZ) - * @return + * 获取 CPU 最小频率 ( 单位 KHZ) + * @return CPU 最小频率 ( 单位 KHZ) */ public static String getMinCpuFreq() { String result = ""; ProcessBuilder cmd; - InputStream in = null; + InputStream is = null; try { String[] args = {"/system/bin/cat", "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq"}; cmd = new ProcessBuilder(args); Process process = cmd.start(); - in = process.getInputStream(); + is = process.getInputStream(); byte[] re = new byte[24]; - while (in.read(re) != -1) { + while (is.read(re) != -1) { result = result + new String(re); } - in.close(); + is.close(); result = Formatter.formatFileSize(DevUtils.getContext(), Long.parseLong(result.trim()) * 1024) + " Hz"; } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getMinCpuFreq"); result = "unknown"; } finally { - if (in != null){ + if (is != null) { try { - in.close(); - } catch (IOException e){ + is.close(); + } catch (Exception e) { } } } @@ -167,8 +164,8 @@ public static String getMinCpuFreq() { } /** - * 实时获取 CPU 当前频率(单位KHZ) - * @return + * 获取 CPU 当前频率 ( 单位 KHZ) + * @return CPU 当前频率 ( 单位 KHZ) */ public static String getCurCpuFreq() { String result = ""; @@ -185,29 +182,26 @@ public static String getCurCpuFreq() { } /** - * 获取 CPU 几核 - * @return + * 获取 CPU 核心数 + * @return CPU 核心数 */ public static int getCoresNumbers() { // 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 - if (Pattern.matches("cpu[0-9]+", pathname.getName())) { - return true; - } - return false; + // Check if filename is "cpu", followed by a single digit number + return Pattern.matches("cpu[0-9]+", pathname.getName()); } } // CPU 核心数 int CPU_CORES = 0; try { - //Get directory containing CPU info + // Get directory containing CPU info File dir = new File("/sys/devices/system/cpu/"); - //Filter to only list the devices we care about + // Filter to only list the devices we care about File[] files = dir.listFiles(new CpuFilter()); - //Return the number of cores (virtual CPU devices) + // Return the number of cores (virtual CPU devices) CPU_CORES = files.length; } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getCoresNumbers"); @@ -222,14 +216,14 @@ public boolean accept(File pathname) { } /** - * 获取CPU名字 - * @return + * 获取 CPU 名字 + * @return CPU 名字 */ public static String getCpuName() { try { - BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/cpuinfo"), 8192); - String line = bufferedReader.readLine(); - bufferedReader.close(); + BufferedReader br = new BufferedReader(new FileReader("/proc/cpuinfo"), 8192); + String line = br.readLine(); + br.close(); String[] array = line.split(":\\s+", 2); if (array.length > 1) { return array[1]; @@ -242,34 +236,34 @@ public static String getCpuName() { /** * 获取 CMD 指令回调数据 - * @param args - * @return + * @param strings 指令集 + * @return 执行结果 */ - public static String getCMDOutputString(String[] args) { - InputStream in = null; + public static String getCMDOutputString(final String[] strings) { + InputStream is = null; try { - ProcessBuilder cmd = new ProcessBuilder(args); + ProcessBuilder cmd = new ProcessBuilder(strings); Process process = cmd.start(); - in = process.getInputStream(); - StringBuilder sb = new StringBuilder(); + is = process.getInputStream(); + StringBuilder builder = new StringBuilder(); byte[] re = new byte[64]; int len; - while ((len = in.read(re)) != -1) { - sb.append(new String(re, 0, len)); + while ((len = is.read(re)) != -1) { + builder.append(new String(re, 0, len)); } - in.close(); + is.close(); process.destroy(); - return sb.toString(); - } catch (IOException e) { + return builder.toString(); + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getCMDOutputString"); } finally { - if (in != null){ + if (is != null) { try { - in.close(); - } catch (IOException e){ + is.close(); + } catch (Exception e) { } } } return null; } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/CapturePictureUtils.java b/lib/DevApp/src/main/java/dev/utils/app/CapturePictureUtils.java new file mode 100644 index 0000000000..f3691545c7 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/CapturePictureUtils.java @@ -0,0 +1,493 @@ +package dev.utils.app; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Picture; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Build; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.WindowManager; +import android.webkit.WebView; +import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.ScrollView; + +import java.util.ArrayList; +import java.util.List; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 截图工具类 + * @author Ttt + *
+ *     截图
+ *     @see 
+ *     WebView 截长图解决方案
+ *     @see 
+ *     X5 WebView 使用 snapshotWholePage 方法清晰截图
+ *     @see 
+ *     Android 实现截屏和截长图功能的几种方法
+ *     @see 
+ * 
+ */ +public final class CapturePictureUtils { + + private CapturePictureUtils() { + } + + // 日志 TAG + private static final String TAG = CapturePictureUtils.class.getSimpleName(); + + // ======== + // = 截图 = + // ======== + + // =========== + // = WebView = + // =========== + + /** + * 关闭 WebView 优化 + *
+     *     推荐在 setContentView 前调用
+     *     {@link CapturePictureUtils#snapshotByWebView}
+     * 
+ */ + public static void enableSlowWholeDocumentDraw() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + android.webkit.WebView.enableSlowWholeDocumentDraw(); + } + } + + /** + * 截图 WebView + * @param webView {@link WebView} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByWebView(final WebView webView) { + return snapshotByWebView(webView, Integer.MAX_VALUE, Bitmap.Config.RGB_565); + } + + /** + * 截图 WebView + * @param webView {@link WebView} + * @param maxHeight 最大高度 + * @return {@link Bitmap} + */ + public static Bitmap snapshotByWebView(final WebView webView, final int maxHeight) { + return snapshotByWebView(webView, maxHeight, Bitmap.Config.RGB_565); + } + + /** + * 截图 WebView + * @param webView {@link WebView} + * @param scale 缩放比例 + * @return {@link Bitmap} + */ + public static Bitmap snapshotByWebView(final WebView webView, final float scale) { + return snapshotByWebView(webView, Integer.MAX_VALUE, Bitmap.Config.RGB_565, scale); + } + + /** + * 截图 WebView + * @param webView {@link WebView} + * @param maxHeight 最大高度 + * @param config {@link Bitmap.Config} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByWebView(final WebView webView, final int maxHeight, final Bitmap.Config config) { + return snapshotByWebView(webView, maxHeight, config, 0f); + } + + /** + * 截图 WebView + *
+     *     TODO 在 Android 5.0 及以上版本, Android 对 WebView 进行了优化, 为了减少内存使用和提高性能
+     *     TODO 使用 WebView 加载网页时只绘制显示部分, 如果我们不做处理, 就会出现只截到屏幕内显示的 WebView 内容, 其它部分是空白的情况
+     *     TODO 通过调用 WebView.enableSlowWholeDocumentDraw() 方法可以关闭这种优化, 但要注意的是, 该方法需要在 WebView 实例被创建前就要调用,
+     *     TODO 否则没有效果, 所以我们在 WebView 实例被创建前加入代码
+     *     {@link CapturePictureUtils#enableSlowWholeDocumentDraw}
+     * 
+ * @param webView {@link WebView} + * @param maxHeight 最大高度 + * @param config {@link Bitmap.Config} + * @param scale 缩放比例 + * @return {@link Bitmap} + */ + public static Bitmap snapshotByWebView(final WebView webView, final int maxHeight, + final Bitmap.Config config, final float scale) { + if (webView != null && config != null) { + // Android 5.0 以上 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + try { + float newScale = scale; + if (newScale <= 0) { + // 该方法已抛弃, 可通过 setWebViewClient + // onScaleChanged(WebView view, float oldScale, float newScale) + // 存储并传入 newScale + newScale = webView.getScale(); + } + int width = webView.getWidth(); + int height = (int) (webView.getContentHeight() * newScale + 0.5); + // 重新设置高度 + height = (height > maxHeight) ? maxHeight : height; + // 创建位图 + Bitmap bitmap = Bitmap.createBitmap(width, height, config); + Canvas canvas = new Canvas(bitmap); + webView.draw(canvas); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "snapshotByWebView - SDK_INT >= 21(5.0)"); + } + } else { + try { + Picture picture = webView.capturePicture(); + int width = picture.getWidth(); + int height = picture.getHeight(); + if (width > 0 && height > 0) { + // 重新设置高度 + height = (height > maxHeight) ? maxHeight : height; + // 创建位图 + Bitmap bitmap = Bitmap.createBitmap(width, height, config); + Canvas canvas = new Canvas(bitmap); + picture.draw(canvas); + return bitmap; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "snapshotByWebView - SDK_INT < 21(5.0)"); + } + } + } + return null; + } + + // ======== + // = View = + // ======== + + /** + * 通过 View 绘制为 Bitmap + * @param view {@link View} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByView(final View view) { + return snapshotByView(view, Bitmap.Config.ARGB_8888); + } + + /** + * 通过 View 绘制为 Bitmap + * @param view {@link View} + * @param config {@link Bitmap.Config} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByView(final View view, final Bitmap.Config config) { + if (view == null) return null; + try { + Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), config); + Canvas canvas = new Canvas(bitmap); + view.layout(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); + view.draw(canvas); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "snapshotByView"); + } + return null; + } + + /** + * 通过 View Cache 绘制为 Bitmap + * @param view {@link View} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByViewCache(final View view) { + if (view == null) return null; + try { + // 清除视图焦点 + view.clearFocus(); + // 将视图设为不可点击 + view.setPressed(false); + + // 获取视图是否可以保存画图缓存 + boolean willNotCache = view.willNotCacheDrawing(); + view.setWillNotCacheDrawing(false); + + // 获取绘制缓存位图的背景颜色 + int color = view.getDrawingCacheBackgroundColor(); + // 设置绘图背景颜色 + view.setDrawingCacheBackgroundColor(0); + if (color != 0) { // 获取的背景不是黑色的则释放以前的绘图缓存 + view.destroyDrawingCache(); // 释放绘图资源所使用的缓存 + } + + // 重新创建绘图缓存, 此时的背景色是黑色 + view.buildDrawingCache(); + // 获取绘图缓存, 注意这里得到的只是一个图像的引用 + Bitmap cacheBitmap = view.getDrawingCache(); + if (cacheBitmap == null) return null; + + Bitmap bitmap = Bitmap.createBitmap(cacheBitmap); + // 释放位图内存 + view.destroyDrawingCache(); + // 回滚以前的缓存设置、缓存颜色设置 + view.setWillNotCacheDrawing(willNotCache); + view.setDrawingCacheBackgroundColor(color); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "snapshotByViewCache"); + } + return null; + } + + // ================ + // = LinearLayout = + // ================ + + /** + * 通过 LinearLayout 绘制为 Bitmap + * @param linearLayout {@link LinearLayout} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByLinearLayout(final LinearLayout linearLayout) { + return snapshotByLinearLayout(linearLayout, Bitmap.Config.ARGB_8888); + } + + /** + * 通过 LinearLayout 绘制为 Bitmap + *
+     *     LinearLayout 容器中不能有诸如 ListView、WebView 这样的高度可变的控件
+     * 
+ * @param linearLayout {@link LinearLayout} + * @param config {@link Bitmap.Config} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByLinearLayout(final LinearLayout linearLayout, final Bitmap.Config config) { + return snapshotByView(linearLayout, config); + } + + // ============== + // = ScrollView = + // ============== + + /** + * 通过 ScrollView 绘制为 Bitmap + * @param scrollView {@link ScrollView} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByScrollView(final ScrollView scrollView) { + return snapshotByScrollView(scrollView, Bitmap.Config.ARGB_8888); + } + + /** + * 通过 ScrollView 绘制为 Bitmap + *
+     *     ScrollView 容器中不能有诸如 ListView、WebView 这样的高度可变的控件
+     * 
+ * @param scrollView {@link ScrollView} + * @param config {@link Bitmap.Config} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByScrollView(final ScrollView scrollView, final Bitmap.Config config) { + if (scrollView == null) return null; + try { + int height = 0; + for (int i = 0, len = scrollView.getChildCount(); i < len; i++) { + height += scrollView.getChildAt(i).getHeight(); + } + + Bitmap bitmap = Bitmap.createBitmap(scrollView.getWidth(), height, config); + Canvas canvas = new Canvas(bitmap); + scrollView.layout(scrollView.getLeft(), scrollView.getTop(), scrollView.getRight(), scrollView.getBottom()); + scrollView.draw(canvas); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "snapshotByScrollView"); + } + return null; + } + + // ============ + // = ListView = + // ============ + + /** + * 通过 ListView 绘制为 Bitmap + * @param listView {@link ListView} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByListView(final ListView listView) { + return snapshotByListView(listView, Bitmap.Config.ARGB_8888); + } + + /** + * 通过 ListView 绘制为 Bitmap + * @param listView {@link ListView} + * @param config {@link Bitmap.Config} + * @return {@link Bitmap} + */ + public static Bitmap snapshotByListView(final ListView listView, final Bitmap.Config config) { + if (listView == null) return null; + try { + int height = 0; + // 获取子项间分隔符占用的高度 + int dividerHeight = listView.getDividerHeight(); + ListAdapter listAdapter = listView.getAdapter(); + List listBitmaps = new ArrayList<>(); + // 循环绘制每个 Item 并保存 Bitmap + for (int i = 0, len = listAdapter.getCount(); i < len; i++) { + View childView = listAdapter.getView(i, null, listView); + childView.measure(View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); + childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight()); + childView.setDrawingCacheEnabled(true); + childView.buildDrawingCache(); + + listBitmaps.add(childView.getDrawingCache()); + height += childView.getMeasuredHeight(); + } + // 追加子项间分隔符占用的高度 + height += (dividerHeight * (listAdapter.getCount() - 1)); + + int width = listView.getMeasuredWidth(); + // 创建位图 + Bitmap bitmap = Bitmap.createBitmap(width, height, config); + Canvas canvas = new Canvas(bitmap); + // 拼接 Bitmap + Paint paint = new Paint(); + int iHeight = 0; + for (int i = 0, len = listBitmaps.size(); i < len; i++) { + Bitmap bmp = listBitmaps.get(i); + canvas.drawBitmap(bmp, 0, iHeight, paint); + iHeight += (bmp.getHeight() + dividerHeight); + // 释放资源 + bmp.recycle(); + bmp = null; + } + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "snapshotByListView"); + } + return null; + } + + // ============ + // = Activity = + // ============ + + /** + * 获取当前屏幕截图, 包含状态栏 ( 顶部灰色 TitleBar 高度, 没有设置 android:theme 的 NoTitleBar 时会显示 ) + * @param activity {@link Activity} + * @return 当前屏幕截图, 包含状态栏 + */ + public static Bitmap snapshotWithStatusBar(final Activity activity) { + try { + View view = activity.getWindow().getDecorView(); + view.setDrawingCacheEnabled(true); + // 重新创建绘图缓存, 此时的背景色是黑色 + view.buildDrawingCache(); + // 获取绘图缓存, 注意这里得到的只是一个图像的引用 + Bitmap cacheBitmap = view.getDrawingCache(); + if (cacheBitmap == null) return null; + // 获取屏幕宽度 + int[] widthHeight = getScreenWidthHeight(); + + Rect frame = new Rect(); + activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); + // 创建新的图片 + Bitmap bitmap = Bitmap.createBitmap(cacheBitmap, 0, 0, widthHeight[0], widthHeight[1]); + // 释放绘图资源所使用的缓存 + view.destroyDrawingCache(); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "snapshotWithStatusBar"); + } + return null; + } + + /** + * 获取当前屏幕截图, 不包含状态栏 ( 如果 android:theme 全屏, 则截图无状态栏 ) + * @param activity {@link Activity} + * @return 当前屏幕截图, 不包含状态栏 + */ + public static Bitmap snapshotWithoutStatusBar(final Activity activity) { + try { + View view = activity.getWindow().getDecorView(); + view.setDrawingCacheEnabled(true); + // 重新创建绘图缓存, 此时的背景色是黑色 + view.buildDrawingCache(); + // 获取绘图缓存, 注意这里得到的只是一个图像的引用 + Bitmap cacheBitmap = view.getDrawingCache(); + if (cacheBitmap == null) return null; + // 获取屏幕宽度 + int[] widthHeight = getScreenWidthHeight(); + // 获取状态栏高度 + int statusBarHeight = getStatusBarHeight(activity); + + Rect frame = new Rect(); + activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); + // 创建新的图片 + Bitmap bitmap = Bitmap.createBitmap(cacheBitmap, 0, statusBarHeight, widthHeight[0], widthHeight[1] - statusBarHeight); + // 释放绘图资源所使用的缓存 + view.destroyDrawingCache(); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "snapshotWithoutStatusBar"); + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = ScreenUtils = + // =============== + + /** + * 获取屏幕宽高 + * @return int[], 0 = 宽度, 1 = 高度 + */ + private static int[] getScreenWidthHeight() { + try { + WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); + if (windowManager == null) { + DisplayMetrics displayMetrics = DevUtils.getContext().getResources().getDisplayMetrics(); + return new int[]{displayMetrics.widthPixels, displayMetrics.heightPixels}; + } + Point point = new Point(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + windowManager.getDefaultDisplay().getRealSize(point); + } else { + windowManager.getDefaultDisplay().getSize(point); + } + return new int[]{point.x, point.y}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getScreenWidthHeight"); + } + return new int[]{0, 0}; + } + + /** + * 获取应用区域 TitleBar 高度 ( 顶部灰色 TitleBar 高度, 没有设置 android:theme 的 NoTitleBar 时会显示 ) + * @param activity {@link Activity} + * @return 应用区域 TitleBar 高度 + */ + private static int getStatusBarHeight(final Activity activity) { + try { + Rect rect = new Rect(); + activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); + return rect.top; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getStatusBarHeight"); + } + return 0; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/CleanUtils.java b/lib/DevApp/src/main/java/dev/utils/app/CleanUtils.java new file mode 100644 index 0000000000..4bd8fc1ea8 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/CleanUtils.java @@ -0,0 +1,221 @@ +package dev.utils.app; + +import android.os.Environment; + +import java.io.File; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 本应用数据清除管理工具类 + * @author Ttt + *
+ *     主要功能有清除内 / 外缓存、清除数据库、清除 SharedPreferences、清除 files 和清除自定义目录
+ * 
+ */ +public final class CleanUtils { + + private CleanUtils() { + } + + // 日志 TAG + private static final String TAG = CleanUtils.class.getSimpleName(); + + /** + * 清除内部缓存 - path /data/data/package/cache + * @return {@code true} success, {@code false} fail + */ + public static boolean cleanInternalCache() { + try { + return deleteFilesInDir(DevUtils.getContext().getCacheDir()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cleanInternalCache"); + } + return false; + } + + /** + * 清除内部文件 - path /data/data/package/files + * @return {@code true} success, {@code false} fail + */ + public static boolean cleanInternalFiles() { + try { + return deleteFilesInDir(DevUtils.getContext().getFilesDir()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cleanInternalFiles"); + } + return false; + } + + /** + * 清除内部数据库 - path /data/data/package/databases + * @return {@code true} success, {@code false} fail + */ + public static boolean cleanInternalDbs() { + try { + return deleteFilesInDir(new File(DevUtils.getContext().getFilesDir().getParent(), "databases")); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cleanInternalDbs"); + } + return false; + } + + /** + * 根据名称清除数据库 - path /data/data/package/databases/dbName + * @param dbName 数据库名 + * @return {@code true} success, {@code false} fail + */ + public static boolean cleanInternalDbByName(final String dbName) { + try { + return DevUtils.getContext().deleteDatabase(dbName); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cleanInternalDbByName"); + } + return false; + } + + /** + * 清除内部 SP - path /data/data/package/shared_prefs + * @return {@code true} success, {@code false} fail + */ + public static boolean cleanInternalSp() { + try { + return deleteFilesInDir(new File(DevUtils.getContext().getFilesDir().getParent(), "shared_prefs")); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cleanInternalSp"); + } + return false; + } + + /** + * 清除外部缓存 - path /storage/emulated/0/android/data/package/cache + * @return {@code true} success, {@code false} fail + */ + public static boolean cleanExternalCache() { + try { + if (isSDCardEnable()) + return deleteFilesInDir(DevUtils.getContext().getExternalCacheDir()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cleanExternalCache"); + } + return false; + } + + // = + + /** + * 清除自定义路径下的文件, 使用需小心请不要误删, 而且只支持目录下的文件删除 + * @param filePath 文件路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean cleanCustomDir(final String filePath) { + return deleteFilesInDir(getFileByPath(filePath)); + } + + /** + * 清除自定义路径下的文件, 使用需小心请不要误删, 而且只支持目录下的文件删除 + * @param file 文件路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean cleanCustomDir(final File file) { + return deleteFilesInDir(file); + } + + /** + * 清除本应用所有的数据 + * @param filePaths 文件路径数组 + */ + public static void cleanApplicationData(final String... filePaths) { + cleanInternalCache(); + cleanExternalCache(); + cleanInternalDbs(); + cleanInternalSp(); + cleanInternalFiles(); + try { + for (String path : filePaths) { + cleanCustomDir(path); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cleanApplicationData"); + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = FileUtils = + // ============= + + /** + * 删除目录下所有过滤的文件 + * @param dir 目录 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + private static boolean deleteFilesInDir(final File dir) { + // dir is null then return false + if (dir == null) return false; + // dir doesn't exist then return true + if (!dir.exists()) return true; + // dir isn't a directory then return false + if (!dir.isDirectory()) return false; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (file.isFile()) { + if (!file.delete()) return false; + } else if (file.isDirectory()) { + if (!deleteDir(file)) return false; + } + } + } + return true; + } + + /** + * 删除目录 + * @param dir 目录 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + private static boolean deleteDir(final File dir) { + if (dir == null) return false; + // dir doesn't exist then return true + if (!dir.exists()) return true; + // dir isn't a directory then return false + if (!dir.isDirectory()) return false; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (file.isFile()) { + if (!file.delete()) return false; + } else if (file.isDirectory()) { + if (!deleteDir(file)) return false; + } + } + } + return dir.delete(); + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + // =============== + // = SDCardUtils = + // =============== + + /** + * 判断内置 SDCard 是否正常挂载 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSDCardEnable() { + return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ClickUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ClickUtils.java new file mode 100644 index 0000000000..904b614dd9 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ClickUtils.java @@ -0,0 +1,445 @@ +package dev.utils.app; + +import android.graphics.Rect; +import android.view.TouchDelegate; +import android.view.View; + +import java.util.HashMap; +import java.util.Map; + +import dev.utils.LogPrintUtils; + +/** + * detail: 点击 ( 双击 ) 工具类 + * @author Ttt + */ +public final class ClickUtils { + + private ClickUtils() { + } + + // 日志 TAG + private static final String TAG = ClickUtils.class.getSimpleName(); + // 全局共用的点击辅助类 + private static final ClickAssist sGlobalClickAssist = new ClickAssist(); + // 功能模块 ClickAssist Maps + private static final Map sClickAssistMaps = new HashMap<>(); + + /** + * 增加控件的触摸范围, 最大范围只能是父布局所包含的的区域 + * @param view 待添加点击范围 View + * @param range 点击范围 + */ + public static void addTouchArea(final View view, final int range) { + addTouchArea(view, range, range, range, range); + } + + /** + * 增加控件的触摸范围, 最大范围只能是父布局所包含的的区域 + * @param view 待添加点击范围 View + * @param top top range + * @param bottom bottom range + * @param left left range + * @param right right range + */ + public static void addTouchArea(final View view, final int top, final int bottom, final int left, final int right) { + if (view != null) { + try { + final View parent = (View) view.getParent(); + parent.post(new Runnable() { + public void run() { + try { + Rect bounds = new Rect(); + view.getHitRect(bounds); + + // 设置范围 + bounds.top -= top; + bounds.bottom += bottom; + bounds.left -= left; + bounds.right += right; + + TouchDelegate touchDelegate = new TouchDelegate(bounds, view); + if (view.getParent() instanceof View) { + ((View) view.getParent()).setTouchDelegate(touchDelegate); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "addTouchArea - runnable"); + } + } + }); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "addTouchArea"); + } + } + } + + // ====================== + // = 功能模块辅助类操作 = + // ====================== + + /** + * 获取对应功能模块点击辅助类 + * @param object key by Object + * @return {@link ClickAssist} + */ + public static ClickAssist get(final Object object) { + // 获取对应模块点击辅助类 + ClickAssist clickAssist = sClickAssistMaps.get(object); + if (clickAssist != null) { + return clickAssist; + } + clickAssist = new ClickAssist(); + sClickAssistMaps.put(object, clickAssist); + return clickAssist; + } + + /** + * 移除对应功能模块点击辅助类 + * @param object key by Object + */ + public static void remove(final Object object) { + sClickAssistMaps.remove(object); + } + + // ====================== + // = 全局点击辅助类操作 = + // ====================== + + /** + * 判断是否双击 ( 无效点击 - 短时间内多次点击 ) + * @return {@code true} yes, {@code false} no + */ + public static boolean isFastDoubleClick() { + return sGlobalClickAssist.isFastDoubleClick(); + } + + /** + * 判断是否双击 ( 无效点击 - 短时间内多次点击 ) + * @param tagId id + * @return {@code true} yes, {@code false} no + */ + public static boolean isFastDoubleClick(final int tagId) { + return sGlobalClickAssist.isFastDoubleClick(tagId); + } + + /** + * 判断是否双击 ( 无效点击 - 短时间内多次点击 ) + * @param tagId id + * @param intervalTime 双击时间间隔 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFastDoubleClick(final int tagId, final long intervalTime) { + return sGlobalClickAssist.isFastDoubleClick(tagId, intervalTime); + } + + // = + + /** + * 判断是否双击 ( 无效点击 - 短时间内多次点击 ) + * @param object key by Object + * @param configKey 时间间隔配置 Key + * @return {@code true} yes, {@code false} no + */ + public static boolean isFastDoubleClick(final Object object, final String configKey) { + return sGlobalClickAssist.isFastDoubleClick(object, configKey); + } + + /** + * 判断是否双击 ( 无效点击 - 短时间内多次点击 ) + * @param object key by Object + * @param intervalTime 双击时间间隔 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFastDoubleClick(final Object object, final long intervalTime) { + return sGlobalClickAssist.isFastDoubleClick(object, intervalTime); + } + + // = + + /** + * 初始化配置信息 + * @param mapConfigs config Maps + * @return {@link ClickAssist} + */ + public static ClickAssist initConfig(final Map mapConfigs) { + return sGlobalClickAssist.initConfig(mapConfigs); + } + + /** + * 添加配置信息 + * @param key config Key + * @param value config Value + * @return {@link ClickAssist} + */ + public static ClickAssist putConfig(final String key, final Long value) { + return sGlobalClickAssist.putConfig(key, value); + } + + /** + * 移除配置信息 + * @param key config Key + * @return {@link ClickAssist} + */ + public static ClickAssist removeConfig(final String key) { + return sGlobalClickAssist.removeConfig(key); + } + + /** + * 获取配置时间 + * @param key config Key + * @return 配置时间 + */ + public static Long getConfigTime(final String key) { + return sGlobalClickAssist.getConfigTime(key); + } + + // = + + /** + * 移除点击记录 + * @param key tag Key + * @return {@link ClickAssist} + */ + public static ClickAssist removeRecord(final String key) { + return sGlobalClickAssist.removeRecord(key); + } + + /** + * 清空全部点击记录 + * @return {@link ClickAssist} + */ + public static ClickAssist clearRecord() { + return sGlobalClickAssist.clearRecord(); + } + + // = + + /** + * 设置默认点击时间间隔 + * @param intervalTime 双击时间间隔 + * @return {@link ClickAssist} + */ + public static ClickAssist setIntervalTime(final long intervalTime) { + return sGlobalClickAssist.setIntervalTime(intervalTime); + } + + /** + * 重置处理 + * @return {@link ClickAssist} + */ + public static ClickAssist reset() { + return sGlobalClickAssist.reset(); + } + + // = + + /** + * detail: 点击 ( 双击 ) 辅助类 + * @author Ttt + *
+     *     ps: 该辅助类, 主要避免全局共用一个双击控制类, 容易出现冲突, 方便控制某个 Activity 或功能模块 双击处理
+     *     使用 Key(Tag-Object) 获取指定的 {@link ClickAssist}, 能够实现不同 Activity 或功能模块 单独使用 {@link ClickAssist}
+     *     并且可以进行销毁处理
+     * 
+ */ + public static class ClickAssist { + + // 最后一次点击的标识 id + private int mLastTagId = -1; + // 最后一次点击时间 + private long mLastClickTime = 0L; + // 双击间隔时间 + private long mIntervalTime = 1000L; + // 配置数据 + private final Map mConfigMaps = new HashMap<>(); + // 点击记录数据 + private final Map mRecordMaps = new HashMap<>(); + + public ClickAssist() { + } + + /** + * 构造函数 + * @param intervalTime 双击间隔时间 + */ + public ClickAssist(final long intervalTime) { + this.mIntervalTime = intervalTime; + } + + // = + + /** + * 判断是否双击 ( 无效点击 - 短时间内多次点击 ) + * @return {@code true} yes, {@code false} no + */ + public boolean isFastDoubleClick() { + return isFastDoubleClick(-1, mIntervalTime); + } + + /** + * 判断是否双击 ( 无效点击 - 短时间内多次点击 ) + * @param tagId id + * @return {@code true} yes, {@code false} no + */ + public boolean isFastDoubleClick(final int tagId) { + return isFastDoubleClick(tagId, mIntervalTime); + } + + /** + * 判断是否双击 ( 无效点击 - 短时间内多次点击 ) + * @param tagId id + * @param intervalTime 双击间隔时间 + * @return {@code true} yes, {@code false} no + */ + public boolean isFastDoubleClick(final int tagId, final long intervalTime) { + long curTime = System.currentTimeMillis(); + long diffTime = curTime - mLastClickTime; + // 判断时间是否超过 + if (mLastTagId == tagId && mLastClickTime > 0 && diffTime < intervalTime) { + LogPrintUtils.dTag(TAG, "isFastDoubleClick 无效点击 tagId: " + tagId + ", intervalTime: " + intervalTime); + return true; + } + LogPrintUtils.dTag(TAG, "isFastDoubleClick 有效点击 tagId: " + tagId + ", intervalTime: " + intervalTime); + mLastTagId = tagId; + mLastClickTime = curTime; + return false; + } + + // = + + /** + * 判断是否双击 ( 无效点击 - 短时间内多次点击 ) + * @param object key by Object + * @param configKey 时间间隔配置 Key + * @return {@code true} yes, {@code false} no + */ + public boolean isFastDoubleClick(final Object object, final String configKey) { + return isFastDoubleClick(object, getConfigTime(configKey)); + } + + /** + * 判断是否双击 ( 无效点击 - 短时间内多次点击 ) + * @param object key by Object + * @param intervalTime 双击时间间隔 + * @return {@code true} yes, {@code false} no + */ + public boolean isFastDoubleClick(final Object object, final long intervalTime) { + // 获取 TAG + String tag = ((object != null) ? ("obj_" + object.hashCode()) : "obj_null"); + // 获取上次点击的时间 + Long lastTime = mRecordMaps.get(tag); + if (lastTime == null) { + lastTime = 0L; + } + long curTime = System.currentTimeMillis(); + long diffTime = curTime - lastTime; + // 判断时间是否超过 + if (lastTime > 0 && diffTime < intervalTime) { + LogPrintUtils.dTag(TAG, "isFastDoubleClick 无效点击 obj: " + object + ", intervalTime: " + intervalTime); + return true; + } + LogPrintUtils.dTag(TAG, "isFastDoubleClick 有效点击 obj: " + object + ", intervalTime: " + intervalTime); + // 保存上次点击时间 + mRecordMaps.put(tag, curTime); + return false; + } + + // = + + /** + * 初始化配置信息 + * @param mapConfigs config Maps + * @return {@link ClickAssist} + */ + public ClickAssist initConfig(final Map mapConfigs) { + if (mapConfigs != null) { + mConfigMaps.putAll(mapConfigs); + } + return this; + } + + /** + * 添加配置信息 + * @param key config Key + * @param value config Value + * @return {@link ClickAssist} + */ + public ClickAssist putConfig(final String key, final Long value) { + mConfigMaps.put(key, value); + return this; + } + + /** + * 移除配置信息 + * @param key config Key + * @return {@link ClickAssist} + */ + public ClickAssist removeConfig(final String key) { + mConfigMaps.remove(key); + return this; + } + + /** + * 获取配置时间 + * @param key config Key + * @return 配置时间 + */ + public Long getConfigTime(final String key) { + // 获取配置时间 + Long configTime = mConfigMaps.get(key); + // 判断是否为 null + return (configTime != null) ? configTime : mIntervalTime; + } + + // = + + /** + * 移除点击记录 + * @param key tag Key + * @return {@link ClickAssist} + */ + public ClickAssist removeRecord(final String key) { + mRecordMaps.remove(key); + return this; + } + + /** + * 清空全部点击记录 + * @return {@link ClickAssist} + */ + public ClickAssist clearRecord() { + mRecordMaps.clear(); + return this; + } + + // = + + /** + * 设置默认点击时间间隔 + * @param intervalTime 双击时间间隔 + * @return {@link ClickAssist} + */ + public ClickAssist setIntervalTime(final long intervalTime) { + this.mIntervalTime = intervalTime; + return this; + } + + /** + * 重置处理 + * @return {@link ClickAssist} + */ + public ClickAssist reset() { + // 重置最后一次点击的标识 id + mLastTagId = -1; + // 重置最后一次点击时间 + mLastClickTime = 0L; + // 重置双击间隔时间 + mIntervalTime = 1000L; + // 清空配置信息 + mConfigMaps.clear(); + // 清空点击记录 + mRecordMaps.clear(); + return this; + } + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/ClipboardUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ClipboardUtils.java similarity index 87% rename from DevLibUtils/src/main/java/dev/utils/app/ClipboardUtils.java rename to lib/DevApp/src/main/java/dev/utils/app/ClipboardUtils.java index 9dc5d825e5..3d67f58581 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ClipboardUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/app/ClipboardUtils.java @@ -11,19 +11,19 @@ /** * detail: 剪贴板相关工具类 - * Created by Ttt + * @author Ttt */ public final class ClipboardUtils { private ClipboardUtils() { } - // 日志TAG + // 日志 TAG private static final String TAG = ClipboardUtils.class.getSimpleName(); /** * 复制文本到剪贴板 - * @param text + * @param text 文本 */ public static void copyText(final CharSequence text) { try { @@ -32,14 +32,14 @@ public static void copyText(final CharSequence text) { ClipData clipData = ClipData.newPlainText("text", text); // 设置复制的数据 clipManager.setPrimaryClip(clipData); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "copyText"); } } /** - * 获取剪贴板的文本 - * @return 剪贴板的文本 + * 获取剪贴板文本 + * @return 剪贴板文本 */ public static CharSequence getText() { try { @@ -48,15 +48,15 @@ public static CharSequence getText() { if (clipData != null && clipData.getItemCount() > 0) { return clipData.getItemAt(0).coerceToText(DevUtils.getContext()); } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getText"); } return null; } /** - * 复制uri到剪贴板 - * @param uri + * 复制 URI 到剪贴板 + * @param uri {@link Uri} */ public static void copyUri(final Uri uri) { try { @@ -65,14 +65,14 @@ public static void copyUri(final Uri uri) { ClipData clipData = ClipData.newUri(DevUtils.getContext().getContentResolver(), "", uri); // 设置复制的数据 clipManager.setPrimaryClip(clipData); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "copyUri"); } } /** - * 获取剪贴板的uri - * @return 剪贴板的uri + * 获取剪贴板 URI + * @return 剪贴板 URI */ public static Uri getUri() { try { @@ -81,7 +81,7 @@ public static Uri getUri() { if (clipData != null && clipData.getItemCount() > 0) { return clipData.getItemAt(0).getUri(); } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getUri"); } return null; @@ -98,14 +98,14 @@ public static void copyIntent(final Intent intent) { ClipData clipData = ClipData.newIntent("intent", intent); // 设置复制的数据 clipManager.setPrimaryClip(clipData); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "copyIntent"); } } /** - * 获取剪贴板的意图 - * @return 剪贴板的意图 + * 获取剪贴板意图 + * @return 剪贴板意图 */ public static Intent getIntent() { try { @@ -114,9 +114,9 @@ public static Intent getIntent() { if (clipData != null && clipData.getItemCount() > 0) { return clipData.getItemAt(0).getIntent(); } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getIntent"); } return null; } -} +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/ContentResolverUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ContentResolverUtils.java similarity index 51% rename from DevLibUtils/src/main/java/dev/utils/app/ContentResolverUtils.java rename to lib/DevApp/src/main/java/dev/utils/app/ContentResolverUtils.java index ca8fcb7d5b..528b2bfc70 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ContentResolverUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/app/ContentResolverUtils.java @@ -14,34 +14,35 @@ /** * detail: ContentResolver 工具类 - * Created by Ttt + * @author Ttt + *
+ *     @see 
+ * 
*/ public final class ContentResolverUtils { - // https://blog.csdn.net/lemon_blue/article/details/52353851 - // https://www.cnblogs.com/Sharley/p/7942142.html - private ContentResolverUtils() { } - // 日志TAG + // 日志 TAG private static final String TAG = ContentResolverUtils.class.getSimpleName(); - // MediaStore.MediaColumns.MIME_TYPE, isVideo ? "video/3gp" : "image/jpeg" - /** * 通知刷新本地资源 - * @param file - * @return + *
+     *     注意事项: 部分手机 ( 如小米 ) 通知文件地址层级过深, 将会并入相册文件夹中
+     *     尽量放在 SDCrad/XXX/xx.jpg 层级中
+     * 
+ * @param file 文件 + * @return {@code true} success, {@code false} fail */ - public static boolean notifyMediaStore(File file){ - if (file != null){ + public static boolean notifyMediaStore(final File file) { + if (file != null) { try { - // 最后通知图库扫描更新 + // 通知图库扫描更新 DevUtils.getContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file))); - // 通知成功 return true; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "notifyMediaStore"); } } @@ -49,56 +50,61 @@ public static boolean notifyMediaStore(File file){ } /** - * 添加图片到系统相册(包含原图、相册图, 会存在两张) - 想要一张, 直接调用 notifyMediaStore() - * @param file - * @param fileName - * @param isNotify - * @return + * 添加图片到系统相册 ( 包含原图、相册图, 会存在两张 ) - 想要一张, 直接调用 notifyMediaStore() + * @param file 文件 + * @param fileName 文件名 + * @param isNotify 是否广播通知图库扫描 + * @return {@code true} success, {@code false} fail */ - public static boolean insertImageIntoMediaStore(File file, String fileName, boolean isNotify){ - if (file != null){ + public static boolean insertImageIntoMediaStore(final File file, final String fileName, final boolean isNotify) { + if (file != null) { try { // 添加到相册 - MediaStore.Images.Media.insertImage(DevUtils.getContext().getContentResolver(), file.getAbsolutePath(), TextUtils.isEmpty(fileName) ? file.getName() : fileName, null); - if (isNotify){ - notifyMediaStore(file); - } - } catch (Exception e){ + MediaStore.Images.Media.insertImage(DevUtils.getContext().getContentResolver(), + file.getAbsolutePath(), TextUtils.isEmpty(fileName) ? file.getName() : fileName, null); + // 通知图库扫描更新 + if (isNotify) notifyMediaStore(file); + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "insertImageIntoMediaStore"); } } return false; } + // = + /** * 添加视频到系统相册 - * @param file - * @return + * @param file 文件 + * @return {@code true} success, {@code false} fail */ - public static boolean insertVideoIntoMediaStore(File file){ + public static boolean insertVideoIntoMediaStore(final File file) { return insertIntoMediaStore(file, -1, true, "video/3gp"); } /** * 保存到系统相册 - * @param file - * @param createTime - * @param isVideo - * @param mimeType + * @param file 文件 + * @param createTime 创建时间 + * @param isVideo 是否视频 + * @param mimeType 资源类型 + * @return {@code true} success, {@code false} fail */ - public static boolean insertIntoMediaStore(File file, long createTime, boolean isVideo, String mimeType) { + public static boolean insertIntoMediaStore(final File file, final long createTime, final boolean isVideo, final String mimeType) { if (file != null && !TextUtils.isEmpty(mimeType)) { try { - ContentResolver mContentResolver = DevUtils.getContext().getContentResolver(); - // 防止创建时间为null - if (createTime <= 0) - createTime = System.currentTimeMillis(); + ContentResolver resolver = DevUtils.getContext().getContentResolver(); + // 插入时间 + long insertTime = createTime; + // 防止创建时间为 null + if (insertTime <= 0) + insertTime = System.currentTimeMillis(); ContentValues values = new ContentValues(); values.put(MediaStore.MediaColumns.TITLE, file.getName()); values.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName()); - // 值一样,但是还是用常量区分对待 - values.put(isVideo ? MediaStore.Video.VideoColumns.DATE_TAKEN : MediaStore.Images.ImageColumns.DATE_TAKEN, createTime); + // 值一样, 但是还是用常量区分对待 + values.put(isVideo ? MediaStore.Video.VideoColumns.DATE_TAKEN : MediaStore.Images.ImageColumns.DATE_TAKEN, insertTime); values.put(MediaStore.MediaColumns.DATE_MODIFIED, System.currentTimeMillis()); values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis()); if (!isVideo) @@ -109,11 +115,10 @@ public static boolean insertIntoMediaStore(File file, long createTime, boolean i values.put(MediaStore.MediaColumns.SIZE, file.length()); // 文件类型 values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType); - // 生成所属的URI资源 - Uri uri = mContentResolver.insert(isVideo ? MediaStore.Video.Media.EXTERNAL_CONTENT_URI : MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + // 生成所属的 URI 资源 + Uri uri = resolver.insert(isVideo ? MediaStore.Video.Media.EXTERNAL_CONTENT_URI : MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); // 最后通知图库更新 DevUtils.getContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)); - // 表示成功 return true; } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "insertIntoMediaStore"); @@ -121,4 +126,4 @@ public static boolean insertIntoMediaStore(File file, long createTime, boolean i } return false; } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/DBUtils.java b/lib/DevApp/src/main/java/dev/utils/app/DBUtils.java new file mode 100644 index 0000000000..f417533bf8 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/DBUtils.java @@ -0,0 +1,109 @@ +package dev.utils.app; + +import android.os.Environment; + +import java.io.InputStream; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.common.FileUtils; + +/** + * detail: 数据库工具类 ( 导入导出等 ) + * @author Ttt + */ +public final class DBUtils { + + private DBUtils() { + } + + // 日志 TAG + private static final String TAG = DBUtils.class.getSimpleName(); + + /** + * 导出数据库 + * @param targetFile 目标文件 + * @param dbName 数据库名 + * @return {@code true} success, {@code false} fail + */ + public static boolean startExportDatabase(final String targetFile, final String dbName) { + if (!isSDCardEnable()) return false; + try { + // Database 文件地址 + String sourceFilePath = getDBPath() + dbName; + // 复制文件 + return FileUtils.copyFile(sourceFilePath, targetFile, true); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startExportDatabase"); + } + return false; + } + + // = + + /** + * 导入数据库 + * @param srcFilePath 待复制的文件地址 + * @param destFilePath 目标文件地址 + * @return {@code true} success, {@code false} fail + */ + public static boolean startImportDatabase(final String srcFilePath, final String destFilePath) { + if (!isSDCardEnable()) return false; + try { + // 复制文件 + return FileUtils.copyFile(srcFilePath, destFilePath, true); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startImportDatabase"); + } + return false; + } + + /** + * 导入数据库 + * @param inputStream 文件流 ( 被复制 ) + * @param destFilePath 目标文件地址 + * @return {@code true} success, {@code false} fail + */ + public static boolean startImportDatabase(final InputStream inputStream, final String destFilePath) { + if (!isSDCardEnable()) return false; + try { + // 复制文件 + return FileUtils.copyFile(inputStream, destFilePath, true); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startImportDatabase"); + } + return false; + } + + // = + + /** + * 获取数据库路径 + * @return 数据库路径 + */ + public static String getDBPath() { + try { + // Database 文件地址 + return Environment.getDataDirectory() + "/data/" + DevUtils.getContext().getPackageName() + "/databases/"; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDBPath"); + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = SDCardUtils = + // =============== + + /** + * 判断内置 SDCard 是否正常挂载 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSDCardEnable() { + return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/DeviceUtils.java b/lib/DevApp/src/main/java/dev/utils/app/DeviceUtils.java new file mode 100644 index 0000000000..359c250b6f --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/DeviceUtils.java @@ -0,0 +1,701 @@ +package dev.utils.app; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.os.PowerManager; +import android.provider.Settings; +import android.support.annotation.RequiresApi; +import android.support.annotation.RequiresPermission; +import android.text.TextUtils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 设备相关工具类 + * @author Ttt + *
+ *     @see 
+ *     android.os.Build.BOARD: 获取设备基板名称
+ *     android.os.Build.BOOTLOADER: 获取设备引导程序版本号
+ *     android.os.Build.BRAND: 获取设备品牌
+ *     android.os.Build.CPU_ABI: 获取设备指令集名称 (CPU 的类型 )
+ *     android.os.Build.CPU_ABI2: 获取第二个指令集名称
+ *     android.os.Build.DEVICE: 获取设备驱动名称
+ *     android.os.Build.DISPLAY: 获取设备显示的版本包 ( 在系统设置中显示为版本号 ) 和 ID 一样
+ *     android.os.Build.FINGERPRINT: 设备的唯一标识, 由设备的多个信息拼接合成
+ *     android.os.Build.HARDWARE: 设备硬件名称, 一般和基板名称一样 (BOARD)
+ *     android.os.Build.HOST: 设备主机地址
+ *     android.os.Build.ID: 设备版本号
+ *     android.os.Build.MODEL : 获取手机的型号 设备名称
+ *     android.os.Build.MANUFACTURER: 获取设备制造商
+ *     android:os.Build.PRODUCT: 整个产品的名称
+ *     android:os.Build.RADIO: 无线电固件版本号, 通常是不可用的 显示 unknown
+ *     android.os.Build.TAGS: 设备标签, 如 release-keys 或测试的 test-keys
+ *     android.os.Build.TIME: 时间
+ *     android.os.Build.TYPE: 设备版本类型 主要为 "user" 或 "eng".
+ *     android.os.Build.USER: 设备用户名 基本上都为 android-build
+ *     android.os.Build.VERSION.RELEASE: 获取系统版本字符串, 如 4.1.2 或 2.2 或 2.3 等
+ *     android.os.Build.VERSION.CODENAME: 设备当前的系统开发代号, 一般使用 REL 代替
+ *     android.os.Build.VERSION.INCREMENTAL: 系统源代码控制值, 一个数字或者 git hash 值
+ *     android.os.Build.VERSION.SDK: 系统的 API 级别 一般使用下面大的 SDK_INT 来查看
+ *     android.os.Build.VERSION.SDK_INT: 系统的 API 级别 数字表示
+ *     

+ * 所需权限 + * + * + *
+ */ +public final class DeviceUtils { + + private DeviceUtils() { + } + + // 日志 TAG + private static final String TAG = DeviceUtils.class.getSimpleName(); + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + + /** + * 获取设备信息 + * @param deviceInfoMap 设备信息 Map + */ + public static void getDeviceInfo(final Map deviceInfoMap) { + // 获取设备信息类的所有申明的字段, 即包括 public、private 和 proteced, 但是不包括父类的申明字段 + Field[] fields = Build.class.getDeclaredFields(); + // 遍历字段 + for (Field field : fields) { + try { + // 取消 Java 的权限控制检查 + field.setAccessible(true); + // 转换当前设备支持的 ABI - CPU 指令集 + if (field.getName().toLowerCase().startsWith("SUPPORTED".toLowerCase())) { + try { + Object object = field.get(null); + // 判断是否数组 + if (object instanceof String[]) { + if (object != null) { + // 获取类型对应字段的数据, 并保存支持的指令集 [arm64-v8a, armeabi-v7a, armeabi] + deviceInfoMap.put(field.getName(), Arrays.toString((String[]) object)); + } + continue; + } + } catch (Exception e) { + } + } + // 获取类型对应字段的数据, 并保存 + deviceInfoMap.put(field.getName(), field.get(null).toString()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); + } + } + } + + /** + * 处理设备信息 + * @param deviceInfoMap 设备信息 Map + * @param errorInfo 错误提示信息, 如获取设备信息失败 + * @return 拼接后的设备信息字符串 + */ + public static String handlerDeviceInfo(final Map deviceInfoMap, final String errorInfo) { + try { + // 初始化 Builder, 拼接字符串 + StringBuilder builder = new StringBuilder(); + // 获取设备信息 + Iterator> mapIter = deviceInfoMap.entrySet().iterator(); + // 遍历设备信息 + while (mapIter.hasNext()) { + // 获取对应的 key - value + Map.Entry rnEntry = mapIter.next(); + String rnKey = rnEntry.getKey(); // key + String rnValue = rnEntry.getValue(); // value + // 保存设备信息 + builder.append(rnKey); + builder.append(" = "); + builder.append(rnValue); + builder.append(NEW_LINE_STR); + } + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "handlerDeviceInfo"); + } + return errorInfo; + } + + // = + + /** + * 获取设备基板名称 + * @return 设备基板名称 + */ + public static String getBoard() { + return Build.BOARD; + } + + /** + * 获取设备引导程序版本号 + * @return 设备引导程序版本号 + */ + public static String getBootloader() { + return Build.BOOTLOADER; + } + + /** + * 获取设备品牌 + * @return 设备品牌 + */ + public static String getBrand() { + return Build.BRAND; + } + + /** + * 获取支持的第一个指令集 + * @return 支持的第一个指令集 + */ + public static String getCPU_ABI() { + return Build.CPU_ABI; + } + + /** + * 获取支持的第二个指令集 + * @return 支持的第二个指令集 + */ + public static String getCPU_ABI2() { + return Build.CPU_ABI2; + } + + /** + * 获取支持的指令集 如: [arm64-v8a, armeabi-v7a, armeabi] + * @return 支持的指令集 + */ + public static String[] getABIs() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return Build.SUPPORTED_ABIS; + } else { + if (!TextUtils.isEmpty(Build.CPU_ABI2)) { + return new String[]{Build.CPU_ABI, Build.CPU_ABI2}; + } + return new String[]{Build.CPU_ABI}; + } + } + + /** + * 获取支持的 32 位指令集 + * @return 支持的 32 位指令集 + */ + public static String[] getSUPPORTED_32_BIT_ABIS() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return Build.SUPPORTED_32_BIT_ABIS; + } + return null; + } + + /** + * 获取支持的 64 位指令集 + * @return 支持的 64 位指令集 + */ + public static String[] getSUPPORTED_64_BIT_ABIS() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return Build.SUPPORTED_64_BIT_ABIS; + } + return null; + } + + /** + * 获取设备驱动名称 + * @return 设备驱动名称 + */ + public static String getDevice() { + return Build.DEVICE; + } + + /** + * 获取设备显示的版本包 ( 在系统设置中显示为版本号 ) 和 ID 一样 + * @return 设备显示的版本包 + */ + public static String getDisplay() { + return Build.DISPLAY; + } + + /** + * 获取设备的唯一标识, 由设备的多个信息拼接合成 + * @return 设备的唯一标识, 由设备的多个信息拼接合成 + */ + public static String getFingerprint() { + return Build.FINGERPRINT; + } + + /** + * 获取设备硬件名称, 一般和基板名称一样 (BOARD) + * @return 设备硬件名称, 一般和基板名称一样 (BOARD) + */ + public static String getHardware() { + return Build.HARDWARE; + } + + /** + * 获取设备主机地址 + * @return 设备主机地址 + */ + public static String getHost() { + return Build.HOST; + } + + /** + * 获取设备版本号 + * @return 设备版本号 + */ + public static String getID() { + return Build.ID; + } + + /** + * 获取设备型号 如 RedmiNote4X + * @return 设备型号 + */ + public static String getModel() { + String model = Build.MODEL; + if (model != null) { + model = model.trim().replaceAll("\\s*", ""); + } else { + model = ""; + } + return model; + } + + /** + * 获取设备厂商 如 Xiaomi + * @return 设备厂商 + */ + public static String getManufacturer() { + return Build.MANUFACTURER; + } + + /** + * 获取整个产品的名称 + * @return 整个产品的名称 + */ + public static String getProduct() { + return Build.PRODUCT; + } + + /** + * 获取无线电固件版本号, 通常是不可用的 显示 unknown + * @return 无线电固件版本号 + */ + public static String getRadio() { + return Build.RADIO; + } + + /** + * 获取设备标签, 如 release-keys 或测试的 test-keys + * @return 设备标签 + */ + public static String getTags() { + return Build.TAGS; + } + + /** + * 获取设备时间 + * @return 设备时间 + */ + public static long getTime() { + return Build.TIME; + } + + /** + * 获取设备版本类型 主要为 "user" 或 "eng". + * @return 设备版本类型 + */ + public static String getType() { + return Build.TYPE; + } + + /** + * 获取设备用户名 基本上都为 android-build + * @return 设备用户名 + */ + public static String getUser() { + return Build.USER; + } + + // = + + /** + * 获取 SDK 版本号 + * @return SDK 版本号 + */ + public static int getSDKVersion() { + return Build.VERSION.SDK_INT; + } + + /** + * 获取系统版本号, 如 4.1.2 或 2.2 或 2.3 等 + * @return 系统版本号 + */ + public static String getRelease() { + return Build.VERSION.RELEASE; + } + + /** + * 获取设备当前的系统开发代号, 一般使用 REL 代替 + * @return 设备当前的系统开发代号 + */ + public static String getCodename() { + return Build.VERSION.CODENAME; + } + + /** + * 获取系统源代码控制值, 一个数字或者 git hash 值 + * @return 系统源代码控制值 + */ + public static String getIncremental() { + return Build.VERSION.INCREMENTAL; + } + + /** + * 获取 Android id + *
+     *     在设备首次启动时, 系统会随机生成一个 64 位的数字, 并把这个数字以十六进制字符串的形式保存下来,
+     *     这个十六进制的字符串就是 ANDROID_ID, 当设备被 wipe 后该值会被重置
+     * 
+ * @return Android id + */ + public static String getAndroidId() { + String androidId = null; + try { + androidId = Settings.Secure.getString(DevUtils.getContext().getContentResolver(), Settings.Secure.ANDROID_ID); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAndroidId"); + } + return androidId; + } + + /** + * 获取基带版本 BASEBAND-VER + * @return 基带版本 BASEBAND-VER + */ + public static String getBaseband_Ver() { + String Version = ""; + try { + Class cl = Class.forName("android.os.SystemProperties"); + Object invoker = cl.newInstance(); + Method m = cl.getMethod("get", String.class, String.class); + Object result = m.invoke(invoker, "gsm.version.baseband", "no message"); + Version = (String) result; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBaseband_Ver"); + } + return Version; + } + + /** + * 获取内核版本 CORE-VER + * @return 内核版本 CORE-VER + */ + public static String getLinuxCore_Ver() { + String kernelVersion = ""; + try { + Process process = null; + try { + process = Runtime.getRuntime().exec("cat /proc/version"); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLinuxCore_Ver - Process"); + } + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr, 8 * 1024); + + String line; + StringBuilder builder = new StringBuilder(); + try { + while ((line = br.readLine()) != null) { + builder.append(line); + } + } catch (IOException e) { + LogPrintUtils.eTag(TAG, e, "getLinuxCore_Ver - readLine"); + } + String result = builder.toString(); + try { + if (result != "") { + String Keyword = "version "; + int index = result.indexOf(Keyword); + line = result.substring(index + Keyword.length()); + index = line.indexOf(" "); + kernelVersion = line.substring(0, index); + } + } catch (IndexOutOfBoundsException e) { + LogPrintUtils.eTag(TAG, e, "getLinuxCore_Ver - substring"); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLinuxCore_Ver"); + } + return kernelVersion; + } + + // = + + /** + * 判断设备是否 root + * @return {@code true} yes, {@code false} no + */ + public static boolean isDeviceRooted() { + String su = "su"; + String[] locations = {"/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/", + "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/"}; + for (String location : locations) { + if (new File(location + su).exists()) { + return true; + } + } + return false; + } + + /** + * 获取是否启用 ADB + * @return {@code true} yes, {@code false} no + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) + public static boolean isAdbEnabled() { + try { + return Settings.Secure.getInt(DevUtils.getContext().getContentResolver(), Settings.Global.ADB_ENABLED, 0) > 0; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isAdbEnabled"); + } + return false; + } + + // = + + // Default MAC address reported to a client that does not have the android.permission.LOCAL_MAC_ADDRESS permission. + private static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00"; + + /** + * 获取设备 MAC 地址 + *
+     *     没有打开 wifi, 则获取 WLAN MAC 地址失败
+     * 
+ * @return 设备 MAC 地址 + */ + @RequiresPermission(allOf = {android.Manifest.permission.INTERNET, android.Manifest.permission.ACCESS_WIFI_STATE}) + public static String getMacAddress() { + String macAddress = getMacAddressByWifiInfo(); + if (!DEFAULT_MAC_ADDRESS.equals(macAddress)) { + return macAddress; + } + macAddress = getMacAddressByNetworkInterface(); + if (!DEFAULT_MAC_ADDRESS.equals(macAddress)) { + return macAddress; + } + macAddress = getMacAddressByInetAddress(); + if (!DEFAULT_MAC_ADDRESS.equals(macAddress)) { + return macAddress; + } + macAddress = getMacAddressByFile(); + if (!DEFAULT_MAC_ADDRESS.equals(macAddress)) { + return macAddress; + } + return null; + } + + /** + * 获取 MAC 地址 + * @return MAC 地址 + */ + @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) + private static String getMacAddressByWifiInfo() { + try { + @SuppressLint("WifiManagerLeak") + WifiManager wifiManager = (WifiManager) DevUtils.getContext().getSystemService(Context.WIFI_SERVICE); + if (wifiManager != null) { + WifiInfo wifiInfo = wifiManager.getConnectionInfo(); + if (wifiInfo != null) return wifiInfo.getMacAddress(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMacAddressByWifiInfo"); + } + return DEFAULT_MAC_ADDRESS; + } + + /** + * 获取 MAC 地址 + * @return MAC 地址 + */ + @RequiresPermission(android.Manifest.permission.INTERNET) + private static String getMacAddressByNetworkInterface() { + try { + Enumeration nis = NetworkInterface.getNetworkInterfaces(); + while (nis.hasMoreElements()) { + NetworkInterface ni = nis.nextElement(); + if (ni == null || !ni.getName().equalsIgnoreCase("wlan0")) continue; + byte[] macBytes = ni.getHardwareAddress(); + if (macBytes != null && macBytes.length > 0) { + StringBuilder builder = new StringBuilder(); + for (byte b : macBytes) { + builder.append(String.format("%02x:", b)); + } + return builder.substring(0, builder.length() - 1); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMacAddressByNetworkInterface"); + } + return DEFAULT_MAC_ADDRESS; + } + + /** + * 通过 InetAddress 获取 Mac 地址 + * @return Mac 地址 + */ + private static String getMacAddressByInetAddress() { + try { + InetAddress inetAddress = getInetAddress(); + if (inetAddress != null) { + NetworkInterface ni = NetworkInterface.getByInetAddress(inetAddress); + if (ni != null) { + byte[] macBytes = ni.getHardwareAddress(); + if (macBytes != null && macBytes.length > 0) { + StringBuilder builder = new StringBuilder(); + for (byte b : macBytes) { + builder.append(String.format("%02x:", b)); + } + return builder.substring(0, builder.length() - 1); + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMacAddressByInetAddress"); + } + return DEFAULT_MAC_ADDRESS; + } + + /** + * 获取 InetAddress + * @return {@link InetAddress} + */ + private static InetAddress getInetAddress() { + try { + Enumeration nis = NetworkInterface.getNetworkInterfaces(); + while (nis.hasMoreElements()) { + NetworkInterface ni = nis.nextElement(); + // To prevent phone of xiaomi return "10.0.2.15" + if (!ni.isUp()) continue; + Enumeration addresses = ni.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress inetAddress = addresses.nextElement(); + if (!inetAddress.isLoopbackAddress()) { + String hostAddress = inetAddress.getHostAddress(); + if (hostAddress.indexOf(':') < 0) return inetAddress; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getInetAddress"); + } + return null; + } + + /** + * 获取 MAC 地址 + * @return MAC 地址 + */ + private static String getMacAddressByFile() { + ShellUtils.CommandResult result = ShellUtils.execCmd("getprop wifi.interface", false); + if (result.isSuccess()) { + String name = result.successMsg; + if (name != null) { + result = ShellUtils.execCmd("cat /sys/class/net/" + name + "/address", false); + if (result.result == 0) { + String address = result.successMsg; + if (address != null && address.length() > 0) { + return address; + } + } + } + } + return DEFAULT_MAC_ADDRESS; + } + + // = + + /** + * 关机 ( 需要 root 权限 ) + */ + public static void shutdown() { + try { + ShellUtils.execCmd("reboot -p", true); + Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN"); + intent.putExtra("android.intent.extra.KEY_CONFIRM", false); + DevUtils.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "shutdown"); + } + } + + /** + * 重启设备 ( 需要 root 权限 ) + */ + public static void reboot() { + try { + ShellUtils.execCmd("reboot", true); + Intent intent = new Intent(Intent.ACTION_REBOOT); + intent.putExtra("nowait", 1); + intent.putExtra("interval", 1); + intent.putExtra("window", 0); + DevUtils.getContext().sendBroadcast(intent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "reboot"); + } + } + + /** + * 重启设备 ( 需要 root 权限 ) - 并进行特殊的引导模式 (recovery、Fastboot) + * @param reason 传递给内核来请求特殊的引导模式, 如 "recovery" + * 重启到 Fastboot 模式 bootloader + */ + public static void reboot(final String reason) { + try { + PowerManager mPowerManager = (PowerManager) DevUtils.getContext().getSystemService(Context.POWER_SERVICE); + if (mPowerManager == null) return; + mPowerManager.reboot(reason); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "reboot"); + } + } + + /** + * 重启引导到 recovery ( 需要 root 权限 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean rebootToRecovery() { + ShellUtils.CommandResult result = ShellUtils.execCmd("reboot recovery", true); + return result.isSuccess2(); + } + + /** + * 重启引导到 bootloader ( 需要 root 权限 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean rebootToBootloader() { + ShellUtils.CommandResult result = ShellUtils.execCmd("reboot bootloader", true); + return result.isSuccess2(); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/DialogUtils.java b/lib/DevApp/src/main/java/dev/utils/app/DialogUtils.java new file mode 100644 index 0000000000..8a70388db9 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/DialogUtils.java @@ -0,0 +1,1565 @@ +package dev.utils.app; + +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.support.annotation.ArrayRes; +import android.support.annotation.StyleRes; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.PopupWindow; + +import dev.utils.LogPrintUtils; + +/** + * detail: Dialog 操作相关工具类 + * @author Ttt + */ +public final class DialogUtils { + + private DialogUtils() { + } + + // 日志 TAG + private static final String TAG = DialogUtils.class.getSimpleName(); + + // =============== + // = Dialog 相关 = + // =============== + + /** + * 获取 Dialog Window + * @param dialog {@link Dialog} + * @return {@link Window} + */ + public static Window getWindow(final Dialog dialog) { + return (dialog != null) ? dialog.getWindow() : null; + } + + /** + * 获取 Dialog Window LayoutParams + * @param dialog {@link Dialog} + * @return {@link WindowManager.LayoutParams} + */ + public static WindowManager.LayoutParams getAttributes(final Dialog dialog) { + if (dialog != null) { + Window window = dialog.getWindow(); + if (window != null) return window.getAttributes(); + } + return null; + } + + /** + * 设置 Dialog Window LayoutParams + * @param dialog {@link Dialog} + * @param params {@link WindowManager.LayoutParams} + * @param 泛型 + * @return {@link Dialog} + */ + public static T setAttributes(final T dialog, final WindowManager.LayoutParams params) { + if (dialog != null && params != null) { + Window window = dialog.getWindow(); + if (window != null) window.setAttributes(params); + } + return dialog; + } + + // = + + /** + * 设置 Dialog 宽度 + * @param dialog {@link Dialog} + * @param width 宽度 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setWidth(final T dialog, final int width) { + return setWidthHeight(dialog, width, Integer.MIN_VALUE); + } + + /** + * 设置 Dialog 高度 + * @param dialog {@link Dialog} + * @param height 高度 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setHeight(final T dialog, final int height) { + return setWidthHeight(dialog, Integer.MIN_VALUE, height); + } + + /** + * 设置 Dialog 宽度、高度 + * @param dialog {@link Dialog} + * @param width 宽度 + * @param height 高度 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setWidthHeight(final T dialog, final int width, final int height) { + WindowManager.LayoutParams layoutParams = getAttributes(dialog); + if (layoutParams != null) { + if (width != Integer.MIN_VALUE) { + layoutParams.width = width; + } + if (height != Integer.MIN_VALUE) { + layoutParams.height = height; + } + setAttributes(dialog, layoutParams); + } + return dialog; + } + + // = + + /** + * 设置 Dialog X 轴坐标 + * @param dialog {@link Dialog} + * @param x X 轴坐标 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setX(final T dialog, final int x) { + return setXY(dialog, x, Integer.MIN_VALUE); + } + + /** + * 设置 Dialog Y 轴坐标 + * @param dialog {@link Dialog} + * @param y Y 轴坐标 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setY(final T dialog, final int y) { + return setXY(dialog, Integer.MIN_VALUE, y); + } + + /** + * 设置 Dialog X、Y 轴坐标 + * @param dialog {@link Dialog} + * @param x X 轴坐标 + * @param y Y 轴坐标 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setXY(final T dialog, final int x, final int y) { + WindowManager.LayoutParams layoutParams = getAttributes(dialog); + if (layoutParams != null) { + if (x != Integer.MIN_VALUE) { + layoutParams.x = x; + } + if (y != Integer.MIN_VALUE) { + layoutParams.y = y; + } + setAttributes(dialog, layoutParams); + } + return dialog; + } + + // = + + /** + * 设置 Dialog Gravity + * @param dialog {@link Dialog} + * @param gravity 重心 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setGravity(final T dialog, final int gravity) { + WindowManager.LayoutParams layoutParams = getAttributes(dialog); + if (layoutParams != null) { + layoutParams.gravity = gravity; + setAttributes(dialog, layoutParams); + } + return dialog; + } + + /** + * 设置 Dialog 透明度 + * @param dialog {@link Dialog} + * @param dimAmount 透明度 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setDimAmount(final T dialog, final float dimAmount) { + WindowManager.LayoutParams layoutParams = getAttributes(dialog); + if (layoutParams != null) { + layoutParams.dimAmount = dimAmount; + setAttributes(dialog, layoutParams); + } + return dialog; + } + + // = + + /** + * 设置是否允许返回键关闭 + * @param dialog {@link Dialog} + * @param cancel {@code true} 允许, {@code false} 不允许 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setCancelable(final T dialog, final boolean cancel) { + if (dialog != null) { + // 返回键关闭 + dialog.setCancelable(cancel); + } + return dialog; + } + + /** + * 设置是否允许点击其他地方自动关闭 + * @param dialog {@link Dialog} + * @param cancel {@code true} 允许, {@code false} 不允许 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setCanceledOnTouchOutside(final T dialog, final boolean cancel) { + if (dialog != null) { + // 点击其他地方自动关闭 + dialog.setCanceledOnTouchOutside(cancel); + } + return dialog; + } + + /** + * 设置是否允许 返回键关闭、点击其他地方自动关闭 + * @param dialog {@link Dialog} + * @param cancel {@code true} 允许, {@code false} 不允许 + * @param 泛型 + * @return {@link Dialog} + */ + public static T setCancelableAndTouchOutside(final T dialog, final boolean cancel) { + if (dialog != null) { + // 返回键关闭 + dialog.setCancelable(cancel); + // 点击其他地方自动关闭 + dialog.setCanceledOnTouchOutside(cancel); + } + return dialog; + } + + // =============== + // = Dialog 操作 = + // =============== + + /** + * 显示 Dialog + * @param dialog {@link Dialog} + * @param 泛型 + * @return {@link Dialog} + */ + public static T showDialog(final T dialog) { + if (dialog != null && !dialog.isShowing()) { + try { + dialog.show(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "showDialog"); + } + } + return dialog; + } + + /** + * 关闭 Dialog + * @param dialog {@link Dialog} + */ + public static void closeDialog(final Dialog dialog) { + if (dialog != null && dialog.isShowing()) { + try { + dialog.dismiss(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeDialog"); + } + } + } + + /** + * 关闭多个 Dialog + * @param dialogs {@link Dialog} 数组 + */ + public static void closeDialogs(final Dialog... dialogs) { + if (dialogs != null && dialogs.length != 0) { + for (int i = 0, len = dialogs.length; i < len; i++) { + Dialog dialog = dialogs[i]; + if (dialog != null && dialog.isShowing()) { + try { + dialog.dismiss(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeDialogs"); + } + } + } + } + } + + // = + + /** + * 关闭 DialogFragment + * @param dialog {@link DialogFragment} + */ + public static void closeDialog(final DialogFragment dialog) { + if (dialog != null) { + try { + dialog.dismiss(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeDialog"); + } + } + } + + /** + * 关闭多个 DialogFragment + * @param dialogs {@link DialogFragment} 数组 + */ + public static void closeDialogs(final DialogFragment... dialogs) { + if (dialogs != null && dialogs.length != 0) { + for (int i = 0, len = dialogs.length; i < len; i++) { + DialogFragment dialog = dialogs[i]; + if (dialog != null) { + try { + dialog.dismiss(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeDialogs"); + } + } + } + } + } + + // = + + /** + * 关闭 PopupWindow + * @param popupWindow {@link PopupWindow} + */ + public static void closePopupWindow(final PopupWindow popupWindow) { + if (popupWindow != null && popupWindow.isShowing()) { + try { + popupWindow.dismiss(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closePopupWindow"); + } + } + } + + /** + * 关闭多个 PopupWindow + * @param popupWindows {@link PopupWindow} 数组 + */ + public static void closePopupWindows(final PopupWindow... popupWindows) { + if (popupWindows != null && popupWindows.length != 0) { + for (int i = 0, len = popupWindows.length; i < len; i++) { + PopupWindow popupWindow = popupWindows[i]; + if (popupWindow != null && popupWindow.isShowing()) { + try { + popupWindow.dismiss(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closePopupWindows"); + } + } + } + } + } + + // = + + /** + * detail: Dialog 事件 + * @author Ttt + */ + public static abstract class DialogListener { + + /** + * 最左边按钮点击事件 + * @param dialog {@link DialogInterface} + */ + public void onLeftButton(DialogInterface dialog) { + } + + /** + * 最右边按钮点击事件 + * @param dialog {@link DialogInterface} + */ + public abstract void onRightButton(DialogInterface dialog); + + /** + * 关闭通知 + * @param dialog {@link DialogInterface} + */ + public void onDismiss(DialogInterface dialog) { + } + } + + /** + * 创建提示 Dialog ( 原生样式 ) + * @param context {@link Context} + * @param title dialog 标题 + * @param content dialog 内容 + * @param rightBtn 右边按钮文案 + * @return {@link AlertDialog} + */ + public static AlertDialog createAlertDialog(final Context context, final String title, final String content, final String rightBtn) { + return createAlertDialog(context, title, content, null, rightBtn, null); + } + + /** + * 创建提示 Dialog ( 原生样式 ) + * @param context {@link Context} + * @param title dialog 标题 + * @param content dialog 内容 + * @param rightBtn 右边按钮文案 + * @param dialogListener 事件通知 + * @return {@link AlertDialog} + */ + public static AlertDialog createAlertDialog(final Context context, final String title, final String content, + final String rightBtn, final DialogListener dialogListener) { + return createAlertDialog(context, title, content, null, rightBtn, dialogListener); + } + + /** + * 创建提示 Dialog ( 原生样式 ) + * @param context {@link Context} + * @param title dialog 标题 + * @param content dialog 内容 + * @param leftBtn 左边按钮文案 + * @param rightBtn 右边按钮文案 + * @return {@link AlertDialog} + */ + public static AlertDialog createAlertDialog(final Context context, final String title, final String content, + final String leftBtn, final String rightBtn) { + return createAlertDialog(context, title, content, leftBtn, rightBtn, null); + } + + /** + * 创建提示 Dialog ( 原生样式 ) + * @param context {@link Context} + * @param title dialog 标题 + * @param content dialog 内容 + * @param leftBtn 左边按钮文案 + * @param rightBtn 右边按钮文案 + * @param dialogListener 事件通知 + * @return {@link AlertDialog} + */ + public static AlertDialog createAlertDialog(final Context context, final String title, final String content, + final String leftBtn, final String rightBtn, final DialogListener dialogListener) { + try { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(title); + builder.setMessage(content); + + if (leftBtn != null) { + if (dialogListener == null) { + builder.setNegativeButton(leftBtn, null); + } else { + builder.setNegativeButton(leftBtn, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (dialogListener != null) { + dialogListener.onLeftButton(dialog); + } + } + }); + } + } + + if (rightBtn != null) { + if (dialogListener == null) { + builder.setPositiveButton(rightBtn, null); + } else { + builder.setPositiveButton(rightBtn, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (dialogListener != null) { + dialogListener.onRightButton(dialog); + } + } + }); + } + } + + if (dialogListener != null) { + // 设置 Dialog 关闭监听 + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (dialogListener != null) { + dialogListener.onDismiss(dialog); + } + } + }); + } + return builder.create(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createAlertDialog"); + } + return null; + } + + // = + + /** + * 创建加载中 Dialog ( 原生样式 ) + * @param context {@link Context} + * @param title dialog 标题 + * @param content dialog 内容 + * @return {@link ProgressDialog} + */ + public static ProgressDialog createProgressDialog(final Context context, final String title, final String content) { + return createProgressDialog(context, title, content, false, null); + } + + /** + * 创建加载中 Dialog ( 原生样式 ) + * @param context {@link Context} + * @param title dialog 标题 + * @param content dialog 内容 + * @param isCancel 是否可以返回键关闭 + * @return {@link ProgressDialog} + */ + public static ProgressDialog createProgressDialog(final Context context, final String title, final String content, final boolean isCancel) { + return createProgressDialog(context, title, content, isCancel, null); + } + + /** + * 创建加载中 Dialog ( 原生样式 ) + * @param context {@link Context} + * @param title dialog 标题 + * @param content dialog 内容 + * @param isCancel 是否可以返回键关闭 + * @param cancelListener 取消事件 + * @return {@link ProgressDialog} + */ + public static ProgressDialog createProgressDialog(final Context context, final String title, final String content, final boolean isCancel, + final DialogInterface.OnCancelListener cancelListener) { + try { + ProgressDialog dialog = new ProgressDialog(context); + dialog.setTitle(title); + dialog.setMessage(content); + dialog.setIndeterminate(false); + dialog.setCancelable(isCancel); + dialog.setOnCancelListener(cancelListener); + return dialog; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createProgressDialog"); + } + return null; + } + + // = + + /** + * 自动关闭 dialog + * @param dialog {@link Dialog} + * @param delayMillis 延迟关闭时间 + * @param handler {@link Handler} + * @param 泛型 + * @return {@link Dialog} + */ + public static T autoCloseDialog(final T dialog, final long delayMillis, final Handler handler) { + if (dialog != null && dialog.isShowing()) { + if (handler != null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + try { + if (dialog != null && dialog.isShowing()) { + dialog.dismiss(); + } + } catch (Exception e) { + } + } + }, delayMillis); + } + } + return dialog; + } + + /** + * 自动关闭 DialogFragment + * @param dialog {@link DialogFragment} + * @param delayMillis 延迟关闭时间 + * @param handler {@link Handler} + * @param 泛型 + * @return {@link Dialog} + */ + public static T autoCloseDialog(final T dialog, final long delayMillis, final Handler handler) { + if (dialog != null) { + if (handler != null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + try { + if (dialog != null) { + dialog.dismiss(); + } + } catch (Exception e) { + } + } + }, delayMillis); + } + } + return dialog; + } + + /** + * 自动关闭 PopupWindow + * @param popupWindow {@link PopupWindow} + * @param delayMillis 延迟关闭时间 + * @param handler {@link Handler} + * @param 泛型 + * @return {@link PopupWindow} + */ + public static T autoClosePopupWindow(final T popupWindow, final long delayMillis, final Handler handler) { + if (popupWindow != null && popupWindow.isShowing()) { + if (handler != null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + try { + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(); + } + } catch (Exception e) { + } + } + }, delayMillis); + } + } + return popupWindow; + } + + // =================== + // = 单选列表 Dialog = + // =================== + + /** + * detail: Dialog 单选事件 + * @author Ttt + */ + public static abstract class SingleChoiceListener { + + /** + * 单选选中触发 + * @param dialog {@link DialogInterface} + * @param which 选中索引 + */ + public void onSingleChoiceItems(DialogInterface dialog, int which) { + } + + /** + * 确认选择触发 + * @param dialog {@link DialogInterface} + */ + public void onPositiveButton(DialogInterface dialog) { + } + + /** + * 取消选择触发 + * @param dialog {@link DialogInterface} + */ + public void onCancel(DialogInterface dialog) { + } + + /** + * 关闭通知 + * @param dialog {@link DialogInterface} + */ + public void onDismiss(DialogInterface dialog) { + } + } + + // = + + /** + * 创建单选列表样式 Dialog + * @param context {@link Context} + * @param itemsId R.arrays 数据源 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceListDialog(final Context context, @ArrayRes final int itemsId, final String title, final Drawable icon, + final String positiveBtnText, final SingleChoiceListener singleChoiceListener) { + return createSingleChoiceListDialog(context, itemsId, title, icon, null, positiveBtnText, singleChoiceListener, 0); + } + + /** + * 创建单选列表样式 Dialog + * @param context {@link Context} + * @param itemsId R.arrays 数据源 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceListDialog(final Context context, @ArrayRes final int itemsId, final String title, final Drawable icon, + final String negativeBtnText, final String positiveBtnText, final SingleChoiceListener singleChoiceListener) { + return createSingleChoiceListDialog(context, itemsId, title, icon, negativeBtnText, positiveBtnText, singleChoiceListener, 0); + } + + /** + * 创建单选列表样式 Dialog + * @param context {@link Context} + * @param itemsId R.arrays 数据源 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @param themeResId 样式 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceListDialog(final Context context, @ArrayRes final int itemsId, final String title, final Drawable icon, + final String negativeBtnText, final String positiveBtnText, final SingleChoiceListener singleChoiceListener, + @StyleRes final int themeResId) { + try { + AlertDialog.Builder builder = new AlertDialog.Builder(context, themeResId); + if (!isEmpty(title)) { + builder.setTitle(title); + } + if (icon != null) { + builder.setIcon(icon); + } + builder.setItems(itemsId, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (singleChoiceListener != null) { + singleChoiceListener.onSingleChoiceItems(dialog, which); + } + } + }); + + // 判断是否存在确认按钮文案 + if (!isEmpty(positiveBtnText)) { + builder.setPositiveButton(positiveBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (singleChoiceListener != null) { + singleChoiceListener.onPositiveButton(dialog); + } + } + }); + } + + // 判断是否存在取消按钮文案 + if (!isEmpty(negativeBtnText)) { + builder.setNegativeButton(negativeBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (singleChoiceListener != null) { + singleChoiceListener.onCancel(dialog); + } + } + }); + } + + // 设置 Dialog 关闭监听 + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (singleChoiceListener != null) { + singleChoiceListener.onDismiss(dialog); + } + } + }); + return builder.create(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createSingleChoiceListDialog"); + } + return null; + } + + // = + + /** + * 创建单选列表样式 Dialog + * @param context {@link Context} + * @param items 单选文案数组 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceListDialog(final Context context, final CharSequence[] items, final String title, final Drawable icon, + final String positiveBtnText, final SingleChoiceListener singleChoiceListener) { + return createSingleChoiceListDialog(context, items, title, icon, null, positiveBtnText, singleChoiceListener, 0); + } + + /** + * 创建单选列表样式 Dialog + * @param context {@link Context} + * @param items 单选文案数组 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceListDialog(final Context context, final CharSequence[] items, final String title, final Drawable icon, + final String negativeBtnText, final String positiveBtnText, final SingleChoiceListener singleChoiceListener) { + return createSingleChoiceListDialog(context, items, title, icon, negativeBtnText, positiveBtnText, singleChoiceListener, 0); + } + + /** + * 创建单选列表样式 Dialog + * @param context {@link Context} + * @param items 单选文案数组 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @param themeResId 样式 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceListDialog(final Context context, final CharSequence[] items, final String title, final Drawable icon, + final String negativeBtnText, final String positiveBtnText, final SingleChoiceListener singleChoiceListener, + @StyleRes final int themeResId) { + try { + AlertDialog.Builder builder = new AlertDialog.Builder(context, themeResId); + if (!isEmpty(title)) { + builder.setTitle(title); + } + if (icon != null) { + builder.setIcon(icon); + } + builder.setItems(items, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (singleChoiceListener != null) { + singleChoiceListener.onSingleChoiceItems(dialog, which); + } + } + }); + + // 判断是否存在确认按钮文案 + if (!isEmpty(positiveBtnText)) { + builder.setPositiveButton(positiveBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (singleChoiceListener != null) { + singleChoiceListener.onPositiveButton(dialog); + } + } + }); + } + + // 判断是否存在取消按钮文案 + if (!isEmpty(negativeBtnText)) { + builder.setNegativeButton(negativeBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (singleChoiceListener != null) { + singleChoiceListener.onCancel(dialog); + } + } + }); + } + + // 设置 Dialog 关闭监听 + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (singleChoiceListener != null) { + singleChoiceListener.onDismiss(dialog); + } + } + }); + return builder.create(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createSingleChoiceListDialog"); + } + return null; + } + + // =============== + // = 单选 Dialog = + // =============== + + /** + * 创建单选样式 Dialog + * @param context {@link Context} + * @param itemsId R.arrays 数据源 + * @param checkedItem 默认选中索引 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceDialog(final Context context, @ArrayRes final int itemsId, final int checkedItem, + final String title, final Drawable icon, final String positiveBtnText, + final SingleChoiceListener singleChoiceListener) { + return createSingleChoiceDialog(context, itemsId, checkedItem, title, icon, null, positiveBtnText, singleChoiceListener, 0); + } + + /** + * 创建单选样式 Dialog + * @param context {@link Context} + * @param itemsId R.arrays 数据源 + * @param checkedItem 默认选中索引 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceDialog(final Context context, @ArrayRes final int itemsId, final int checkedItem, + final String title, final Drawable icon, final String negativeBtnText, final String positiveBtnText, + final SingleChoiceListener singleChoiceListener) { + return createSingleChoiceDialog(context, itemsId, checkedItem, title, icon, negativeBtnText, positiveBtnText, singleChoiceListener, 0); + } + + /** + * 创建单选样式 Dialog + * @param context {@link Context} + * @param itemsId R.arrays 数据源 + * @param checkedItem 默认选中索引 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @param themeResId 样式 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceDialog(final Context context, @ArrayRes final int itemsId, final int checkedItem, + final String title, final Drawable icon, final String negativeBtnText, final String positiveBtnText, + final SingleChoiceListener singleChoiceListener, @StyleRes final int themeResId) { + try { + AlertDialog.Builder builder = new AlertDialog.Builder(context, themeResId); + if (!isEmpty(title)) { + builder.setTitle(title); + } + if (icon != null) { + builder.setIcon(icon); + } + builder.setSingleChoiceItems(itemsId, checkedItem, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (singleChoiceListener != null) { + singleChoiceListener.onSingleChoiceItems(dialog, which); + } + } + }); + + // 判断是否存在确认按钮文案 + if (!isEmpty(positiveBtnText)) { + builder.setPositiveButton(positiveBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (singleChoiceListener != null) { + singleChoiceListener.onPositiveButton(dialog); + } + } + }); + } + + // 判断是否存在取消按钮文案 + if (!isEmpty(negativeBtnText)) { + builder.setNegativeButton(negativeBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (singleChoiceListener != null) { + singleChoiceListener.onCancel(dialog); + } + } + }); + } + + // 设置 Dialog 关闭监听 + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (singleChoiceListener != null) { + singleChoiceListener.onDismiss(dialog); + } + } + }); + return builder.create(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createSingleChoiceDialog"); + } + return null; + } + + // = + + /** + * 创建单选样式 Dialog + * @param context {@link Context} + * @param items 单选文案数组 + * @param checkedItem 默认选中索引 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceDialog(final Context context, final CharSequence[] items, final int checkedItem, + final String title, final Drawable icon, final String positiveBtnText, + final SingleChoiceListener singleChoiceListener) { + return createSingleChoiceDialog(context, items, checkedItem, title, icon, null, positiveBtnText, singleChoiceListener, 0); + } + + /** + * 创建单选样式 Dialog + * @param context {@link Context} + * @param items 单选文案数组 + * @param checkedItem 默认选中索引 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceDialog(final Context context, final CharSequence[] items, final int checkedItem, + final String title, final Drawable icon, final String negativeBtnText, final String positiveBtnText, + final SingleChoiceListener singleChoiceListener) { + return createSingleChoiceDialog(context, items, checkedItem, title, icon, negativeBtnText, positiveBtnText, singleChoiceListener, 0); + } + + /** + * 创建单选样式 Dialog + * @param context {@link Context} + * @param items 单选文案数组 + * @param checkedItem 默认选中索引 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param singleChoiceListener 单选事件 + * @param themeResId 样式 + * @return {@link AlertDialog} + */ + public static AlertDialog createSingleChoiceDialog(final Context context, final CharSequence[] items, final int checkedItem, + final String title, final Drawable icon, final String negativeBtnText, final String positiveBtnText, + final SingleChoiceListener singleChoiceListener, @StyleRes final int themeResId) { + try { + AlertDialog.Builder builder = new AlertDialog.Builder(context, themeResId); + if (!isEmpty(title)) { + builder.setTitle(title); + } + if (icon != null) { + builder.setIcon(icon); + } + builder.setSingleChoiceItems(items, checkedItem, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (singleChoiceListener != null) { + singleChoiceListener.onSingleChoiceItems(dialog, which); + } + } + }); + + // 判断是否存在确认按钮文案 + if (!isEmpty(positiveBtnText)) { + builder.setPositiveButton(positiveBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (singleChoiceListener != null) { + singleChoiceListener.onPositiveButton(dialog); + } + } + }); + } + + // 判断是否存在取消按钮文案 + if (!isEmpty(negativeBtnText)) { + builder.setNegativeButton(negativeBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (singleChoiceListener != null) { + singleChoiceListener.onCancel(dialog); + } + } + }); + } + + // 设置 Dialog 关闭监听 + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (singleChoiceListener != null) { + singleChoiceListener.onDismiss(dialog); + } + } + }); + return builder.create(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createSingleChoiceDialog"); + } + return null; + } + + // =============== + // = 多选 Dialog = + // =============== + + /** + * detail: Dialog 多选事件 + * @author Ttt + */ + public static abstract class MultiChoiceListener { + + /** + * 多选选中触发 + * @param dialog {@link DialogInterface} + * @param which 操作索引 + * @param isChecked 是否选中 + */ + public void onMultiChoiceItems(DialogInterface dialog, int which, boolean isChecked) { + } + + /** + * 确认选择触发 + * @param dialog {@link DialogInterface} + * @param checkedItems 选中的数据 + */ + public void onPositiveButton(DialogInterface dialog, boolean[] checkedItems) { + } + + /** + * 取消选择触发 + * @param dialog {@link DialogInterface} + */ + public void onCancel(DialogInterface dialog) { + } + + /** + * 关闭通知 + * @param dialog {@link DialogInterface} + */ + public void onDismiss(DialogInterface dialog) { + } + } + + // = + + /** + * 创建多选样式 Dialog + * @param context {@link Context} + * @param itemsId R.arrays 数据源 + * @param checkedItems 选中状态 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param positiveBtnText 确认按钮文案 + * @param multiChoiceListener 多选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createMultiChoiceDialog(final Context context, @ArrayRes final int itemsId, final boolean[] checkedItems, + final String title, final Drawable icon, final String positiveBtnText, + final MultiChoiceListener multiChoiceListener) { + return createMultiChoiceDialog(context, itemsId, checkedItems, title, icon, null, positiveBtnText, multiChoiceListener, 0); + } + + /** + * 创建多选样式 Dialog + * @param context {@link Context} + * @param itemsId R.arrays 数据源 + * @param checkedItems 选中状态 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param multiChoiceListener 多选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createMultiChoiceDialog(final Context context, @ArrayRes final int itemsId, final boolean[] checkedItems, + final String title, final Drawable icon, final String negativeBtnText, final String positiveBtnText, + final MultiChoiceListener multiChoiceListener) { + return createMultiChoiceDialog(context, itemsId, checkedItems, title, icon, negativeBtnText, positiveBtnText, multiChoiceListener, 0); + } + + /** + * 创建多选样式 Dialog + * @param context {@link Context} + * @param itemsId R.arrays 数据源 + * @param checkedItems 选中状态 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param multiChoiceListener 多选事件 + * @param themeResId 样式 + * @return {@link AlertDialog} + */ + public static AlertDialog createMultiChoiceDialog(final Context context, @ArrayRes final int itemsId, final boolean[] checkedItems, + final String title, final Drawable icon, final String negativeBtnText, + final String positiveBtnText, final MultiChoiceListener multiChoiceListener, + @StyleRes final int themeResId) { + try { + AlertDialog.Builder builder = new AlertDialog.Builder(context, themeResId); + if (!isEmpty(title)) { + builder.setTitle(title); + } + if (icon != null) { + builder.setIcon(icon); + } + builder.setMultiChoiceItems(itemsId, checkedItems, new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int which, boolean isChecked) { + if (multiChoiceListener != null) { + multiChoiceListener.onMultiChoiceItems(dialog, which, isChecked); + } + } + }); + + // 判断是否存在确认按钮文案 + if (!isEmpty(positiveBtnText)) { + builder.setPositiveButton(positiveBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (multiChoiceListener != null) { + multiChoiceListener.onPositiveButton(dialog, checkedItems); + } + } + }); + } + + // 判断是否存在取消按钮文案 + if (!isEmpty(negativeBtnText)) { + builder.setNegativeButton(negativeBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (multiChoiceListener != null) { + multiChoiceListener.onCancel(dialog); + } + } + }); + } + + // 设置 Dialog 关闭监听 + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (multiChoiceListener != null) { + multiChoiceListener.onDismiss(dialog); + } + } + }); + return builder.create(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createMultiChoiceDialog"); + } + return null; + } + + // = + + /** + * 创建多选样式 Dialog + * @param context {@link Context} + * @param items 多选文案数组 + * @param checkedItems 选中状态 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param positiveBtnText 确认按钮文案 + * @param multiChoiceListener 多选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createMultiChoiceDialog(final Context context, final CharSequence[] items, final boolean[] checkedItems, final String title, + final Drawable icon, final String positiveBtnText, final MultiChoiceListener multiChoiceListener) { + return createMultiChoiceDialog(context, items, checkedItems, title, icon, null, positiveBtnText, multiChoiceListener, 0); + } + + /** + * 创建多选样式 Dialog + * @param context {@link Context} + * @param items 多选文案数组 + * @param checkedItems 选中状态 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param multiChoiceListener 多选事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createMultiChoiceDialog(final Context context, final CharSequence[] items, final boolean[] checkedItems, final String title, + final Drawable icon, final String negativeBtnText, final String positiveBtnText, + final MultiChoiceListener multiChoiceListener) { + return createMultiChoiceDialog(context, items, checkedItems, title, icon, negativeBtnText, positiveBtnText, multiChoiceListener, 0); + } + + /** + * 创建多选样式 Dialog + * @param context {@link Context} + * @param items 多选文案数组 + * @param checkedItems 选中状态 + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param multiChoiceListener 多选事件 + * @param themeResId 样式 + * @return {@link AlertDialog} + */ + public static AlertDialog createMultiChoiceDialog(final Context context, final CharSequence[] items, final boolean[] checkedItems, + final String title, final Drawable icon, final String negativeBtnText, final String positiveBtnText, + final MultiChoiceListener multiChoiceListener, @StyleRes final int themeResId) { + try { + AlertDialog.Builder builder = new AlertDialog.Builder(context, themeResId); + if (!isEmpty(title)) { + builder.setTitle(title); + } + if (icon != null) { + builder.setIcon(icon); + } + builder.setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int which, boolean isChecked) { + if (multiChoiceListener != null) { + multiChoiceListener.onMultiChoiceItems(dialog, which, isChecked); + } + } + }); + + // 判断是否存在确认按钮文案 + if (!isEmpty(positiveBtnText)) { + builder.setPositiveButton(positiveBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (multiChoiceListener != null) { + multiChoiceListener.onPositiveButton(dialog, checkedItems); + } + } + }); + } + + // 判断是否存在取消按钮文案 + if (!isEmpty(negativeBtnText)) { + builder.setNegativeButton(negativeBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (multiChoiceListener != null) { + multiChoiceListener.onCancel(dialog); + } + } + }); + } + + // 设置 Dialog 关闭监听 + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (multiChoiceListener != null) { + multiChoiceListener.onDismiss(dialog); + } + } + }); + return builder.create(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createMultiChoiceDialog"); + } + return null; + } + + // ==================== + // = 设置 View Dialog = + // ==================== + + /** + * detail: 自定义 View Dialog 事件 + * @author Ttt + */ + public static abstract class ViewDialogListener { + + /** + * 确认触发 + * @param dialog {@link DialogInterface} + */ + public void onPositiveButton(DialogInterface dialog) { + } + + /** + * 取消触发 + * @param dialog {@link DialogInterface} + */ + public void onCancel(DialogInterface dialog) { + } + + /** + * 关闭通知 + * @param dialog {@link DialogInterface} + */ + public void onDismiss(DialogInterface dialog) { + } + } + + // = + + /** + * 创建自定义 View 样式 Dialog + * @param context {@link Context} + * @param view {@link View} + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param positiveBtnText 确认按钮文案 + * @param viewDialogListener 自定义 View Dialog 事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createViewDialog(final Context context, final View view, final String title, final Drawable icon, + final String positiveBtnText, final ViewDialogListener viewDialogListener) { + return createViewDialog(context, view, title, icon, null, positiveBtnText, viewDialogListener, 0); + } + + /** + * 创建自定义 View 样式 Dialog + * @param context {@link Context} + * @param view {@link View} + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param viewDialogListener 自定义 View Dialog 事件 + * @return {@link AlertDialog} + */ + public static AlertDialog createViewDialog(final Context context, final View view, final String title, final Drawable icon, + final String negativeBtnText, final String positiveBtnText, final ViewDialogListener viewDialogListener) { + return createViewDialog(context, view, title, icon, negativeBtnText, positiveBtnText, viewDialogListener, 0); + } + + /** + * 创建自定义 View 样式 Dialog + * @param context {@link Context} + * @param view {@link View} + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param positiveBtnText 确认按钮文案 + * @param viewDialogListener 自定义 View Dialog 事件 + * @param themeResId 样式 + * @return {@link AlertDialog} + */ + public static AlertDialog createViewDialog(final Context context, final View view, final String title, final Drawable icon, + final String positiveBtnText, final ViewDialogListener viewDialogListener, + @StyleRes final int themeResId) { + return createViewDialog(context, view, title, icon, null, positiveBtnText, viewDialogListener, themeResId); + } + + /** + * 创建自定义 View 样式 Dialog + * @param context {@link Context} + * @param view {@link View} + * @param title 标题 + * @param icon 图标 ( 标题左侧 ) + * @param negativeBtnText 取消按钮文案 + * @param positiveBtnText 确认按钮文案 + * @param viewDialogListener 自定义 View Dialog 事件 + * @param themeResId 样式 + * @return {@link AlertDialog} + */ + public static AlertDialog createViewDialog(final Context context, final View view, final String title, final Drawable icon, + final String negativeBtnText, final String positiveBtnText, final ViewDialogListener viewDialogListener, + @StyleRes final int themeResId) { + try { + AlertDialog.Builder builder = new AlertDialog.Builder(context, themeResId); + if (!isEmpty(title)) { + builder.setTitle(title); + } + if (icon != null) { + builder.setIcon(icon); + } + if (view != null) { + builder.setView(view); + } + // 判断是否存在确认按钮文案 + if (!isEmpty(positiveBtnText)) { + builder.setPositiveButton(positiveBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (viewDialogListener != null) { + viewDialogListener.onPositiveButton(dialog); + } + } + }); + } + + // 判断是否存在取消按钮文案 + if (!isEmpty(negativeBtnText)) { + builder.setNegativeButton(negativeBtnText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + // 触发回调 + if (viewDialogListener != null) { + viewDialogListener.onCancel(dialog); + } + } + }); + } + + // 设置 Dialog 关闭监听 + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (viewDialogListener != null) { + viewDialogListener.onDismiss(dialog); + } + } + }); + return builder.create(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createViewDialog"); + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/EditTextUtils.java b/lib/DevApp/src/main/java/dev/utils/app/EditTextUtils.java new file mode 100644 index 0000000000..0341e61b23 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/EditTextUtils.java @@ -0,0 +1,634 @@ +package dev.utils.app; + +import android.text.Editable; +import android.text.InputFilter; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.DigitsKeyListener; +import android.text.method.KeyListener; +import android.view.View; +import android.widget.EditText; + +import java.util.UUID; + +import dev.utils.LogPrintUtils; + +/** + * detail: EditText 工具类 + * @author Ttt + *
+ *     EditText 属性大全 ( 不局限于仅仅是 EditText)
+ *     @see 
+ *     

+ * EditText 多行显示及所有属性 + * @see
+ *

+ * EditText 设置不自动获取焦点, 点击后才获取, 并弹出软键盘 + * @see
+ *

+ * EditText 点击无反应解决办法 + * @see
+ *

+ * EditText 限制输入的 4 种方法 + * @see
+ *

+ * 自定义 EditText 光标和下划线颜色 + * @see
+ *
+ */ +public final class EditTextUtils { + + private EditTextUtils() { + } + + // 日志 TAG + private static final String TAG = EditTextUtils.class.getSimpleName(); + + // ================= + // = 获取 EditText = + // ================= + + /** + * 获取 EditText + * @param view {@link View} + * @param 泛型 + * @return {@link EditText} + */ + public static T getEditText(final View view) { + if (view != null) { + try { + return (T) view; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getEditText"); + } + } + return null; + } + + // = + + /** + * 获取输入的内容 + * @param editText {@link EditText} + * @param 泛型 + * @return {@link EditText} + */ + public static String getText(final T editText) { + if (editText != null) { + return editText.getText().toString(); + } + return ""; + } + + /** + * 获取输入的内容长度 + * @param editText {@link EditText} + * @param 泛型 + * @return {@link EditText} + */ + public static int getTextLength(final T editText) { + return getText(editText).length(); + } + + // = + + /** + * 设置内容 + * @param editText {@link EditText} + * @param content 文本内容 + * @param 泛型 + * @return {@link EditText} + */ + public static T setText(final T editText, final String content) { + return setText(editText, content, true); + } + + /** + * 设置内容 + * @param editText {@link EditText} + * @param content 文本内容 + * @param isSelect 是否设置光标 + * @param 泛型 + * @return {@link EditText} + */ + public static T setText(final T editText, final String content, final boolean isSelect) { + if (editText != null && content != null) { + editText.setText(content); + // 设置光标 + if (isSelect) { + editText.setSelection(editText.getText().toString().length()); + } + } + return editText; + } + + /** + * 设置多个 EditText 文本 + * @param content 文本内容 + * @param views View(EditText)[] + */ + public static void setTexts(final String content, final View... views) { + if (views != null) { + for (View view : views) { + setText(getEditText(view), content); + } + } + } + + /** + * 设置多个 EditText 文本 + * @param content 文本内容 + * @param views EditText[] + * @param 泛型 + */ + public static void setTexts(final String content, final T... views) { + if (views != null) { + for (T view : views) { + setText(view, content); + } + } + } + + // = + + /** + * 追加内容 ( 当前光标位置追加 ) + * @param editText {@link EditText} + * @param content 文本内容 + * @param isSelect 是否设置光标 + * @param 泛型 + * @return {@link EditText} + */ + public static T insert(final T editText, final String content, final boolean isSelect) { + if (editText != null) { + return insert(editText, content, editText.getSelectionStart(), isSelect); + } + return editText; + } + + /** + * 追加内容 + * @param editText {@link EditText} + * @param content 文本内容 + * @param start 开始添加的位置 + * @param isSelect 是否设置光标 + * @param 泛型 + * @return {@link EditText} + */ + public static T insert(final T editText, final String content, final int start, final boolean isSelect) { + if (editText != null && !TextUtils.isEmpty(content)) { + try { + Editable editable = editText.getText(); + // 在指定位置 追加内容 + editable.insert(start, content); + // 设置光标 + if (isSelect) { + editText.setSelection(editText.getText().toString().length()); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "insert"); + } + } + return editText; + } + + // = + + /** + * 设置长度限制 + * @param editText {@link EditText} + * @param maxLength 长度限制 + * @param 泛型 + * @return {@link EditText} + */ + public static T setMaxLength(final T editText, final int maxLength) { + if (editText != null && maxLength > 0) { + // 设置最大长度限制 + InputFilter[] filters = {new InputFilter.LengthFilter(maxLength)}; + editText.setFilters(filters); + } + return editText; + } + + /** + * 设置长度限制, 并且设置内容 + * @param editText {@link EditText} + * @param content 文本内容 + * @param maxLength 长度限制 + * @param 泛型 + * @return {@link EditText} + */ + public static T setMaxLengthAndText(final T editText, final String content, final int maxLength) { + return setText(setMaxLength(editText, maxLength), content); + } + + // ======== + // = 光标 = + // ======== + + /** + * 设置是否显示光标 + * @param editText {@link EditText} + * @param visible 是否显示光标 + * @param 泛型 + * @return {@link EditText} + */ + public static T setCursorVisible(final T editText, final boolean visible) { + if (editText != null) { + editText.setCursorVisible(visible); + } + return editText; + } + + /** + * 获取光标位置 + * @param editText {@link EditText} + * @param 泛型 + * @return 光标位置 + */ + public static int getSelectionStart(final T editText) { + if (editText != null) { + return editText.getSelectionStart(); + } + return 0; + } + + /** + * 设置光标在第一位 + * @param editText {@link EditText} + * @param 泛型 + * @return {@link EditText} + */ + public static T setSelectionToTop(final T editText) { + return setSelection(editText, 0); + } + + /** + * 设置光标在最后一位 + * @param editText {@link EditText} + * @param 泛型 + * @return {@link EditText} + */ + public static T setSelectionToBottom(final T editText) { + return setSelection(editText, getTextLength(editText)); + } + + /** + * 设置光标位置 + * @param editText {@link EditText} + * @param index 光标位置 + * @param 泛型 + * @return {@link EditText} + */ + public static T setSelection(final T editText, final int index) { + if (editText != null && index >= 0) { + // 获取数据长度 + int length = editText.getText().toString().length(); + // 设置光标 + editText.setSelection((index > length) ? length : index); + } + return editText; + } + + /** + * 设置输入类型 + * @param editText {@link EditText} + * @param type 类型 + * @param 泛型 + * @return {@link EditText} + */ + public static T setInputType(final T editText, final int type) { + if (editText != null) { + editText.setInputType(type); + } + return editText; + } + + /** + * 设置软键盘右下角按钮类型 + * @param editText {@link EditText} + * @param imeOptions 软键盘按钮类型 + * @param 泛型 + * @return {@link EditText} + */ + public static T setImeOptions(final T editText, final int imeOptions) { + if (editText != null) { + editText.setImeOptions(imeOptions); + } + return editText; + } + + // = + + /** + * 添加输入监听事件 + * @param editText {@link EditText} + * @param watcher 输入监听 + * @param 泛型 + * @return {@link EditText} + */ + public static T addTextChangedListener(final T editText, final TextWatcher watcher) { + if (editText != null && watcher != null) { + editText.addTextChangedListener(watcher); + } + return editText; + } + + /** + * 移除输入监听事件 + * @param editText {@link EditText} + * @param watcher 输入监听 + * @param 泛型 + * @return {@link EditText} + */ + public static T removeTextChangedListener(final T editText, final TextWatcher watcher) { + if (editText != null && watcher != null) { + editText.removeTextChangedListener(watcher); + } + return editText; + } + + // ========================= + // = Key Listener 快捷处理 = + // ========================= + + // 0123456789 + private static final char[] NUMBERS = new char[]{48, 49, 50, 51, 52, 53, 54, 55, 56, 57}; + + // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + private static final char[] LETTERS = new char[]{97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90}; + + // 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + private static final char[] NUMBERS_AND_LETTERS = new char[]{48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90}; + + /** + * 设置 KeyListener + * @param editText {@link EditText} + * @param keyListener {@link KeyListener} + * @param 泛型 + * @return {@link EditText} + */ + public static T setKeyListener(final T editText, final KeyListener keyListener) { + if (editText != null) { + editText.setKeyListener(keyListener); + } + return editText; + } + + /** + * 设置 KeyListener + * @param editText {@link EditText} + * @param accepted 允许输入的内容, 如: 0123456789 + * @param 泛型 + * @return {@link EditText} + */ + public static T setKeyListener(final T editText, final String accepted) { + if (editText != null) { + // editText.setKeyListener(DigitsKeyListener.getInstance(accepted)); + editText.setKeyListener(createDigitsKeyListener(-1, accepted)); + } + return editText; + } + + /** + * 设置 KeyListener + * @param editText {@link EditText} + * @param accepted 允许输入的内容 + * @param 泛型 + * @return {@link EditText} + */ + public static T setKeyListener(final T editText, final char[] accepted) { + if (editText != null) { + editText.setKeyListener(createDigitsKeyListener(-1, accepted)); + } + return editText; + } + + // = + + /** + * 获取 DigitsKeyListener ( 限制只能输入字母, 默认弹出英文输入法 ) + * @return {@link DigitsKeyListener} + */ + public static DigitsKeyListener getLettersKeyListener() { + return createDigitsKeyListener((InputType.TYPE_TEXT_VARIATION_PASSWORD), LETTERS); + } + + /** + * 获取 DigitsKeyListener ( 限制只能输入字母和数字, 默认弹出英文输入法 ) + * @return {@link DigitsKeyListener} + */ + public static DigitsKeyListener getNumberAndLettersKeyListener() { + return createDigitsKeyListener((InputType.TYPE_TEXT_VARIATION_PASSWORD), NUMBERS_AND_LETTERS); + } + + /** + * 获取 DigitsKeyListener ( 限制只能输入数字, 默认弹出数字列表 ) + * @return {@link DigitsKeyListener} + */ + public static DigitsKeyListener getNumberKeyListener() { + return createDigitsKeyListener((InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL), NUMBERS); + } + + // = + + /** + * 创建 DigitsKeyListener + * @param accepted 允许输入的内容 ( 可以传入 "", 这样无法输入内容 ) + * @return {@link DigitsKeyListener} + */ + public static DigitsKeyListener createDigitsKeyListener(final String accepted) { + return createDigitsKeyListener(-1, (accepted == null) ? null : accepted.toCharArray()); + } + + /** + * 创建 DigitsKeyListener + * @param inputType 输入类型 + * @param accepted 允许输入的内容 ( 可以传入 "", 这样无法输入内容 ) + * @return {@link DigitsKeyListener} + */ + public static DigitsKeyListener createDigitsKeyListener(final int inputType, final String accepted) { + return createDigitsKeyListener(inputType, (accepted == null) ? null : accepted.toCharArray()); + } + + // = + + /** + * 创建 DigitsKeyListener + * @param accepted 允许输入的内容 + * @return {@link DigitsKeyListener} + */ + public static DigitsKeyListener createDigitsKeyListener(final char[] accepted) { + return createDigitsKeyListener(-1, accepted); + } + + /** + * 创建 DigitsKeyListener + * @param inputType 输入类型 + * @param accepted 允许输入的内容 + * @return {@link DigitsKeyListener} + */ + public static DigitsKeyListener createDigitsKeyListener(final int inputType, final char[] accepted) { + DigitsKeyListener digitsKeyListener = new DigitsKeyListener() { + @Override + protected char[] getAcceptedChars() { + if (accepted != null) { + return accepted; + } + return super.getAcceptedChars(); + } + + @Override + public int getInputType() { + if (inputType != -1) { + return inputType; + } + return super.getInputType(); + } + }; + return digitsKeyListener; + } + + // =============== + // = TextWatcher = + // =============== + + /** + * detail: 开发输入监听抽象类 + * @author Ttt + *
+     *     @see 
+     *     editText.addTextChangedListener(DevTextWatcher);
+     * 
+ */ + public static abstract class DevTextWatcher implements TextWatcher { + + // 标记 id - 一定程度上唯一 + private final int markId = UUID.randomUUID().hashCode(); + // 判断是否操作中 + private boolean operate = false; + // 标记状态, 特殊需求处理 + private int operateState = -1; + // 类型 + private int type = -1; + + /** + * 构造函数 + */ + public DevTextWatcher() { + } + + /** + * 构造函数 + * @param type 类型 + */ + public DevTextWatcher(int type) { + this.type = type; + } + + /** + * 获取标记 id + * @return 标记 id + */ + public final int getMarkId() { + return markId; + } + + /** + * 判断是否操作中 + * @return {@code true} yes, {@code false} no + */ + public final boolean isOperate() { + return operate; + } + + /** + * 设置是否操作中 + * @param operate {@code true} yes, {@code false} no + * @return {@link DevTextWatcher} + */ + public final DevTextWatcher setOperate(boolean operate) { + this.operate = operate; + return this; + } + + /** + * 获取操作状态 + * @return 操作状态 + */ + public final int getOperateState() { + return operateState; + } + + /** + * 设置操作状态 + * @param operateState 操作状态 + * @return {@link DevTextWatcher} + */ + public final DevTextWatcher setOperateState(int operateState) { + this.operateState = operateState; + return this; + } + + /** + * 获取类型 + * @return 类型 + */ + public int getType() { + return type; + } + + /** + * 设置类型 + * @param type 类型 + * @return {@link DevTextWatcher} + */ + public DevTextWatcher setType(int type) { + this.type = type; + return this; + } + + // ============ + // = 回调接口 = + // ============ + + /** + * 在文本变化前调用 + * @param s 修改之前的文字 + * @param start 字符串中即将发生修改的位置 + * @param count 字符串中即将被修改的文字的长度, 如果是新增的话则为 0 + * @param after 被修改的文字修改之后的长度, 如果是删除的话则为 0 + */ + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + /** + * 在文本变化后调用 + * @param s 改变后的字符串 + * @param start 有变动的字符串的位置 + * @param before 被改变的字符串长度, 如果是新增则为 0 + * @param count 添加的字符串长度, 如果是删除则为 0 + */ + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + /** + * 在文本变化后调用 + * @param s 修改后的文字 + */ + @Override + public void afterTextChanged(Editable s) { + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/FileRecordUtils.java b/lib/DevApp/src/main/java/dev/utils/app/FileRecordUtils.java new file mode 100644 index 0000000000..7d422c9adb --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/FileRecordUtils.java @@ -0,0 +1,491 @@ +package dev.utils.app; + +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.text.TextUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 文件记录工具类 + * @author Ttt + */ +public final class FileRecordUtils { + + private FileRecordUtils() { + } + + // 日志 TAG + private static final String TAG = FileRecordUtils.class.getSimpleName(); + + // ============ + // = 配置信息 = + // ============ + + // APP 版本 ( 如 1.0.01) 显示给用户看的 + private static String APP_VERSION_NAME = ""; + // android:versionCode 整数值, 代表应用程序代码的相对版本 + private static String APP_VERSION_CODE = ""; + // 应用包名 + private static String PACKAGE_NAME = ""; + // 设备信息 + private static String DEVICE_INFO_STR = null; + // 设备信息存储 Map + private static Map DEVICE_INFO_MAPS = new HashMap<>(); + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + // 换行字符串 ( 两行 ) + private static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 初始化调用方法 ( 内部已调用 ) + */ + public static void init() { + // 如果版本信息为 null, 才进行处理 + if (TextUtils.isEmpty(APP_VERSION_CODE) || TextUtils.isEmpty(APP_VERSION_NAME)) { + // 获取 APP 版本信息 + String[] versions = getAppVersion(); + // 防止为 null + if (versions != null && versions.length == 2) { + // 保存 APP 版本信息 + APP_VERSION_NAME = versions[0]; + APP_VERSION_CODE = versions[1]; + } + } + + // 获取包名 + if (TextUtils.isEmpty(PACKAGE_NAME)) { + try { + PACKAGE_NAME = DevUtils.getContext().getPackageName(); + } catch (Exception e) { + } + } + + // 判断是否存在设备信息 + if (DEVICE_INFO_MAPS.size() == 0) { + // 获取设备信息 + getDeviceInfo(DEVICE_INFO_MAPS); + // 转换字符串 + handlerDeviceInfo(""); + } + } + + // ============ + // = 异常日志 = + // ============ + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String filePath, final String fileName) { + return saveErrorLog(ex, null, null, filePath, fileName, true); + } + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String head, final String bottom, + final String filePath, final String fileName) { + return saveErrorLog(ex, head, bottom, filePath, fileName, true); + } + + // = + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @param printDevice 是否打印设备信息 + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String filePath, final String fileName, final boolean printDevice) { + return saveErrorLog(ex, null, null, filePath, fileName, printDevice); + } + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @param printDevice 是否打印设备信息 + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String head, final String bottom, + final String filePath, final String fileName, final boolean printDevice) { + return saveLog(getThrowableStackTrace(ex, "failed to get exception information"), + head, bottom, filePath, fileName, printDevice); + } + + // ============ + // = 正常日志 = + // ============ + + /** + * 保存日志 + * @param log 日志信息 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String filePath, final String fileName) { + return saveLog(log, null, null, filePath, fileName, true); + } + + /** + * 保存日志 + * @param log 日志信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String head, final String bottom, + final String filePath, final String fileName) { + return saveLog(log, head, bottom, filePath, fileName, true); + } + + // = + + /** + * 保存日志 + * @param log 日志信息 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @param printDevice 是否打印设备信息 + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String filePath, final String fileName, final boolean printDevice) { + return saveLog(log, null, null, filePath, fileName, printDevice); + } + + /** + * 保存日志 + * @param log 日志信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @param printDevice 是否打印设备信息 + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String head, final String bottom, + final String filePath, final String fileName, final boolean printDevice) { + // 日志拼接 + StringBuilder builder = new StringBuilder(); + // 如果存在顶部内容, 则进行添加 + if (!TextUtils.isEmpty(head)) { + builder.append(head); + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + } + // 保存 APP 信息 + builder.append("date: " + getDateNow()); + builder.append(NEW_LINE_STR); + builder.append("versionName: " + APP_VERSION_NAME); + builder.append(NEW_LINE_STR); + builder.append("versionCode: " + APP_VERSION_CODE); + builder.append(NEW_LINE_STR); + builder.append("package: " + PACKAGE_NAME); + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + // 如果需要打印设备信息 + if (printDevice) { + // 设备信息 + String deviceInfo = handlerDeviceInfo("failed to get device information"); + // 保存设备信息 + builder.append(deviceInfo); + builder.append(NEW_LINE_STR); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + } + // 保存日志信息 + builder.append(log); + // 如果存在顶部内容, 则进行添加 + if (!TextUtils.isEmpty(bottom)) { + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + builder.append(bottom); + } + // 保存日志到文件 + return saveFile(filePath, fileName, builder.toString()); + } + + // ================ + // = 设备信息处理 = + // ================ + + /** + * 处理设备信息 + * @param errorInfo 错误提示信息, 如获取设备信息失败 + * @return 拼接后的设备信息字符串 + */ + private static String handlerDeviceInfo(final String errorInfo) { + // 如果不为 null, 则直接返回之前的信息 + if (!TextUtils.isEmpty(DEVICE_INFO_STR)) { + return DEVICE_INFO_STR; + } + // 设备信息 + String deviceInfo = handlerDeviceInfo(DEVICE_INFO_MAPS, null); + // 如果为 null + if (deviceInfo == null) { + return errorInfo; + } + // 保存设备信息 + DEVICE_INFO_STR = deviceInfo; + // 返回设备信息 + return DEVICE_INFO_STR; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = FileUtils = + // ============= + + /** + * 保存文件 + * @param filePath 保存路径 + * @param fileName 文件名. 后缀 + * @param content 保存内容 + * @return {@code true} success, {@code false} fail + */ + private static boolean saveFile(final String filePath, final String fileName, final String content) { + if (filePath != null && fileName != null && content != null) { + try { + // 防止文件没创建 + createFolder(filePath); + // 保存路径 + File file = new File(filePath, fileName); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + fos.write(content.getBytes()); + fos.close(); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param dirPath 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + private static boolean createFolder(final String dirPath) { + return createFolder(getFileByPath(dirPath)); + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param file 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + private static boolean createFolder(final File file) { + if (file != null) { + try { + // 当这个文件夹不存在的时候则创建文件夹 + if (!file.exists()) { + // 允许创建多级目录 + return file.mkdirs(); + // 这个无法创建多级目录 + // rootFile.mkdir(); + } + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createFolder"); + } + } + return false; + } + + // ================== + // = ThrowableUtils = + // ================== + + /** + * 获取异常栈信息 + * @param throwable 异常 + * @param errorInfo 获取失败返回字符串 + * @return 异常栈信息字符串 + */ + private static String getThrowableStackTrace(final Throwable throwable, final String errorInfo) { + if (throwable != null) { + PrintWriter printWriter = null; + try { + Writer writer = new StringWriter(); + printWriter = new PrintWriter(writer); + throwable.printStackTrace(printWriter); + return writer.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getThrowableStackTrace"); + return e.toString(); + } finally { + if (printWriter != null) { + try { + printWriter.close(); + } catch (Exception e) { + } + } + } + } + return errorInfo; + } + + // ============= + // = DateUtils = + // ============= + + /** + * 获取当前日期的字符串 + * @return 当前日期 yyyy-MM-dd HH:mm:ss 格式字符串 + */ + private static String getDateNow() { + try { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime()); + } catch (Exception e) { + } + return null; + } + + // =============== + // = DeviceUtils = + // =============== + + /** + * 获取设备信息 + * @param deviceInfoMap 设备信息 Map + */ + private static void getDeviceInfo(final Map deviceInfoMap) { + // 获取设备信息类的所有申明的字段, 即包括 public、private 和 proteced, 但是不包括父类的申明字段 + Field[] fields = Build.class.getDeclaredFields(); + // 遍历字段 + for (Field field : fields) { + try { + // 取消 Java 的权限控制检查 + field.setAccessible(true); + // 转换当前设备支持的 ABI - CPU 指令集 + if (field.getName().toLowerCase().startsWith("SUPPORTED".toLowerCase())) { + try { + Object object = field.get(null); + // 判断是否数组 + if (object instanceof String[]) { + if (object != null) { + // 获取类型对应字段的数据, 并保存支持的指令集 [arm64-v8a, armeabi-v7a, armeabi] + deviceInfoMap.put(field.getName(), Arrays.toString((String[]) object)); + } + continue; + } + } catch (Exception e) { + } + } + // 获取类型对应字段的数据, 并保存 + deviceInfoMap.put(field.getName(), field.get(null).toString()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); + } + } + } + + /** + * 处理设备信息 + * @param deviceInfoMap 设备信息 Map + * @param errorInfo 错误提示信息, 如获取设备信息失败 + * @return 拼接后的设备信息字符串 + */ + private static String handlerDeviceInfo(final Map deviceInfoMap, final String errorInfo) { + try { + // 初始化 Builder, 拼接字符串 + StringBuilder builder = new StringBuilder(); + // 获取设备信息 + Iterator> mapIter = deviceInfoMap.entrySet().iterator(); + // 遍历设备信息 + while (mapIter.hasNext()) { + // 获取对应的 key - value + Map.Entry rnEntry = mapIter.next(); + String rnKey = rnEntry.getKey(); // key + String rnValue = rnEntry.getValue(); // value + // 保存设备信息 + builder.append(rnKey); + builder.append(" = "); + builder.append(rnValue); + builder.append(NEW_LINE_STR); + } + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "handlerDeviceInfo"); + } + return errorInfo; + } + + // ================= + // = ManifestUtils = + // ================= + + /** + * 获取 APP 版本信息 + * @return 0 = versionName, 1 = versionCode + */ + private static String[] getAppVersion() { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_SIGNATURES); + if (packageInfo != null) { + String versionName = packageInfo.versionName == null ? "null" : packageInfo.versionName; + String versionCode = packageInfo.versionCode + ""; + return new String[]{versionName, versionCode}; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppVersion"); + } + return null; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/FlashlightUtils.java b/lib/DevApp/src/main/java/dev/utils/app/FlashlightUtils.java similarity index 54% rename from DevLibUtils/src/main/java/dev/utils/app/FlashlightUtils.java rename to lib/DevApp/src/main/java/dev/utils/app/FlashlightUtils.java index f759e88006..f49076778d 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/FlashlightUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/app/FlashlightUtils.java @@ -9,36 +9,34 @@ import dev.DevUtils; import dev.utils.LogPrintUtils; -import static android.hardware.Camera.Parameters.FLASH_MODE_OFF; -import static android.hardware.Camera.Parameters.FLASH_MODE_TORCH; - /** * detail: 手电筒工具类 - * Created by Ttt + * @author Ttt */ public final class FlashlightUtils { private FlashlightUtils() { } - // 日志TAG - private final String TAG = FileRecordUtils.class.getSimpleName(); + // 日志 TAG + private final String TAG = FlashlightUtils.class.getSimpleName(); + // 单例对象 + private static final FlashlightUtils INSTANCE = new FlashlightUtils(); + /** + * 获取 FlashlightUtils 实例 + * @return {@link FlashlightUtils} + */ public static FlashlightUtils getInstance() { - return LazyHolder.INSTANCE; + return INSTANCE; } - private static final class LazyHolder { - private static final FlashlightUtils INSTANCE = new FlashlightUtils(); - } - - // = - + // Camera 对象 private Camera mCamera; /** * 注册摄像头 - * @return + * @return {@code true} success, {@code false} fail */ public boolean register() { try { @@ -46,9 +44,7 @@ public boolean register() { } catch (Throwable t) { return false; } - if (mCamera == null) { - return false; - } + if (mCamera == null) return false; try { mCamera.setPreviewTexture(new SurfaceTexture(0)); mCamera.startPreview(); @@ -72,12 +68,10 @@ public void unregister() { * 打开闪光灯 */ public void setFlashlightOn() { - if (mCamera == null) { - return; - } + if (mCamera == null) return; Camera.Parameters parameters = mCamera.getParameters(); - if (!FLASH_MODE_TORCH.equals(parameters.getFlashMode())) { - parameters.setFlashMode(FLASH_MODE_TORCH); + if (!Camera.Parameters.FLASH_MODE_TORCH.equals(parameters.getFlashMode())) { + parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); mCamera.setParameters(parameters); } } @@ -86,49 +80,50 @@ public void setFlashlightOn() { * 关闭闪光灯 */ public void setFlashlightOff() { - if (mCamera == null) { - return; - } + if (mCamera == null) return; Camera.Parameters parameters = mCamera.getParameters(); - if (FLASH_MODE_TORCH.equals(parameters.getFlashMode())) { - parameters.setFlashMode(FLASH_MODE_OFF); + if (Camera.Parameters.FLASH_MODE_TORCH.equals(parameters.getFlashMode())) { + parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); mCamera.setParameters(parameters); } } /** * 是否打开闪光灯 - * @return + * @return {@code true} yes, {@code false} no */ public boolean isFlashlightOn() { - if (mCamera == null) { - return false; - } + if (mCamera == null) return false; Camera.Parameters parameters = mCamera.getParameters(); - return FLASH_MODE_TORCH.equals(parameters.getFlashMode()); + return Camera.Parameters.FLASH_MODE_TORCH.equals(parameters.getFlashMode()); } /** * 是否支持手机闪光灯 - * @return + * @return {@code true} yes, {@code false} no */ - public static boolean isFlashlightEnable() { - return DevUtils.getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH); + public boolean isFlashlightEnable() { + try { + return DevUtils.getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isFlashlightEnable"); + } + return false; } // = /** * 打开闪光灯 - * @param camera + * @param camera {@link android.graphics.Camera} */ - public void setFlashlightOn(Camera camera) { + public void setFlashlightOn(final Camera camera) { if (camera != null) { try { Camera.Parameters parameter = camera.getParameters(); parameter.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); camera.setParameters(parameter); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setFlashlightOn"); } } @@ -136,35 +131,34 @@ public void setFlashlightOn(Camera camera) { /** * 关闭闪光灯 - * @param camera + * @param camera {@link android.graphics.Camera} */ - public void setFlashlightOff(Camera camera) { + public void setFlashlightOff(final Camera camera) { if (camera != null) { try { Camera.Parameters parameter = camera.getParameters(); parameter.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); camera.setParameters(parameter); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setFlashlightOff"); } } } /** - * 是否打开了摄像头 - * @param camera - * @return + * 是否打开闪光灯 + * @param camera {@link android.graphics.Camera} + * @return {@code true} yes, {@code false} no */ - public boolean isFlashlightOn(Camera camera) { - if (camera == null) { - return false; - } - try { - Camera.Parameters parameters = camera.getParameters(); - return FLASH_MODE_TORCH.equals(parameters.getFlashMode()); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "isFlashlightOn"); + public boolean isFlashlightOn(final Camera camera) { + if (camera != null) { + try { + Camera.Parameters parameters = camera.getParameters(); + return Camera.Parameters.FLASH_MODE_TORCH.equals(parameters.getFlashMode()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isFlashlightOn"); + } } return false; } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/HandlerUtils.java b/lib/DevApp/src/main/java/dev/utils/app/HandlerUtils.java new file mode 100644 index 0000000000..995d6d45df --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/HandlerUtils.java @@ -0,0 +1,128 @@ +package dev.utils.app; + +import android.os.Handler; +import android.os.Looper; + +/** + * detail: Handler 工具类 + * @author Ttt + */ +public final class HandlerUtils { + + private HandlerUtils() { + } + + // 主线程 Handler + private static Handler sMainHandler; + + /** + * 获取主线程 Handler + * @return 主线程 Handler + */ + public static Handler getMainHandler() { + if (sMainHandler == null) { + sMainHandler = new Handler(Looper.getMainLooper()); + } + return sMainHandler; + } + + /** + * 在主线程 Handler 中执行任务 + * @param runnable 可执行的任务 + */ + public static void postRunnable(final Runnable runnable) { + if (runnable != null) { + getMainHandler().post(runnable); + } + } + + /** + * 在主线程 Handler 中执行延迟任务 + * @param runnable 可执行的任务 + * @param delayMillis 延迟时间 + */ + public static void postRunnable(final Runnable runnable, final long delayMillis) { + if (runnable != null) { + getMainHandler().postDelayed(runnable, delayMillis); + } + } + + /** + * 在主线程 Handler 中执行延迟任务 + * @param runnable 可执行的任务 + * @param delayMillis 延迟时间 + * @param number 轮询次数 + * @param interval 轮询时间 + */ + public static void postRunnable(final Runnable runnable, final long delayMillis, final int number, final int interval) { + postRunnable(runnable, delayMillis, number, interval, null); + } + + /** + * 在主线程 Handler 中执行延迟任务 + * @param runnable 可执行的任务 + * @param delayMillis 延迟时间 + * @param number 轮询次数 + * @param interval 轮询时间 + * @param onEndListener 结束通知 + */ + public static void postRunnable(final Runnable runnable, final long delayMillis, final int number, final int interval, final OnEndListener onEndListener) { + if (runnable != null) { + Runnable loop = new Runnable() { + private int mNumber; + + @Override + public void run() { + if (mNumber < number) { + mNumber++; + if (runnable != null) { + try { + runnable.run(); + } catch (Exception e) { + } + } + // 判断是否超过次数 + if (mNumber < number) { + getMainHandler().postDelayed(this, interval); + } + } + + // 判断是否超过次数 + if (mNumber >= number) { + if (onEndListener != null) { + onEndListener.onEnd(delayMillis, number, interval); + } + } + } + }; + getMainHandler().postDelayed(loop, delayMillis); + } + } + + /** + * 在主线程 Handler 中清除任务 + * @param runnable 需要清除的任务 + */ + public static void removeRunnable(final Runnable runnable) { + if (runnable != null) { + getMainHandler().removeCallbacks(runnable); + } + } + + // = + + /** + * detail: 结束回调事件 + * @author Ttt + */ + public interface OnEndListener { + + /** + * 结束通知 + * @param delayMillis 延迟时间 + * @param number 轮询次数 + * @param interval 轮询时间 + */ + void onEnd(long delayMillis, int number, int interval); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/HtmlUtils.java b/lib/DevApp/src/main/java/dev/utils/app/HtmlUtils.java new file mode 100644 index 0000000000..bcbe2daf6b --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/HtmlUtils.java @@ -0,0 +1,154 @@ +package dev.utils.app; + +import android.os.Build; +import android.text.Html; +import android.widget.TextView; + +import dev.utils.LogPrintUtils; + +/** + * detail: Html 工具类 + * @author Ttt + */ +public final class HtmlUtils { + + private HtmlUtils() { + } + + // 日志 TAG + private static final String TAG = HtmlUtils.class.getSimpleName(); + + /** + * 设置 Html 内容 + * @param textView {@link TextView} + * @param content Html content + * @param 泛型 + */ + public static void setHtmlText(final T textView, final String content) { + if (textView != null && content != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + textView.setText(Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY)); + } else { + textView.setText(Html.fromHtml(content)); + } + } + } + + // = + + /** + * 为给定的字符串添加 HTML 颜色标记 + * @param content 给定的字符串 + * @param color 颜色值, 如: #000000 + * @return Html 内容字符串 + */ + public static String addHtmlColor(final String content, final String color) { + return "" + content + ""; + } + + /** + * 为给定的字符串添加 HTML 颜色标记 + * @param format 格式化字符串 + * @param content 给定的字符串 + * @param color 颜色值, 如: #000000 + * @return Html 内容字符串 + */ + public static String addHtmlColor(final String format, final String content, final String color) { + return String.format(format, addHtmlColor(content, color)); + } + + /** + * 为给定的字符串添加 HTML 加粗标记 + * @param content 给定的字符串 + * @return Html 内容字符串 + */ + public static String addHtmlBold(final String content) { + return "" + content + ""; + } + + /** + * 为给定的字符串添加 HTML 颜色标记并加粗 + * @param content 给定的字符串 + * @param color 颜色值, 如: #000000 + * @return Html 内容字符串 + */ + public static String addHtmlColorAndBlod(final String content, final String color) { + return addHtmlBold(addHtmlColor(content, color)); + } + + /** + * 为给定的字符串添加 HTML 下划线 + * @param content 给定的字符串 + * @return Html 内容字符串 + */ + public static String addHtmlUnderline(final String content) { + return "" + content + ""; + } + + /** + * 为给定的字符串添加 HTML 字体倾斜 + *
+     *     如果需要倾斜自定义角度, 需要自定义 TextView, 在 onDraw 里面加上
+     *     - 倾斜度, 上下左右居中
+     *     canvas.rotate( 倾斜角度, getMeasuredWidth() / 3, getMeasuredHeight() / 3);
+     * 
+ * @param content 给定的字符串 + * @return Html 内容字符串 + */ + public static String addHtmlIncline(final String content) { + return "" + content + ""; + } + + // = + + /** + * 将给定的字符串中所有给定的关键字标色 + * @param content 给定的字符串 + * @param keyword 给定的关键字 + * @param color 颜色值, 如: #000000 + * @return Html 内容字符串 + */ + public static String keywordReplaceHtmlColor(final String content, final String keyword, final String color) { + return replaceStr(content, keyword, addHtmlColor(keyword, color)); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + // ================== + // = DevCommonUtils = + // ================== + + /** + * 替换字符串 + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @param replace 替换的内容 + * @return 处理后的字符串 + */ + private static String replaceStr(final String str, final String suffix, final String replace) { + // 如果替换的内容或者判断的字符串为 null, 则直接跳过 + if (!isEmpty(str) && !isEmpty(suffix) && replace != null && !suffix.equals(replace)) { + try { + return str.replaceAll(suffix, replace); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "replaceStr"); + } + } + return str; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ImageViewUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ImageViewUtils.java new file mode 100644 index 0000000000..e6dbb3c123 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ImageViewUtils.java @@ -0,0 +1,1092 @@ +package dev.utils.app; + +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.RequiresApi; +import android.util.DisplayMetrics; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import java.lang.reflect.Field; + +import dev.utils.LogPrintUtils; + +/** + * detail: ImageView 工具类 + * @author Ttt + */ +public final class ImageViewUtils { + + private ImageViewUtils() { + } + + // 日志 TAG + private static final String TAG = ImageViewUtils.class.getSimpleName(); + + // ================== + // = 获取 ImageView = + // ================== + + /** + * 获取 ImageView + * @param view {@link View} + * @param 泛型 + * @return {@link ImageView} + */ + public static T getImageView(final View view) { + if (view != null) { + try { + return (T) view; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getImageView"); + } + } + return null; + } + + // ======== + // = 宽高 = + // ======== + + /** + * 获取 ImageView 是否保持宽高比 + * @param imageView ImageView + * @return {@code true} yes, {@code false} no + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) + public static boolean getAdjustViewBounds(final ImageView imageView) { + if (imageView != null) { + return imageView.getAdjustViewBounds(); + } + return false; + } + + /** + * 设置 ImageView 是否保持宽高比 + * @param imageView ImageView + * @param adjustViewBounds 是否调整此视图的边界以保持可绘制的原始纵横比 + * @return {@link ImageView} + */ + public static ImageView setAdjustViewBounds(final ImageView imageView, final boolean adjustViewBounds) { + if (imageView != null) { + imageView.setAdjustViewBounds(adjustViewBounds); + } + return imageView; + } + + /** + * 获取 ImageView 最大高度 + * @param imageView ImageView + * @return view 最大高度 + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) + public static int getMaxHeight(final ImageView imageView) { + if (imageView != null) { + return imageView.getMaxHeight(); + } + return -1; + } + + /** + * 设置 ImageView 最大高度 + * @param imageView ImageView + * @param maxHeight 最大高度 + * @return {@link ImageView} + */ + public static ImageView setMaxHeight(final ImageView imageView, final int maxHeight) { + if (imageView != null) { + imageView.setMaxHeight(maxHeight); + } + return imageView; + } + + /** + * 获取 ImageView 最大宽度 + * @param imageView ImageView + * @return view 最大宽度 + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) + public static int getMaxWidth(final ImageView imageView) { + if (imageView != null) { + return imageView.getMaxWidth(); + } + return -1; + } + + /** + * 设置 ImageView 最大宽度 + * @param imageView ImageView + * @param maxWidth 最大宽度 + * @return {@link ImageView} + */ + public static ImageView setMaxWidth(final ImageView imageView, final int maxWidth) { + if (imageView != null) { + imageView.setMaxWidth(maxWidth); + } + return imageView; + } + + // ======== + // = 背景 = + // ======== + + /** + * 设置背景图片 + * @param view {@link View} + * @param background 背景图片 + * @return {@link View} + */ + public static View setBackground(final View view, final Drawable background) { + if (view != null) { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + view.setBackground(background); + } else { + view.setBackgroundDrawable(background); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setBackground"); + } + } + return view; + } + + /** + * 设置背景颜色 + * @param view {@link View} + * @param color 背景颜色 + * @return {@link View} + */ + public static View setBackgroundColor(final View view, @ColorInt final int color) { + if (view != null) { + try { + view.setBackgroundColor(color); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setBackgroundColor"); + } + } + return view; + } + + /** + * 设置背景资源 + * @param view {@link View} + * @param resId resource identifier + * @return {@link View} + */ + public static View setBackgroundResource(final View view, @DrawableRes final int resId) { + if (view != null) { + try { + view.setBackgroundResource(resId); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setBackgroundResource"); + } + } + return view; + } + + /** + * 设置背景着色颜色 + * @param view {@link View} + * @param tint 着色颜色 + * @return {@link View} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static View setBackgroundTintList(final View view, final ColorStateList tint) { + if (view != null) { + try { + view.setBackgroundTintList(tint); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setBackgroundTintList"); + } + } + return view; + } + + /** + * 设置背景着色模式 + * @param view {@link View} + * @param tintMode 着色模式 {@link PorterDuff.Mode} + * @return {@link View} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static View setBackgroundTintMode(final View view, final PorterDuff.Mode tintMode) { + if (view != null) { + try { + view.setBackgroundTintMode(tintMode); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setBackgroundTintMode"); + } + } + return view; + } + + // ======== + // = 前景 = + // ======== + + /** + * 设置前景图片 + * @param view {@link View} + * @param foreground 前景图片 + * @return {@link View} + */ + @RequiresApi(api = Build.VERSION_CODES.M) + public static View setForeground(final View view, final Drawable foreground) { + if (view != null) { + try { + view.setForeground(foreground); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setForeground"); + } + } + return view; + } + + /** + * 设置前景重心 + * @param view {@link View} + * @param gravity 重心 + * @return {@link View} + */ + @RequiresApi(api = Build.VERSION_CODES.M) + public static View setForegroundGravity(final View view, final int gravity) { + if (view != null) { + try { + view.setForegroundGravity(gravity); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setForegroundGravity"); + } + } + return view; + } + + /** + * 设置前景着色颜色 + * @param view {@link View} + * @param tint 着色颜色 + * @return {@link View} + */ + @RequiresApi(api = Build.VERSION_CODES.M) + public static View setForegroundTintList(final View view, final ColorStateList tint) { + if (view != null) { + try { + view.setForegroundTintList(tint); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setForegroundTintList"); + } + } + return view; + } + + /** + * 设置前景着色模式 + * @param view {@link View} + * @param tintMode 着色模式 {@link PorterDuff.Mode} + * @return {@link View} + */ + @RequiresApi(api = Build.VERSION_CODES.M) + public static View setForegroundTintMode(final View view, final PorterDuff.Mode tintMode) { + if (view != null) { + try { + view.setForegroundTintMode(tintMode); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setForegroundTintMode"); + } + } + return view; + } + + // ============= + // = ImageView = + // ============= + + /** + * 设置 ImageView Bitmap + * @param view {@link View} + * @param bitmap {@link Bitmap} + * @return {@link View} + */ + public static View setImageBitmap(final View view, final Bitmap bitmap) { + setImageBitmap(getImageView(view), bitmap); + return view; + } + + /** + * 设置 ImageView Bitmap + * @param imageView {@link ImageView} + * @param bitmap {@link Bitmap} + * @param 泛型 + * @return {@link ImageView} + */ + public static T setImageBitmap(final T imageView, final Bitmap bitmap) { + if (imageView != null) { + try { + imageView.setImageBitmap(bitmap); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setImageBitmap"); + } + } + return imageView; + } + + // = + + /** + * 设置 ImageView Drawable + * @param view {@link View} + * @param drawable {@link Bitmap} + * @return {@link View} + */ + public static View setImageDrawable(final View view, final Drawable drawable) { + setImageDrawable(getImageView(view), drawable); + return view; + } + + /** + * 设置 ImageView Drawable + * @param imageView {@link ImageView} + * @param drawable {@link Drawable} + * @param 泛型 + * @return {@link ImageView} + */ + public static T setImageDrawable(final T imageView, final Drawable drawable) { + if (imageView != null) { + try { + imageView.setImageDrawable(drawable); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setImageDrawable"); + } + } + return imageView; + } + + // = + + /** + * 设置 ImageView 资源 + * @param view {@link View} + * @param resId resource identifier + * @return {@link View} + */ + public static View setImageResource(final View view, @DrawableRes final int resId) { + setImageResource(getImageView(view), resId); + return view; + } + + /** + * 设置 ImageView 资源 + * @param imageView {@link ImageView} + * @param resId resource identifier + * @param 泛型 + * @return {@link ImageView} + */ + public static T setImageResource(final T imageView, @DrawableRes final int resId) { + if (imageView != null) { + try { + imageView.setImageResource(resId); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setImageResource"); + } + } + return imageView; + } + + // = + + /** + * 设置 ImageView Matrix + * @param view {@link View} + * @param matrix {@link Matrix} + * @return {@link View} + */ + public static View setImageMatrix(final View view, final Matrix matrix) { + setImageMatrix(getImageView(view), matrix); + return view; + } + + /** + * 设置 ImageView Matrix + * @param imageView {@link ImageView} + * @param matrix {@link Matrix} + * @param 泛型 + * @return {@link ImageView} + */ + public static T setImageMatrix(final T imageView, final Matrix matrix) { + if (imageView != null) { + try { + imageView.setImageMatrix(matrix); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setImageMatrix"); + } + } + return imageView; + } + + // = + + /** + * 设置 ImageView 着色颜色 + * @param view {@link View} + * @param tint 着色颜色 + * @return {@link View} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static View setImageTintList(final View view, final ColorStateList tint) { + setImageTintList(getImageView(view), tint); + return view; + } + + /** + * 设置 ImageView 着色颜色 + * @param imageView {@link ImageView} + * @param tint 着色颜色 + * @param 泛型 + * @return {@link ImageView} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static T setImageTintList(final T imageView, final ColorStateList tint) { + if (imageView != null) { + try { + imageView.setImageTintList(tint); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setImageTintList"); + } + } + return imageView; + } + + // = + + /** + * 设置 ImageView 着色模式 + * @param view {@link View} + * @param tintMode 着色模式 {@link PorterDuff.Mode} + * @return {@link View} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static View setImageTintMode(final View view, final PorterDuff.Mode tintMode) { + setImageTintMode(getImageView(view), tintMode); + return view; + } + + /** + * 设置 ImageView 着色模式 + * @param imageView {@link ImageView} + * @param tintMode 着色模式 {@link PorterDuff.Mode} + * @param 泛型 + * @return {@link ImageView} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static T setImageTintMode(final T imageView, final PorterDuff.Mode tintMode) { + if (imageView != null) { + try { + imageView.setImageTintMode(tintMode); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setImageTintMode"); + } + } + return imageView; + } + + // = + + /** + * 设置 ImageView 缩放类型 + * @param view {@link View} + * @param scaleType 缩放类型 {@link ImageView.ScaleType} + * @return {@link View} + */ + public static View setScaleType(final View view, final ImageView.ScaleType scaleType) { + setScaleType(getImageView(view), scaleType); + return view; + } + + /** + * 设置 ImageView 缩放类型 + * @param imageView {@link ImageView} + * @param scaleType 缩放类型 {@link ImageView.ScaleType} + * @param 泛型 + * @return {@link ImageView} + */ + public static T setScaleType(final T imageView, final ImageView.ScaleType scaleType) { + if (imageView != null) { + try { + imageView.setScaleType(scaleType); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setScaleType"); + } + } + return imageView; + } + + // ======== + // = 获取 = + // ======== + + /** + * 获取 View 背景 Drawable + * @param view {@link View} + * @return 背景 Drawable + */ + public static Drawable getBackground(final View view) { + if (view != null) return view.getBackground(); + return null; + } + + /** + * 获取 View 背景着色颜色 + * @param view {@link View} + * @return 背景着色颜色 {@link ColorStateList} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static ColorStateList getBackgroundTintList(final View view) { + if (view != null) return view.getBackgroundTintList(); + return null; + } + + /** + * 获取 View 背景着色模式 + * @param view {@link View} + * @return 背景着色模式 {@link PorterDuff.Mode} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static PorterDuff.Mode getBackgroundTintMode(final View view) { + if (view != null) return view.getBackgroundTintMode(); + return null; + } + + // = + + /** + * 获取 View 前景 Drawable + * @param view {@link View} + * @return 前景 Drawable + */ + @RequiresApi(api = Build.VERSION_CODES.M) + public static Drawable getForeground(final View view) { + if (view != null) return view.getForeground(); + return null; + } + + /** + * 获取 View 前景重心 + * @param view {@link View} + * @return 前景重心 {@link Gravity} + */ + @RequiresApi(api = Build.VERSION_CODES.M) + public static int getForegroundGravity(final View view) { + if (view != null) return view.getForegroundGravity(); + return Gravity.FILL; + } + + /** + * 获取 View 前景着色颜色 + * @param view {@link View} + * @return 前景着色颜色 {@link ColorStateList} + */ + @RequiresApi(api = Build.VERSION_CODES.M) + public static ColorStateList getForegroundTintList(final View view) { + if (view != null) return view.getForegroundTintList(); + return null; + } + + /** + * 获取 View 前景着色模式 + * @param view {@link View} + * @return 前景着色模式 {@link PorterDuff.Mode} + */ + @RequiresApi(api = Build.VERSION_CODES.M) + public static PorterDuff.Mode getForegroundTintMode(final View view) { + if (view != null) return view.getForegroundTintMode(); + return null; + } + + // = + + /** + * 获取 ImageView Matrix + * @param view {@link View} + * @return {@link Matrix} + */ + public static Matrix getImageMatrix(final View view) { + return getImageMatrix(getImageView(view)); + } + + /** + * 获取 ImageView Matrix + * @param imageView {@link ImageView} + * @param 泛型 + * @return {@link Matrix} + */ + public static Matrix getImageMatrix(final T imageView) { + if (imageView != null) return imageView.getImageMatrix(); + return null; + } + + // = + + /** + * 获取 ImageView 着色颜色 + * @param view {@link View} + * @return {@link ColorStateList} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static ColorStateList getImageTintList(final View view) { + return getImageTintList(getImageView(view)); + } + + /** + * 获取 ImageView 着色颜色 + * @param imageView {@link ImageView} + * @param 泛型 + * @return {@link ColorStateList} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static ColorStateList getImageTintList(final T imageView) { + if (imageView != null) return imageView.getImageTintList(); + return null; + } + + // = + + /** + * 获取 ImageView 着色模式 + * @param view {@link View} + * @return {@link PorterDuff.Mode} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static PorterDuff.Mode getImageTintMode(final View view) { + return getImageTintMode(getImageView(view)); + } + + /** + * 获取 ImageView 着色模式 + * @param imageView {@link ImageView} + * @param 泛型 + * @return {@link PorterDuff.Mode} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static PorterDuff.Mode getImageTintMode(final T imageView) { + if (imageView != null) return imageView.getImageTintMode(); + return null; + } + + // = + + /** + * 获取 ImageView 缩放模式 + * @param view {@link View} + * @return {@link ImageView.ScaleType} + */ + public static ImageView.ScaleType getScaleType(final View view) { + return getScaleType(getImageView(view)); + } + + /** + * 获取 ImageView 缩放模式 + * @param imageView {@link ImageView} + * @param 泛型 + * @return {@link ImageView.ScaleType} + */ + public static ImageView.ScaleType getScaleType(final T imageView) { + if (imageView != null) return imageView.getScaleType(); + return null; + } + + // = + + /** + * 获取 ImageView Drawable + * @param view {@link View} + * @return {@link Drawable} + */ + public static Drawable getDrawable(final View view) { + return getDrawable(getImageView(view)); + } + + /** + * 获取 ImageView Drawable + * @param imageView {@link ImageView} + * @param 泛型 + * @return {@link Drawable} + */ + public static Drawable getDrawable(final T imageView) { + if (imageView != null) return imageView.getDrawable(); + return null; + } + + // ============ + // = 着色处理 = + // ============ + + /** + * ImageView 着色处理 + * @param imageView {@link ImageView} + * @param color 颜色值 + * @param 泛型 + * @return {@link ImageView} + */ + public static T setColorFilter(final T imageView, @ColorInt final int color) { + return setColorFilter(imageView, getDrawable(imageView), color); + } + + /** + * ImageView 着色处理, 并且设置 Drawable + * @param imageView {@link ImageView} + * @param drawable {@link Drawable} + * @param color 颜色值 + * @param 泛型 + * @return {@link ImageView} + */ + public static T setColorFilter(final T imageView, final Drawable drawable, @ColorInt final int color) { + try { + setBackground(imageView, setColorFilter(drawable, color)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setColorFilter"); + } + return imageView; + } + + // = + + /** + * ImageView 着色处理 + * @param imageView {@link ImageView} + * @param colorFilter 颜色过滤 ( 效果 ) + * @param 泛型 + * @return {@link ImageView} + */ + public static T setColorFilter(final T imageView, final ColorFilter colorFilter) { + return setColorFilter(imageView, getDrawable(imageView), colorFilter); + } + + /** + * ImageView 着色处理, 并且设置 Drawable + * @param imageView {@link ImageView} + * @param drawable {@link Drawable} + * @param colorFilter 颜色过滤 ( 效果 ) + * @param 泛型 + * @return {@link ImageView} + */ + public static T setColorFilter(final T imageView, final Drawable drawable, final ColorFilter colorFilter) { + try { + setBackground(imageView, setColorFilter(drawable, colorFilter)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setColorFilter"); + } + return imageView; + } + + // ============ + // = 多个操作 = + // ============ + + /** + * 设置 View 图片资源 + * @param resId resource identifier + * @param views View[] + */ + public static void setBackgroundResources(@DrawableRes final int resId, final View... views) { + setBackgroundResources(resId, View.VISIBLE, views); + } + + /** + * 设置 View 图片资源 + * @param resId resource identifier + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + */ + public static void setBackgroundResources(@DrawableRes final int resId, final int isVisibility, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view != null) { + try { + // 设置显示状态 + view.setVisibility(isVisibility); + // 设置图片资源 + view.setBackgroundResource(resId); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setBackgroundResources"); + } + } + } + } + } + + // = + + /** + * 设置 View 图片资源 + * @param resId resource identifier + * @param views View[] + */ + public static void setImageResources(@DrawableRes final int resId, final View... views) { + setImageResources(resId, View.VISIBLE, views); + } + + /** + * 设置 View 图片资源 + * @param resId resource identifier + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + */ + public static void setImageResources(@DrawableRes final int resId, final int isVisibility, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + ImageView imageView = getImageView(views[i]); + if (imageView != null) { + try { + // 设置显示状态 + imageView.setVisibility(isVisibility); + // 设置图片资源 + imageView.setImageResource(resId); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setImageResources"); + } + } + } + } + } + + // = + + /** + * 设置 View Bitmap + * @param bitmap {@link Bitmap} + * @param views View[] + */ + public static void setImageBitmaps(final Bitmap bitmap, final View... views) { + setImageBitmaps(bitmap, View.VISIBLE, views); + } + + /** + * 设置 View Bitmap + * @param bitmap {@link Bitmap} + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + */ + public static void setImageBitmaps(final Bitmap bitmap, final int isVisibility, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + ImageView imageView = getImageView(views[i]); + if (imageView != null) { + try { + // 设置显示状态 + imageView.setVisibility(isVisibility); + // 设置 Bitmap + imageView.setImageBitmap(bitmap); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setImageBitmaps"); + } + } + } + } + } + + // = + + /** + * 设置 View Drawable + * @param drawable {@link drawable} + * @param views View[] + */ + public static void setImageDrawables(final Drawable drawable, final View... views) { + setImageDrawables(drawable, View.VISIBLE, views); + } + + /** + * 设置 View Drawable + * @param drawable {@link drawable} + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + */ + public static void setImageDrawables(final Drawable drawable, final int isVisibility, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + ImageView imageView = getImageView(views[i]); + if (imageView != null) { + try { + // 设置显示状态 + imageView.setVisibility(isVisibility); + // 设置 Drawable + imageView.setImageDrawable(drawable); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setImageDrawables"); + } + } + } + } + } + + // = + + /** + * 设置 View 缩放模式 + * @param scaleType {@link ImageView.ScaleType} + * @param views View[] + */ + public static void setScaleTypes(final ImageView.ScaleType scaleType, final View... views) { + setScaleTypes(scaleType, View.VISIBLE, views); + } + + /** + * 设置 View 缩放模式 + * @param scaleType {@link ImageView.ScaleType} + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + */ + public static void setScaleTypes(final ImageView.ScaleType scaleType, final int isVisibility, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + ImageView imageView = getImageView(views[i]); + if (imageView != null) { + try { + // 设置显示状态 + imageView.setVisibility(isVisibility); + // 设置缩放模式 + imageView.setScaleType(scaleType); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setScaleTypes"); + } + } + } + } + } + + // ============ + // = 信息获取 = + // ============ + + /** + * 根据 ImageView 获适当的宽高 + * @param imageView {@link ImageView} + * @return 宽高, 0 = 宽, 1 = 高 + */ + public static int[] getImageViewSize(final ImageView imageView) { + int[] imageSize = new int[]{0, 0}; + try { + if (imageView == null) return imageSize; + // = + DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics(); + ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams(); + + // 获取 ImageView 的实际宽度 + int width = imageView.getWidth(); + if (width <= 0) { + width = layoutParams.width; // 获取 imageView 在 layout 中声明的宽度 + } + if (width <= 0) { +// width = imageView.getMaxWidth(); // 检查最大值 + width = getImageViewFieldValue(imageView, "mMaxWidth"); + } + if (width <= 0) { + width = displayMetrics.widthPixels; + } + + // 获取 ImageView 的实际高度 + int height = imageView.getHeight(); + if (height <= 0) { + height = layoutParams.height; // 获取 imageView 在 layout 中声明的高度 + } + if (height <= 0) { +// height = imageView.getMaxHeight(); // 检查最大值 + height = getImageViewFieldValue(imageView, "mMaxHeight"); // 检查最大值 + } + if (height <= 0) { + height = displayMetrics.heightPixels; + } + + // 填充宽高信息 + imageSize[0] = width; + imageSize[1] = height; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getImageViewSize"); + } + return imageSize; + } + + /** + * 通过反射获取 ImageView 的属性值 + * @param object 对象 + * @param fieldName 属性名 + * @return 指定属性的值 + */ + private static int getImageViewFieldValue(final Object object, final String fieldName) { + int value = 0; + try { + Field field = ImageView.class.getDeclaredField(fieldName); + field.setAccessible(true); + int fieldValue = field.getInt(object); + if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) { + value = fieldValue; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getImageViewFieldValue"); + } + return value; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============== + // = ImageUtils = + // ============== + + /** + * 图片着色 - tint + * @param drawable {@link Drawable} + * @param color 颜色值 + * @return 着色后的 {@link Drawable} + */ + private static Drawable setColorFilter(final Drawable drawable, @ColorInt final int color) { + return setColorFilter(drawable, color, PorterDuff.Mode.SRC_IN); + } + + /** + * 图片着色 - tint + * @param drawable {@link Drawable} + * @param color 颜色值 + * @param mode 着色模式 {@link PorterDuff.Mode} + * @return 着色后的 {@link Drawable} + */ + private static Drawable setColorFilter(final Drawable drawable, @ColorInt final int color, final PorterDuff.Mode mode) { + if (drawable != null && mode != null) { + try { + drawable.setColorFilter(color, mode); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setColorFilter"); + } + } + return drawable; + } + + /** + * 图片着色 - tint + * @param drawable {@link Drawable} + * @param colorFilter 颜色过滤 ( 效果 ) + * @return 着色后的 {@link Drawable} + */ + private static Drawable setColorFilter(final Drawable drawable, final ColorFilter colorFilter) { + if (drawable != null && colorFilter != null) { + try { + drawable.setColorFilter(colorFilter); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setColorFilter"); + } + } + return drawable; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/IntentUtils.java b/lib/DevApp/src/main/java/dev/utils/app/IntentUtils.java new file mode 100644 index 0000000000..a3786c3c73 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/IntentUtils.java @@ -0,0 +1,608 @@ +package dev.utils.app; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.MediaStore; +import android.provider.Settings; +import android.support.annotation.RequiresPermission; +import android.support.v4.content.FileProvider; +import android.text.TextUtils; + +import java.io.File; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Intent 相关工具类 + * @author Ttt + *
+ *     所需权限
+ *     
+ *     
+ *     
+ * 
+ */ +public final class IntentUtils { + + private IntentUtils() { + } + + // 日志 TAG + private static final String TAG = IntentUtils.class.getSimpleName(); + + /** + * 获取 Intent + * @param intent {@link Intent} + * @param isNewTask 是否开启新的任务栈 (Context 非 Activity 则需要设置 FLAG_ACTIVITY_NEW_TASK) + * @return {@link Intent} + */ + public static Intent getIntent(final Intent intent, final boolean isNewTask) { + if (intent != null) { + return isNewTask ? intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) : intent; + } + return null; + } + + /** + * 判断 Intent 是否可用 + * @param intent {@link Intent} + * @return {@code true} yes, {@code false} no + */ + public static boolean isIntentAvailable(final Intent intent) { + if (intent != null) { + try { + return DevUtils.getContext().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isIntentAvailable"); + } + } + return false; + } + + /** + * 获取安装 APP( 支持 8.0) 的意图 + * @param filePath 文件路径 + * @return 安装 APP( 支持 8.0) 的意图 + */ + public static Intent getInstallAppIntent(final String filePath) { + return getInstallAppIntent(getFileByPath(filePath)); + } + + /** + * 获取安装 APP( 支持 8.0) 的意图 + * @param file 文件 + * @return 安装 APP( 支持 8.0) 的意图 + */ + public static Intent getInstallAppIntent(final File file) { + return getInstallAppIntent(file, false); + } + + /** + * 获取安装 APP( 支持 8.0) 的意图 + * @param file 文件 + * @param isNewTask 是否开启新的任务栈 + * @return 安装 APP( 支持 8.0) 的意图 + */ + public static Intent getInstallAppIntent(final File file, final boolean isNewTask) { + if (file == null) return null; + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + Uri data; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + data = Uri.fromFile(file); + } else { + data = FileProvider.getUriForFile(DevUtils.getContext(), DevUtils.getAuthority(), file); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + intent.setDataAndType(data, "application/vnd.android.package-archive"); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getInstallAppIntent"); + } + return null; + } + + /** + * 获取卸载 APP 的意图 + * @param packageName 应用包名 + * @return 卸载 APP 的意图 + */ + public static Intent getUninstallAppIntent(final String packageName) { + return getUninstallAppIntent(packageName, false); + } + + /** + * 获取卸载 APP 的意图 + * @param packageName 应用包名 + * @param isNewTask 是否开启新的任务栈 + * @return 卸载 APP 的意图 + */ + public static Intent getUninstallAppIntent(final String packageName, final boolean isNewTask) { + try { + Intent intent = new Intent(Intent.ACTION_DELETE); + intent.setData(Uri.parse("package:" + packageName)); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getUninstallAppIntent"); + } + return null; + } + + /** + * 获取打开 APP 的意图 + * @param packageName 应用包名 + * @return 打开 APP 的意图 + */ + public static Intent getLaunchAppIntent(final String packageName) { + return getLaunchAppIntent(packageName, false); + } + + /** + * 获取打开 APP 的意图 + * @param packageName 应用包名 + * @param isNewTask 是否开启新的任务栈 + * @return 打开 APP 的意图 + */ + public static Intent getLaunchAppIntent(final String packageName, final boolean isNewTask) { + try { + Intent intent = DevUtils.getContext().getPackageManager().getLaunchIntentForPackage(packageName); + if (intent == null) return null; + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLaunchAppIntent"); + } + return null; + } + + /** + * 获取跳转到系统设置的意图 + * @param isNewTask 是否开启新的任务栈 + * @return 跳转到系统设置的意图 + */ + public static Intent getSystemSettingIntent(final boolean isNewTask) { + try { + Intent intent = new Intent(Settings.ACTION_SETTINGS); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSystemSettingIntent"); + } + return null; + } + + /** + * 获取 APP 通知权限设置的意图 + * @param packageName 应用包名 + * @return APP 通知权限设置的意图 + */ + public static Intent getLaunchAppNotificationSettingsIntent(final String packageName) { + return getLaunchAppNotificationSettingsIntent(packageName, false); + } + + /** + * 获取 APP 通知权限设置的意图 + * @param packageName 应用包名 + * @param isNewTask 是否开启新的任务栈 + * @return APP 通知权限设置的意图 + */ + public static Intent getLaunchAppNotificationSettingsIntent(final String packageName, final boolean isNewTask) { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); + ApplicationInfo applicationInfo = packageInfo.applicationInfo; + + Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS); + // 这种方案适用于 API 26 即 8.0 ( 含 8.0) 以上可以用 + intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName); + intent.putExtra(Settings.EXTRA_CHANNEL_ID, applicationInfo.uid); + // 这种方案适用于 API 21 - 25 即 5.0 - 7.1 之间的版本可以使用 + intent.putExtra("app_package", packageName); + intent.putExtra("app_uid", applicationInfo.uid); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLaunchAppNotificationSettingsIntent"); + } + return null; + } + + /** + * 获取 APP 具体设置的意图 + * @param packageName 应用包名 + * @return APP 具体设置的意图 + */ + public static Intent getLaunchAppDetailsSettingsIntent(final String packageName) { + return getLaunchAppDetailsSettingsIntent(packageName, false); + } + + /** + * 获取 APP 具体设置的意图 + * @param packageName 应用包名 + * @param isNewTask 是否开启新的任务栈 + * @return APP 具体设置的意图 + */ + public static Intent getLaunchAppDetailsSettingsIntent(final String packageName, final boolean isNewTask) { + try { + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.parse("package:" + packageName)); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLaunchAppDetailsSettingsIntent"); + } + return null; + } + + /** + * 获取到应用商店 APP 详情界面的意图 + * @param packageName 应用包名 + * @param marketPkg 应用商店包名, 如果为 "" 则由系统弹出应用商店列表供用户选择, 否则调转到目标市场的应用详情界面, 某些应用商店可能会失败 + * @return 到应用商店 APP 详情界面的意图 + */ + public static Intent getLaunchAppDetailIntent(final String packageName, final String marketPkg) { + return getLaunchAppDetailIntent(packageName, marketPkg, false); + } + + /** + * 获取到应用商店 APP 详情界面的意图 + * @param packageName 应用包名 + * @param marketPkg 应用商店包名, 如果为 "" 则由系统弹出应用商店列表供用户选择, 否则调转到目标市场的应用详情界面, 某些应用商店可能会失败 + * @param isNewTask 是否开启新的任务栈 + * @return 到应用商店 APP 详情界面的意图 + */ + public static Intent getLaunchAppDetailIntent(final String packageName, final String marketPkg, final boolean isNewTask) { + try { + if (TextUtils.isEmpty(packageName)) return null; + + Uri uri = Uri.parse("market://details?id=" + packageName); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + if (!TextUtils.isEmpty(marketPkg)) { + intent.setPackage(marketPkg); + } + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLaunchAppDetailIntent"); + } + return null; + } + + // = + + /** + * 获取分享文本的意图 + * @param content 分享文本 + * @return 分享文本的意图 + */ + public static Intent getShareTextIntent(final String content) { + return getShareTextIntent(content, false); + } + + /** + * 获取分享文本的意图 + * @param content 分享文本 + * @param isNewTask 是否开启新的任务栈 + * @return 分享文本的意图 + */ + public static Intent getShareTextIntent(final String content, final boolean isNewTask) { + try { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TEXT, content); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getShareTextIntent"); + } + return null; + } + + /** + * 获取分享图片的意图 + * @param content 文本 + * @param imagePath 图片文件路径 + * @return 分享图片的意图 + */ + public static Intent getShareImageIntent(final String content, final String imagePath) { + return getShareImageIntent(content, imagePath, false); + } + + /** + * 获取分享图片的意图 + * @param content 文本 + * @param imagePath 图片文件路径 + * @param isNewTask 是否开启新的任务栈 + * @return 分享图片的意图 + */ + public static Intent getShareImageIntent(final String content, final String imagePath, final boolean isNewTask) { + try { + return getShareImageIntent(content, getFileByPath(imagePath), isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getShareImageIntent"); + } + return null; + } + + /** + * 获取分享图片的意图 + * @param content 文本 + * @param image 图片文件 + * @return 分享图片的意图 + */ + public static Intent getShareImageIntent(final String content, final File image) { + return getShareImageIntent(content, image, false); + } + + /** + * 获取分享图片的意图 + * @param content 文本 + * @param image 图片文件 + * @param isNewTask 是否开启新的任务栈 + * @return 分享图片的意图 + */ + public static Intent getShareImageIntent(final String content, final File image, final boolean isNewTask) { + try { + return getShareImageIntent(content, Uri.fromFile(image), isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getShareImageIntent"); + } + return null; + } + + /** + * 获取分享图片的意图 + * @param content 分享文本 + * @param uri 图片 uri + * @return 分享图片的意图 + */ + public static Intent getShareImageIntent(final String content, final Uri uri) { + return getShareImageIntent(content, uri, false); + } + + /** + * 获取分享图片的意图 + * @param content 分享文本 + * @param uri 图片 uri + * @param isNewTask 是否开启新的任务栈 + * @return 分享图片的意图 + */ + public static Intent getShareImageIntent(final String content, final Uri uri, final boolean isNewTask) { + try { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_TEXT, content); + intent.putExtra(Intent.EXTRA_STREAM, uri); + intent.setType("image/*"); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getShareImageIntent"); + } + return null; + } + + /** + * 获取其他应用组件的意图 + * @param packageName 应用包名 + * @param className class.getCanonicalName() + * @return 其他应用组件的意图 + */ + public static Intent getComponentIntent(final String packageName, final String className) { + return getComponentIntent(packageName, className, null, false); + } + + /** + * 获取其他应用组件的意图 + * @param packageName 应用包名 + * @param className class.getCanonicalName() + * @param isNewTask 是否开启新的任务栈 + * @return 其他应用组件的意图 + */ + public static Intent getComponentIntent(final String packageName, final String className, final boolean isNewTask) { + return getComponentIntent(packageName, className, null, isNewTask); + } + + /** + * 获取其他应用组件的意图 + * @param packageName 应用包名 + * @param className class.getCanonicalName() + * @param bundle {@link Bundle} + * @return 其他应用组件的意图 + */ + public static Intent getComponentIntent(final String packageName, final String className, final Bundle bundle) { + return getComponentIntent(packageName, className, bundle, false); + } + + /** + * 获取其他应用组件的意图 + * @param packageName 应用包名 + * @param className class.getCanonicalName() + * @param bundle {@link Bundle} + * @param isNewTask 是否开启新的任务栈 + * @return 其他应用组件的意图 + */ + public static Intent getComponentIntent(final String packageName, final String className, final Bundle bundle, final boolean isNewTask) { + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + if (bundle != null) intent.putExtras(bundle); + ComponentName componentName = new ComponentName(packageName, className); + intent.setComponent(componentName); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getComponentIntent"); + } + return null; + } + + /** + * 获取关机的意图 + * @return 关机的意图 + */ + public static Intent getShutdownIntent() { + return getShutdownIntent(false); + } + + /** + * 获取关机的意图 + * @param isNewTask 是否开启新的任务栈 + * @return 关机的意图 + */ + public static Intent getShutdownIntent(final boolean isNewTask) { + try { + Intent intent = new Intent(Intent.ACTION_SHUTDOWN); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getShutdownIntent"); + } + return null; + } + + /** + * 获取跳至拨号界面意图 + * @param phoneNumber 电话号码 + * @return 跳至拨号界面意图 + */ + public static Intent getDialIntent(final String phoneNumber) { + return getDialIntent(phoneNumber, false); + } + + /** + * 获取跳至拨号界面意图 + * @param phoneNumber 电话号码 + * @param isNewTask 是否开启新的任务栈 + * @return 跳至拨号界面意图 + */ + public static Intent getDialIntent(final String phoneNumber, final boolean isNewTask) { + try { + Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber)); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDialIntent"); + } + return null; + } + + /** + * 获取拨打电话意图 + * @param phoneNumber 电话号码 + * @return 拨打电话意图 + */ + @RequiresPermission(android.Manifest.permission.CALL_PHONE) + public static Intent getCallIntent(final String phoneNumber) { + return getCallIntent(phoneNumber, false); + } + + /** + * 获取拨打电话意图 + * @param phoneNumber 电话号码 + * @param isNewTask 是否开启新的任务栈 + * @return 拨打电话意图 + */ + @RequiresPermission(android.Manifest.permission.CALL_PHONE) + public static Intent getCallIntent(final String phoneNumber, final boolean isNewTask) { + try { + Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNumber)); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getCallIntent"); + } + return null; + } + + /** + * 获取发送短信界面的意图 + * @param phoneNumber 接收号码 + * @param content 短信内容 + * @return 发送短信界面的意图 + */ + public static Intent getSendSmsIntent(final String phoneNumber, final String content) { + return getSendSmsIntent(phoneNumber, content, false); + } + + /** + * 获取跳至发送短信界面的意图 + * @param phoneNumber 接收号码 + * @param content 短信内容 + * @param isNewTask 是否开启新的任务栈 + * @return 发送短信界面的意图 + */ + public static Intent getSendSmsIntent(final String phoneNumber, final String content, final boolean isNewTask) { + try { + Uri uri = Uri.parse("smsto:" + phoneNumber); + Intent intent = new Intent(Intent.ACTION_SENDTO, uri); + intent.putExtra("sms_body", content); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSendSmsIntent"); + } + return null; + } + + /** + * 获取拍照的意图 + * @param outUri 输出的 uri ( 保存地址 ) + * @return 拍照的意图 + */ + public static Intent getCaptureIntent(final Uri outUri) { + return getCaptureIntent(outUri, false); + } + + /** + * 获取拍照的意图 + * @param outUri 输出的 uri ( 保存地址 ) + * @param isNewTask 是否开启新的任务栈 + * @return 拍照的意图 + */ + public static Intent getCaptureIntent(final Uri outUri, final boolean isNewTask) { + try { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + return getIntent(intent, isNewTask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getCaptureIntent"); + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = FileUtils = + // ============= + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + // ============ + // = UriUtils = + // ============ + + /** + * 获取文件 Uri + * @param file 文件 + * @param authority android:authorities + * @return 指定文件 {@link Uri} + */ + private static Uri getUriForFile(final File file, final String authority) { + if (file == null || authority == null) return null; + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return FileProvider.getUriForFile(DevUtils.getContext(), authority, file); + } else { + return Uri.fromFile(file); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getUriForFile"); + return null; + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/JSONObjectUtils.java b/lib/DevApp/src/main/java/dev/utils/app/JSONObjectUtils.java new file mode 100644 index 0000000000..1e60a33400 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/JSONObjectUtils.java @@ -0,0 +1,305 @@ +package dev.utils.app; + +import android.os.Build; +import android.text.TextUtils; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONTokener; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Map; + +import dev.utils.LogPrintUtils; + +/** + * detail: Android 原生 JSONObject 工具类 + * @author Ttt + */ +public final class JSONObjectUtils { + + private JSONObjectUtils() { + } + + // 日志 TAG + private static final String TAG = JSONObjectUtils.class.getSimpleName(); + // JSON 格式内容 默认缩进 + public static final int JSON_INDENT = 4; + + // ==================== + // = 转换 JSON 字符串 = + // ==================== + + /** + * 转换为 JSON 格式字符串 + *
+     *     TODO 不支持 实体类 转 JSON 字符串
+     * 
+ * @param object Object + * @return JSON String + */ + public static String toJson(final Object object) { + return toJson(object, -1); + } + + /** + * 转换为 JSON 格式字符串 + *
+     *     TODO 不支持 实体类 转 JSON 字符串
+     * 
+ * @param object Object + * @param jsonIndent JSON 缩进间隔 + * @return JSON String + */ + public static String toJson(final Object object, final int jsonIndent) { + if (object == null) return null; + // 判断是否格式化 + boolean format = jsonIndent >= 1; + try { + if (object instanceof String) { + String json = (String) object; + if (json.startsWith("{")) { + JSONObject jsonObject = new JSONObject(json); + return format ? jsonObject.toString(jsonIndent) : jsonObject.toString(); + } else if (json.startsWith("[")) { + JSONArray jsonArray = new JSONArray(json); + return format ? jsonArray.toString(jsonIndent) : jsonArray.toString(); + } + } else if (object instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) object; + return format ? jsonObject.toString(jsonIndent) : jsonObject.toString(); + } else if (object instanceof JSONArray) { + JSONArray jsonArray = (JSONArray) object; + return format ? jsonArray.toString(jsonIndent) : jsonArray.toString(); + } else if (object instanceof Map) { + JSONObject jsonObject = new JSONObject((Map) object); + return format ? jsonObject.toString(jsonIndent) : jsonObject.toString(); + } else if (object instanceof Collection) { + JSONArray jsonArray = new JSONArray((Collection) object); + return format ? jsonArray.toString(jsonIndent) : jsonArray.toString(); + } else if (object.getClass().isArray()) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + JSONArray jsonArray = new JSONArray(object); + return format ? jsonArray.toString(jsonIndent) : jsonArray.toString(); + } else { + JSONArray jsonArray = new JSONArray(); + int length = Array.getLength(object); + for (int i = 0; i < length; ++i) { + jsonArray.put(wrap(Array.get(object, i))); + } + return format ? jsonArray.toString(jsonIndent) : jsonArray.toString(); + } + } else if (object instanceof JSONTokener) { + JSONTokener jsonTokener = (JSONTokener) object; + // 获取 value 对象 + Object tokenerObj = jsonTokener.nextValue(); + // 判断是什么格式 + if (tokenerObj instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) tokenerObj; + return format ? jsonObject.toString(jsonIndent) : jsonObject.toString(); + } else if (tokenerObj instanceof JSONArray) { + JSONArray jsonArray = (JSONArray) tokenerObj; + return format ? jsonArray.toString(jsonIndent) : jsonArray.toString(); + } + } + // 抛出不支持的类型 + throw new Exception("Value " + object + " of className" + object.getClass().getName()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "toJson"); + } + return null; + } + + // ================== + // = 转换 JSON 对象 = + // ================== + + /** + * Object 转换 JSON 对象 + *
+     *     fromJson(xx, JSONObject.class);
+     *     fromJson(xx, JSONArray.class);
+     *     fromJson(xx, JSONTokener.class);
+     * 
+ * @param object Object + * @param type JSONObject.class || JSONArray.class || JSONTokener.class + * @param 泛型 + * @return 指定 type JSON 对象 + */ + public static T fromJson(final Object object, final Class type) { + if (object == null || type == null) return null; + try { + if (type.equals(JSONObject.class)) { + if (object instanceof JSONObject) { + return (T) object; + } else if (object instanceof String) { + return (T) new JSONObject((String) object); + } else if (object instanceof JSONTokener) { + return (T) new JSONObject((JSONTokener) object); + } else if (object instanceof Map) { + return (T) new JSONObject((Map) object); + } + } else if (type.equals(JSONArray.class)) { + if (object instanceof JSONArray) { + return (T) object; + } else if (object instanceof String) { + return (T) new JSONArray((String) object); + } else if (object instanceof JSONTokener) { + return (T) new JSONArray((JSONTokener) object); + } else if (object instanceof Collection) { + return (T) new JSONArray((Collection) object); + } else if (object.getClass().isArray()) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + return (T) new JSONArray(object); + } else { + JSONArray jsonArray = new JSONArray(); + int length = Array.getLength(object); + for (int i = 0; i < length; ++i) { + jsonArray.put(wrap(Array.get(object, i))); + } + return (T) jsonArray; + } + } + } else if (type.equals(JSONTokener.class)) { + if (object instanceof String) { + return (T) new JSONTokener((String) object); + } + } + // 抛出不支持的类型 + throw new Exception("Value " + object + " of className" + object.getClass().getName() + + " converted Type " + type.getCanonicalName()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "fromJson"); + } + return null; + } + + // ============ + // = 其他处理 = + // ============ + + /** + * 包装转换 Object - {@link JSONObject#wrap(Object)} + * @param object Object + * @return 转换后的 Object + */ + public static Object wrap(final Object object) { + if (object == null) return null; + if (object instanceof JSONArray || object instanceof JSONObject) { + return object; + } + try { + if (object instanceof Collection) { + return new JSONArray((Collection) object); + } else if (object.getClass().isArray()) { + // 版本兼容 + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + return new JSONArray(object); + } else { + JSONArray jsonArray = new JSONArray(); + int length = Array.getLength(object); + for (int i = 0; i < length; ++i) { + jsonArray.put(wrap(Array.get(object, i))); + } + return jsonArray; + } + } + if (object instanceof Map) { + return new JSONObject((Map) object); + } + if (object instanceof Boolean || + object instanceof Byte || + object instanceof Character || + object instanceof Double || + object instanceof Float || + object instanceof Integer || + object instanceof Long || + object instanceof Short || + object instanceof String) { + return object; + } + if (object.getClass().getPackage().getName().startsWith("java.")) { + return object.toString(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "wrap"); + } + return null; + } + + /** + * 字符串 JSON 转义处理 + * @param str 字符串 + * @return 转义后的 JSON 字符串 + */ + public static String stringJSONEscape(final String str) { + if (str == null) return ""; + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + switch (ch) { + case '"': + builder.append("\\\""); + break; + case '\\': + builder.append("\\\\"); + break; + case '\b': + builder.append("\\b"); + break; + case '\f': + builder.append("\\f"); + break; + case '\n': + builder.append("\\n"); + break; + case '\r': + builder.append("\\r"); + break; + case '\t': + builder.append("\\t"); + break; + case '/': + builder.append("\\/"); + break; + default: + if (ch >= '\u0000' && ch <= '\u001F') { + String ss = Integer.toHexString(ch); + builder.append("\\u"); + for (int k = 0; k < 4 - ss.length(); k++) { + builder.append('0'); + } + builder.append(ss.toUpperCase()); + } else { + builder.append(ch); + } + } + } + return builder.toString(); + } + + /** + * 判断字符串是否 JSON 格式 + * @param json 待校验 JSON String + * @return {@code true} yes, {@code false} no + */ + public static boolean isJSON(final String json) { + if (!TextUtils.isEmpty(json)) { + if (json.startsWith("[") && json.endsWith("]")) { + try { + new JSONArray(json); + return true; + } catch (Exception e) { + } + } else if (json.startsWith("{") && json.endsWith("}")) { + try { + new JSONObject(json); + return true; + } catch (Exception e) { + } + } + } + return false; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/KeyBoardUtils.java b/lib/DevApp/src/main/java/dev/utils/app/KeyBoardUtils.java new file mode 100644 index 0000000000..e22592e89c --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/KeyBoardUtils.java @@ -0,0 +1,548 @@ +package dev.utils.app; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.graphics.Rect; +import android.os.Handler; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +import java.lang.reflect.Field; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 软键盘相关工具类 + * @author Ttt + *
+ *     避免输入法面板遮挡 manifest.xml 中 activity 中设置
+ *     android:windowSoftInputMode="adjustPan"
+ *     android:windowSoftInputMode="adjustUnspecified|stateHidden"
+ * 
+ */ +public final class KeyBoardUtils { + + private KeyBoardUtils() { + } + + // 日志 TAG + private static final String TAG = KeyBoardUtils.class.getSimpleName(); + // 默认延迟时间 ( 毫秒 ) + private static int DELAY_MILLIS = 300; + // 键盘显示 + public static final int KEYBOARD_DISPLAY = 930; + // 键盘隐藏 + public static final int KEYBOARD_HIDE = 931; + + /** + * 设置延迟时间 + * @param delayMillis 延迟时间 ( 毫秒 ) + */ + public static void setDelayMillis(final int delayMillis) { + DELAY_MILLIS = delayMillis; + } + + // ============== + // = 打开软键盘 = + // ============== + + /** + * 打开软键盘 + * @param editText {@link EditText} + */ + public static void openKeyboard(final EditText editText) { + if (editText != null) { + try { + InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED); + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openKeyboard"); + } + } + } + + /** + * 打开软键盘 + * @param editText {@link EditText} + * @param handler {@link Handler} + */ + public static void openKeyboard(final EditText editText, final Handler handler) { + openKeyboard(editText, handler, DELAY_MILLIS); + } + + /** + * 打开软键盘 + * @param editText {@link EditText} + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + */ + public static void openKeyboard(final EditText editText, final Handler handler, final int delayMillis) { + if (editText != null && handler != null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + try { + editText.requestFocus(); + editText.setSelection(editText.getText().toString().length()); + } catch (Exception e) { + } + openKeyboard(editText); + } + }, delayMillis); + } + } + + // = + + /** + * 打开软键盘 + */ + public static void openKeyboard() { + try { + InputMethodManager imm = (InputMethodManager) DevUtils.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openKeyboard"); + } + } + + /** + * 打开软键盘 + * @param handler {@link Handler} + */ + public static void openKeyboard(final Handler handler) { + openKeyboard(handler, DELAY_MILLIS); + } + + /** + * 打开软键盘 + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + */ + public static void openKeyboard(final Handler handler, final int delayMillis) { + if (DevUtils.getContext() != null && handler != null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + openKeyboard(); + } + }, delayMillis); + } + } + + // ============== + // = 关闭软键盘 = + // ============== + + /** + * 关闭软键盘 + * @param editText {@link EditText} + */ + public static void closeKeyboard(final EditText editText) { + if (editText != null) { + try { + InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeKeyboard"); + } + } + } + + /** + * 关闭软键盘 + */ + public static void closeKeyboard() { + if (DevUtils.getContext() != null) { + try { + InputMethodManager imm = (InputMethodManager) DevUtils.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeKeyboard"); + } + } + } + + /** + * 关闭软键盘 + * @param activity {@link Activity} + */ + public static void closeKeyboard(final Activity activity) { + if (activity != null) { + try { + InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(activity.getWindow().peekDecorView().getWindowToken(), 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeKeyboard"); + } + } + } + + /** + * 关闭 dialog 中打开的键盘 + * @param dialog {@link Dialog} + */ + public static void closeKeyboard(final Dialog dialog) { + if (dialog != null) { + try { + InputMethodManager imm = (InputMethodManager) dialog.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(dialog.getWindow().peekDecorView().getWindowToken(), 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeKeyboard"); + } + } + } + + // = + + /** + * 关闭软键盘 - 特殊处理 + * @param editText {@link EditText} + * @param dialog {@link Dialog} + */ + public static void closeKeyBoardSpecial(final EditText editText, final Dialog dialog) { + try { + // 关闭输入法 + closeKeyboard(); + // 关闭输入法 + closeKeyboard(editText); + // 关闭输入法 + closeKeyboard(dialog); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeKeyBoardSpecial"); + } + } + + /** + * 关闭软键盘 - 特殊处理 + * @param editText {@link EditText} + * @param dialog {@link Dialog} + * @param handler {@link Handler} + */ + public static void closeKeyBoardSpecial(final EditText editText, final Dialog dialog, final Handler handler) { + closeKeyBoardSpecial(editText, dialog, handler, DELAY_MILLIS); + } + + /** + * 关闭软键盘 - 特殊处理 ( 两个都关闭 ) + * @param editText {@link EditText} + * @param dialog {@link Dialog} + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + */ + public static void closeKeyBoardSpecial(final EditText editText, final Dialog dialog, final Handler handler, final int delayMillis) { + if (handler != null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + closeKeyBoardSpecial(editText, dialog); + } + }, delayMillis); + } + } + + // = + + /** + * 关闭软键盘 + * @param editText {@link EditText} + * @param handler {@link Handler} + */ + public static void closeKeyboard(final EditText editText, final Handler handler) { + closeKeyboard(editText, handler, DELAY_MILLIS); + } + + /** + * 关闭软键盘 + * @param editText {@link EditText} + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + */ + public static void closeKeyboard(final EditText editText, final Handler handler, final int delayMillis) { + if (editText != null && handler != null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + closeKeyboard(editText); + } + }, delayMillis); + } + } + + /** + * 关闭软键盘 + * @param handler {@link Handler} + */ + public static void closeKeyboard(final Handler handler) { + closeKeyboard(handler, DELAY_MILLIS); + } + + /** + * 关闭软键盘 + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + */ + public static void closeKeyboard(final Handler handler, final int delayMillis) { + if (DevUtils.getContext() != null && handler != null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + closeKeyboard(); + } + }, delayMillis); + } + } + + /** + * 关闭软键盘 + * @param activity {@link Activity} + * @param handler {@link Handler} + */ + public static void closeKeyboard(final Activity activity, final Handler handler) { + closeKeyboard(activity, handler, DELAY_MILLIS); + } + + /** + * 关闭软键盘 + * @param activity {@link Activity} + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + */ + public static void closeKeyboard(final Activity activity, final Handler handler, final int delayMillis) { + if (activity != null && handler != null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + closeKeyboard(activity); + } + }, delayMillis); + } + } + + /** + * 关闭软键盘 + * @param dialog {@link Dialog} + * @param handler {@link Handler} + */ + public static void closeKeyboard(final Dialog dialog, final Handler handler) { + closeKeyboard(dialog, handler, DELAY_MILLIS); + } + + /** + * 关闭软键盘 + * @param dialog {@link Dialog} + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + */ + public static void closeKeyboard(final Dialog dialog, final Handler handler, final int delayMillis) { + if (dialog != null && handler != null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + closeKeyboard(dialog); + } + }, delayMillis); + } + } + + // = + + // 下面暂时无法使用, 缺少判断键盘是否显示, 否则和自动切换无区别 + // InputMethodManager.isActive() ( 无法获取 ) + // Activity.getWindow().getAttributes().softInputMode ( 有些版本可以, 不适用 ) + + /** + * 自动切换键盘状态, 如果键盘显示了则隐藏, 隐藏着显示 + */ + public static void toggleKeyboard() { + try { + InputMethodManager imm = (InputMethodManager) DevUtils.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "toggleKeyboard"); + } + } + + // ================================ + // = 点击非 EditText 则隐藏输入法 = + // ================================ + + /** + * 设置某个 View 内所有非 EditText 的子 View OnTouchListener 事件 + * @param view {@link View} + * @param activity {@link Activity} + */ + public static void judgeView(final View view, final Activity activity) { + if (view == null || activity == null) return; + if (!(view instanceof EditText)) { + view.setOnTouchListener(new View.OnTouchListener() { + public boolean onTouch(View v, MotionEvent event) { + closeKeyboard(activity); + return false; + } + }); + } + // = + if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + if (viewGroup != null) { + for (int i = 0, len = viewGroup.getChildCount(); i < len; i++) { + View innerView = viewGroup.getChildAt(i); + judgeView(innerView, activity); + } + } + } + } + + // ================== + // = 输入法隐藏显示 = + // ================== + + /** + * 判断软键盘是否可见 + * @param activity {@link Activity} + * @return {@code true} 可见, {@code false} 不可见 + */ + public static boolean isSoftInputVisible(final Activity activity) { + return isSoftInputVisible(activity, 200); + } + + /** + * 判断软键盘是否可见 + * @param activity {@link Activity} + * @param minHeightOfSoftInput 软键盘最小高度 + * @return {@code true} 可见, {@code false} 不可见 + */ + public static boolean isSoftInputVisible(final Activity activity, final int minHeightOfSoftInput) { + return getContentViewInvisibleHeight(activity) >= minHeightOfSoftInput; + } + + /** + * 计算 Activity content View 高度 + * @param activity {@link Activity} + * @return View 的高度 + */ + private static int getContentViewInvisibleHeight(final Activity activity) { + try { + final View contentView = activity.findViewById(android.R.id.content); + Rect rect = new Rect(); + contentView.getWindowVisibleDisplayFrame(rect); + return contentView.getRootView().getHeight() - rect.height(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getContentViewInvisibleHeight"); + return 0; + } + } + + /** + * 注册软键盘改变监听 + * @param activity {@link Activity} + * @param listener {@link OnSoftInputChangedListener} + */ + public static void registerSoftInputChangedListener(final Activity activity, final OnSoftInputChangedListener listener) { + try { + // 获取根 View + final View contentView = activity.findViewById(android.R.id.content); + // 添加事件 + contentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (listener != null) { + // 获取高度 + int height = getContentViewInvisibleHeight(activity); + // 判断是否相同 + listener.onSoftInputChanged(height >= 200, height); + } + } + }); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "registerSoftInputChangedListener"); + } + } + + /** + * 注册软键盘改变监听 + * @param activity {@link Activity} + * @param listener {@link OnSoftInputChangedListener} + */ + public static void registerSoftInputChangedListener2(final Activity activity, final OnSoftInputChangedListener listener) { + final View decorView = activity.getWindow().getDecorView(); + decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (listener != null) { + try { + Rect rect = new Rect(); + decorView.getWindowVisibleDisplayFrame(rect); + // 计算出可见屏幕的高度 + int displayHight = rect.bottom - rect.top; + // 获取屏幕整体的高度 + int hight = decorView.getHeight(); + // 获取键盘高度 + int keyboardHeight = hight - displayHight; + // 计算一定比例 + boolean visible = ((double) displayHight / (double) hight) < 0.8d; + // 判断是否显示 + listener.onSoftInputChanged(visible, keyboardHeight); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "registerSoftInputChangedListener2"); + } + } + } + }); + } + + /** + * detail: 输入法弹出、隐藏改变事件 + * @author Ttt + */ + public interface OnSoftInputChangedListener { + + /** + * 输入法弹出、隐藏改变通知 + * @param visible 是否显示了输入法 + * @param height 输入法高度 + */ + void onSoftInputChanged(boolean visible, int height); + } + + // = + + /** + * 修复软键盘内存泄漏 在 Activity.onDestroy() 中使用 + * @param context {@link Context} + */ + public static void fixSoftInputLeaks(final Context context) { + if (context == null) return; + try { + InputMethodManager imm = (InputMethodManager) DevUtils.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + String[] strArr = new String[]{"mCurRootView", "mServedView", "mNextServedView", "mLastSrvView"}; + for (int i = 0; i < 3; i++) { + try { + Field declaredField = imm.getClass().getDeclaredField(strArr[i]); + if (declaredField == null) continue; + if (!declaredField.isAccessible()) { + declaredField.setAccessible(true); + } + Object object = declaredField.get(imm); + if (object == null || !(object instanceof View)) continue; + View view = (View) object; + if (view.getContext() == context) { + declaredField.set(imm, null); + } else { + return; + } + } catch (Throwable th) { + } + } + } catch (Exception e) { + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/KeyguardUtils.java b/lib/DevApp/src/main/java/dev/utils/app/KeyguardUtils.java new file mode 100644 index 0000000000..7ca1cc5a6d --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/KeyguardUtils.java @@ -0,0 +1,164 @@ +package dev.utils.app; + +import android.app.KeyguardManager; +import android.content.Context; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.support.annotation.RequiresPermission; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 锁屏管理工具类 ( 锁屏、禁用锁屏, 判断是否锁屏 ) + * @author Ttt + *
+ *     所需权限
+ *     
+ * 
+ */ +@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) +public final class KeyguardUtils { + + // 日志 TAG + private static final String TAG = KeyguardUtils.class.getSimpleName(); + // KeyguardUtils 实例 + private static KeyguardUtils sInstance; + + /** + * 获取 KeyguardUtils 实例 + * @return {@link KeyguardUtils} + */ + public static KeyguardUtils getInstance() { + if (sInstance == null) { + sInstance = new KeyguardUtils(); + } + return sInstance; + } + + // 锁屏管理类 + private KeyguardManager mKeyguardManager; + // android 26 开始过时 + private KeyguardManager.KeyguardLock mKeyguardLock; + + /** + * 构造函数 + */ + private KeyguardUtils() { + try { + mKeyguardManager = (KeyguardManager) DevUtils.getContext().getSystemService(Context.KEYGUARD_SERVICE); + // 初始化锁 + mKeyguardLock = mKeyguardManager.newKeyguardLock(TAG); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "KeyguardUtils"); + } + } + + /** + * 是否锁屏 - android 4.1 以上支持 + * @return {@code true} yes, {@code false} no + */ + public boolean isKeyguardLocked() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + if (mKeyguardManager != null) return mKeyguardManager.isKeyguardLocked(); + } + return false; + } + + /** + * 是否有锁屏密码 - android 4.1 以上支持 + * @return {@code true} yes, {@code false} no + */ + public boolean isKeyguardSecure() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + if (mKeyguardManager != null) return mKeyguardManager.isKeyguardSecure(); + } + return false; + } + + /** + * 是否锁屏 + * @return {@code true} yes, {@code false} no + */ + public boolean inKeyguardRestrictedInputMode() { + if (mKeyguardManager != null) return mKeyguardManager.inKeyguardRestrictedInputMode(); + return false; + } + + /** + * 获取 KeyguardManager + * @return {@link KeyguardManager} + */ + public KeyguardManager getKeyguardManager() { + return mKeyguardManager; + } + + /** + * 设置 KeyguardManager + * @param keyguardManager {@link KeyguardManager} + */ + public void setKeyguardManager(final KeyguardManager keyguardManager) { + this.mKeyguardManager = keyguardManager; + } + + // = + + /** + * 屏蔽系统的屏保 + * 利用 disableKeyguard 解锁, 解锁并不是真正的解锁, 只是把锁屏的界面隐藏掉而已 + */ + @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) + public void disableKeyguard() { + if (mKeyguardLock != null) { + mKeyguardLock.disableKeyguard(); + } + } + + /** + * 使能显示锁屏界面, 如果你之前调用了 disableKeyguard() 方法取消锁屏界面, 那么会马上显示锁屏界面 + */ + @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) + public void reenableKeyguard() { + if (mKeyguardLock != null) { + mKeyguardLock.reenableKeyguard(); + } + } + + /** + * 释放资源 + */ + @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) + public void release() { + if (mKeyguardLock != null) { + mKeyguardLock.reenableKeyguard(); + } + } + + // = + + /** + * 获取 KeyguardManager.KeyguardLock + * @return {@link KeyguardManager.KeyguardLock} + */ + public KeyguardManager.KeyguardLock getKeyguardLock() { + return mKeyguardLock; + } + + /** + * 设置 KeyguardManager.KeyguardLock + * @param keyguardLock {@link KeyguardManager.KeyguardLock} + */ + public void setKeyguardLock(final KeyguardManager.KeyguardLock keyguardLock) { + this.mKeyguardLock = keyguardLock; + } + + /** + * 设置 KeyguardManager.KeyguardLock ( 通过 TAG 生成 ) + * @param tag TAG + */ + public void setKeyguardLock(final String tag) { + if (mKeyguardManager != null && tag != null) { + this.mKeyguardLock = mKeyguardManager.newKeyguardLock(tag); + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/LanguageUtils.java b/lib/DevApp/src/main/java/dev/utils/app/LanguageUtils.java new file mode 100644 index 0000000000..6524fc8223 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/LanguageUtils.java @@ -0,0 +1,195 @@ +package dev.utils.app; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Build; +import android.os.LocaleList; +import android.util.DisplayMetrics; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import dev.utils.LogPrintUtils; + +/** + * detail: 语言工具类 + * @author Ttt + */ +public final class LanguageUtils { + + private LanguageUtils() { + } + + // 日志 TAG + private static final String TAG = LanguageUtils.class.getSimpleName(); + + /** + * 获取系统语言 + * @return Locale Language + */ + public static String getSystemLanguage() { + return getSystemPreferredLanguage().getLanguage(); + } + + /** + * 获取系统首选语言 + * @return {@link Locale} + */ + public static Locale getSystemPreferredLanguage() { + Locale locale; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + locale = LocaleList.getDefault().get(0); + } else { + locale = Locale.getDefault(); + } + return locale; + } + + /** + * 修改系统语言 (APP 多语言, 单独改变 APP 语言 ) + * @param context {@link Context} + * @param locale {@link Locale} + * @return {@code true} success, {@code false} fail + */ + public static boolean applyLanguage(final Context context, final Locale locale) { + if (context != null && locale != null) { + try { + // 获取 res 资源对象 + Resources resources = context.getResources(); + // 获取设置对象 + Configuration config = resources.getConfiguration(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + // apply locale + config.setLocale(locale); + context.createConfigurationContext(config); + } else { + // updateConfiguration + // 获取屏幕参数: 主要是分辨率, 像素等 + DisplayMetrics displayMetrics = resources.getDisplayMetrics(); + config.locale = locale; + // 更新语言 + resources.updateConfiguration(config, displayMetrics); + } + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "applyLanguage"); + } + } + return false; + } + + /** + * 修改系统语言 (APP 多语言, 单独改变 APP 语言 ) + * @param context {@link Context} + * @param language 语言 + * @return {@code true} success, {@code false} fail + */ + public static boolean applyLanguage(final Context context, final String language) { + Locale locale = getSupportLanguage(language); + if (locale != null) { + return applyLanguage(context, locale); + } else { // 如果为 null, 则使用系统默认语言 + return applyLanguage(context, getSystemPreferredLanguage()); + } + } + + // = + + // 英语 + public static final String ENGLISH = "en"; + // 英语 - 英式 + public static final String UK = "enGB"; + // 英语 - 美式 + public static final String US = "enUS"; + // 法语 + public static final String FRENCH = "fr"; + // 德语 + public static final String GERMAN = "de"; + // 日文 + public static final String JAPAN = "jp"; + // 韩文 + public static final String KOREA = "kr"; + // 中文 + public static final String CHINESE = "zh"; + // 简体中文 + public static final String SIMPLIFIED_CHINESE = "zhCN"; + // 繁体中文 - 默认台湾 + public static final String TRADITIONAL_CHINESE = "zhTW"; + // 台湾 + public static final String TAIWAN_CHINESE = TRADITIONAL_CHINESE; + // 支持的语言字典 + private static Map sSupportLanguageMaps = new HashMap<>(20); + + static { + // 英语 + sSupportLanguageMaps.put(ENGLISH, Locale.ENGLISH); + // 英语 - 英式 + sSupportLanguageMaps.put(UK, Locale.UK); + // 英语 - 美式 + sSupportLanguageMaps.put(US, Locale.US); + // 法语 + sSupportLanguageMaps.put(FRENCH, Locale.FRENCH); + // 德语 + sSupportLanguageMaps.put(GERMAN, Locale.GERMAN); + // 日文 + sSupportLanguageMaps.put(JAPAN, Locale.JAPAN); + // 韩文 + sSupportLanguageMaps.put(KOREA, Locale.KOREA); + // 中文 + sSupportLanguageMaps.put(CHINESE, Locale.CHINESE); + // 简体中文 + sSupportLanguageMaps.put(SIMPLIFIED_CHINESE, Locale.SIMPLIFIED_CHINESE); + // 繁体中文 - 默认香港 + sSupportLanguageMaps.put(TRADITIONAL_CHINESE, Locale.TRADITIONAL_CHINESE); + // 台湾 + sSupportLanguageMaps.put(TAIWAN_CHINESE, Locale.TAIWAN); + } + + /** + * 获取支持的语言 + * @return {@link Map} 支持的语言 + */ + public static Map getSupportLanguages() { + return new HashMap<>(sSupportLanguageMaps); + } + + /** + * 添加支持的语言 + * @param language 语言 + * @param locale {@link Locale} + */ + public static void putSupportLanguage(final String language, final Locale locale) { + sSupportLanguageMaps.put(language, locale); + } + + /** + * 移除支持的语言 + * @param language 语言 + */ + public static void removeSupportLanguage(final String language) { + sSupportLanguageMaps.remove(language); + } + + /** + * 是否支持此语言 + * @param language 语言 + * @return {@code true} 支持, {@code false} 不支持 + */ + public static boolean isSupportLanguage(final String language) { + return sSupportLanguageMaps.containsKey(language); + } + + /** + * 获取支持语言 + * @param language 语言 + * @return {@link Locale} + */ + public static Locale getSupportLanguage(final String language) { + if (isSupportLanguage(language)) { + return sSupportLanguageMaps.get(language); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ListenerUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ListenerUtils.java new file mode 100644 index 0000000000..4cf1c41e3f --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ListenerUtils.java @@ -0,0 +1,216 @@ +package dev.utils.app; + +import android.app.Activity; +import android.support.annotation.IdRes; +import android.view.View; + +import java.lang.reflect.Field; + +import dev.utils.LogPrintUtils; + +/** + * detail: 事件工具类 + * @author Ttt + */ +public final class ListenerUtils { + + private ListenerUtils() { + } + + // 日志 TAG + private static final String TAG = ListenerUtils.class.getSimpleName(); + + /** + * 获取 View 设置的 OnTouchListener 事件对象 + * @param view {@link View} + * @return {@link View.OnTouchListener} + */ + public static View.OnTouchListener getTouchListener(final View view) { + return (View.OnTouchListener) getListenerInfoListener(view, "mOnTouchListener"); + } + + /** + * 获取 View ListenerInfo 对象 ( 内部类 ) + * @param view {@link View} + * @return ListenerInfo + */ + public static Object getListenerInfo(final View view) { + try { + // 获取 ListenerInfo 对象 + Field infoField = View.class.getDeclaredField("mListenerInfo"); + infoField.setAccessible(true); + return infoField.get(view); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getListenerInfo"); + } + return null; + } + + /** + * 获取 View ListenerInfo 对象内部事件对象 + * @param view {@link View} + * @param listener 事件名 + * @return 指定事件名事件对象 + */ + public static Object getListenerInfoListener(final View view, final String listener) { + try { + // 获取 ListenerInfo 对象 + Object listenerInfo = getListenerInfo(view); + // 获取 ListenerInfo 对象中的 mOnTouchListener 属性 + Class infoClazz = Class.forName("android.view.View$ListenerInfo"); + Field listenerField = infoClazz.getDeclaredField(listener); + listenerField.setAccessible(true); + // 进行获取返回 + return listenerField.get(listenerInfo); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getListenerInfoListener"); + } + return null; + } + + // = + + /** + * 设置点击事件 + * @param view {@link View} + * @param onClickListener {@link View.OnClickListener} + * @param viewIds View id 数组 + */ + public static void setOnClicks(final View view, final View.OnClickListener onClickListener, @IdRes final int... viewIds) { + if (view != null && onClickListener != null && viewIds != null) { + for (int i = 0, len = viewIds.length; i < len; i++) { + View findView = findViewById(view, viewIds[i]); + if (findView != null) { + findView.setOnClickListener(onClickListener); + } + } + } + } + + /** + * 设置点击事件 + * @param activity {@link Activity} + * @param onClickListener {@link View.OnClickListener} + * @param viewIds View id 数组 + */ + public static void setOnClicks(final Activity activity, final View.OnClickListener onClickListener, @IdRes final int... viewIds) { + if (activity != null && onClickListener != null && viewIds != null) { + for (int i = 0, len = viewIds.length; i < len; i++) { + View findView = findViewById(activity, viewIds[i]); + if (findView != null) { + findView.setOnClickListener(onClickListener); + } + } + } + } + + /** + * 设置点击事件 + * @param onClickListener {@link View.OnClickListener} + * @param views View 数组 + */ + public static void setOnClicks(final View.OnClickListener onClickListener, final View... views) { + if (onClickListener != null && views != null) { + for (int i = 0, len = views.length; i < len; i++) { + if (views[i] != null) { + views[i].setOnClickListener(onClickListener); + } + } + } + } + + // = + + /** + * 设置长按事件 + * @param view {@link View} + * @param onLongClickListener {@link View.OnLongClickListener} + * @param viewIds View id 数组 + */ + public static void setOnLongClicks(final View view, final View.OnLongClickListener onLongClickListener, @IdRes final int... viewIds) { + if (view != null && onLongClickListener != null && viewIds != null) { + for (int i = 0, len = viewIds.length; i < len; i++) { + View findView = findViewById(view, viewIds[i]); + if (findView != null) { + findView.setOnLongClickListener(onLongClickListener); + } + } + } + } + + /** + * 设置长按事件 + * @param activity {@link Activity} + * @param onLongClickListener {@link View.OnLongClickListener} + * @param viewIds View id 数组 + */ + public static void setOnLongClicks(final Activity activity, final View.OnLongClickListener onLongClickListener, @IdRes final int... viewIds) { + if (activity != null && onLongClickListener != null && viewIds != null) { + for (int i = 0, len = viewIds.length; i < len; i++) { + View findView = findViewById(activity, viewIds[i]); + if (findView != null) { + findView.setOnLongClickListener(onLongClickListener); + } + } + } + } + + /** + * 设置长按事件 + * @param onLongClickListener {@link View.OnLongClickListener} + * @param views View 数组 + */ + public static void setOnLongClicks(final View.OnLongClickListener onLongClickListener, final View... views) { + if (onLongClickListener != null && views != null) { + for (int i = 0, len = views.length; i < len; i++) { + if (views[i] != null) { + views[i].setOnLongClickListener(onLongClickListener); + } + } + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = ViewUtils = + // ============= + + /** + * 初始化 View + * @param view {@link View} + * @param id R.id.viewId + * @param 泛型 + * @return {@link View} + */ + private static T findViewById(final View view, @IdRes final int id) { + if (view != null) { + try { + return view.findViewById(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "findViewById"); + } + } + return null; + } + + /** + * 初始化 View + * @param activity {@link Activity} + * @param id R.id.viewId + * @param 泛型 + * @return {@link View} + */ + private static T findViewById(final Activity activity, @IdRes final int id) { + if (activity != null) { + try { + return activity.findViewById(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "findViewById"); + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/LocationUtils.java b/lib/DevApp/src/main/java/dev/utils/app/LocationUtils.java new file mode 100644 index 0000000000..a040d6da6e --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/LocationUtils.java @@ -0,0 +1,394 @@ +package dev.utils.app; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.location.Address; +import android.location.Criteria; +import android.location.Geocoder; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.location.LocationProvider; +import android.os.Bundle; +import android.provider.Settings; + +import java.util.List; +import java.util.Locale; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 定位相关工具类 + * @author Ttt + *
+ *     所需权限
+ *     
+ *     
+ *     
+ * 
+ */ +public final class LocationUtils { + + private LocationUtils() { + } + + // 日志 TAG + private static final String TAG = LocationUtils.class.getSimpleName(); + // 时间常量 - 2 分钟 + private static final int MINUTES_TWO = 1000 * 60 * 2; + // 定位改变通知事件 + private static OnLocationChangeListener sListener; + // 自定义定位事件 + private static CustomLocationListener sCustomLocationListener; + // 定位管理对象 + private static LocationManager sLocationManager; + + /** + * 获取位置, 需要先判断是否开启了定位 + * @param listener {@link LocationListener} + * @param time 间隔时间 + * @param distance 间隔距离 + * @return {@link Location} + */ + @SuppressLint("MissingPermission") + public static Location getLocation(final LocationListener listener, final long time, final float distance) { + Location location = null; + try { + sLocationManager = (LocationManager) DevUtils.getContext().getSystemService(Context.LOCATION_SERVICE); + if (isLocationEnabled()) { + sLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, time, distance, listener); + if (sLocationManager != null) { + location = sLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); + if (location != null) { + sLocationManager.removeUpdates(listener); + return location; + } + } + } + // when GPS is enabled. + if (isGpsEnabled()) { + if (location == null) { + sLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, time, distance, listener); + if (sLocationManager != null) { + location = sLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); + if (location != null) { + sLocationManager.removeUpdates(listener); + return location; + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLocation"); + } + return location; + } + + /** + * 判断 GPS 是否可用 + * @return {@code true} yes, {@code false} no + */ + public static boolean isGpsEnabled() { + try { + LocationManager locationManager = (LocationManager) DevUtils.getContext().getSystemService(Context.LOCATION_SERVICE); + return locationManager != null && locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isGpsEnabled"); + } + return false; + } + + /** + * 判断定位是否可用 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLocationEnabled() { + try { + LocationManager locationManager = (LocationManager) DevUtils.getContext().getSystemService(Context.LOCATION_SERVICE); + return locationManager != null && (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) || locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isLocationEnabled"); + } + return false; + } + + /** + * 打开 GPS 设置界面 + */ + public static void openGpsSettings() { + try { + Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); + DevUtils.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openGpsSettings"); + } + } + + /** + * 注册 - 使用完记得调用 {@link #unregister()} + *
+     *     如果 minDistance 为 0, 则通过 minTime 来定时更新, minDistance 不为 0, 则以 minDistance 为准, 两者都为 0, 则随时刷新
+     * 
+ * @param minTime 位置信息更新周期 ( 单位: 毫秒 ) + * @param minDistance 位置变化最小距离: 当位置距离变化超过此值时, 将更新位置信息 ( 单位: 米 ) + * @param listener 位置刷新的回调接口 + * @return {@code true} 初始化成功, {@code false} 初始化失败 + */ + @SuppressLint("MissingPermission") + public static boolean register(final long minTime, final long minDistance, final OnLocationChangeListener listener) { + if (listener == null) return false; + try { + sLocationManager = (LocationManager) DevUtils.getContext().getSystemService(Context.LOCATION_SERVICE); + if (sLocationManager == null || (!sLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + && !sLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))) { + return false; + } + sListener = listener; + String provider = sLocationManager.getBestProvider(getCriteria(), true); + Location location = sLocationManager.getLastKnownLocation(provider); + if (location != null) listener.getLastKnownLocation(location); + if (sCustomLocationListener == null) + sCustomLocationListener = new CustomLocationListener(); + sLocationManager.requestLocationUpdates(provider, minTime, minDistance, sCustomLocationListener); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "register"); + } + return false; + } + + /** + * 注销监听 + */ + @SuppressLint("MissingPermission") + public static void unregister() { + try { + if (sLocationManager != null) { + if (sCustomLocationListener != null) { + sLocationManager.removeUpdates(sCustomLocationListener); + sCustomLocationListener = null; + } + sLocationManager = null; + } + if (sListener != null) { + sListener = null; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "unregister"); + } + } + + /** + * 获取定位参数对象 + * @return {@link Criteria} + */ + private static Criteria getCriteria() { + Criteria criteria = new Criteria(); + // 设置定位精确度 Criteria.ACCURACY_COARSE 比较粗略, Criteria.ACCURACY_FINE 则比较精细 + criteria.setAccuracy(Criteria.ACCURACY_FINE); + // 设置是否要求速度 + criteria.setSpeedRequired(false); + // 设置是否允许运营商收费 + criteria.setCostAllowed(false); + // 设置是否需要方位信息 + criteria.setBearingRequired(false); + // 设置是否需要海拔信息 + criteria.setAltitudeRequired(false); + // 设置对电源的需求 + criteria.setPowerRequirement(Criteria.POWER_LOW); + return criteria; + } + + /** + * 根据经纬度获取地理位置 + * @param latitude 纬度 + * @param longitude 经度 + * @return {@link Address} + */ + public static Address getAddress(final double latitude, final double longitude) { + try { + Geocoder geocoder = new Geocoder(DevUtils.getContext(), Locale.getDefault()); + List
addresses = geocoder.getFromLocation(latitude, longitude, 1); + if (addresses.size() > 0) return addresses.get(0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAddress"); + } + return null; + } + + /** + * 根据经纬度获取所在国家 + * @param latitude 纬度 + * @param longitude 经度 + * @return 所在国家 + */ + public static String getCountryName(final double latitude, final double longitude) { + Address address = getAddress(latitude, longitude); + return address == null ? "unknown" : address.getCountryName(); + } + + /** + * 根据经纬度获取所在地 + * @param latitude 纬度 + * @param longitude 经度 + * @return 所在地 + */ + public static String getLocality(final double latitude, final double longitude) { + Address address = getAddress(latitude, longitude); + return address == null ? "unknown" : address.getLocality(); + } + + /** + * 根据经纬度获取所在街道 + * @param latitude 纬度 + * @param longitude 经度 + * @return 所在街道 + */ + public static String getStreet(final double latitude, final double longitude) { + Address address = getAddress(latitude, longitude); + try { + return address == null ? "unknown" : address.getAddressLine(0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getStreet"); + } + return "unknown"; + } + + /** + * 判断是否更好的位置 + * @param newLocation 新的位置 + * @param currentBestLocation 当前最佳位置 + * @return {@code true} yes, {@code false} no + */ + public static boolean isBetterLocation(final Location newLocation, final Location currentBestLocation) { + if (newLocation == null || currentBestLocation == null) return true; + // 检查位置信息的时间间隔 + long timeDelta = newLocation.getTime() - currentBestLocation.getTime(); + boolean isSignificantlyNewer = timeDelta > MINUTES_TWO; + boolean isSignificantlyOlder = timeDelta < -MINUTES_TWO; + boolean isNewer = timeDelta > 0; + + // 如果时间超过 2 分钟, 则使用新的位置 + if (isSignificantlyNewer) { + return true; + } else if (isSignificantlyOlder) { // 时间超过两分钟 + return false; + } + + // 检查新的位置时间 + int accuracyDelta = (int) (newLocation.getAccuracy() - currentBestLocation.getAccuracy()); + boolean isLessAccurate = accuracyDelta > 0; + boolean isMoreAccurate = accuracyDelta < 0; + boolean isSignificantlyLessAccurate = accuracyDelta > 200; + + // 检查旧位置和新位置是否来自同一提供者 + boolean isFromSameProvider = isSameProvider(newLocation.getProvider(), currentBestLocation.getProvider()); + + // 判断最新的位置 + if (isMoreAccurate) { + return true; + } else if (isNewer && !isLessAccurate) { + return true; + } else return isNewer && !isSignificantlyLessAccurate && isFromSameProvider; + } + + /** + * 是否相同的提供者 + * @param provider0 提供者 1 + * @param provider1 提供者 2 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSameProvider(final String provider0, final String provider1) { + if (provider0 == null) { + return provider1 == null; + } + return provider0.equals(provider1); + } + + /** + * detail: 自定义定位监听事件 + * @author Ttt + */ + private static class CustomLocationListener implements LocationListener { + + /** + * 当坐标改变时触发此函数, 如果 Provider 传进相同的坐标, 它就不会被触发 + * @param location 坐标 + */ + @Override + public void onLocationChanged(Location location) { + if (sListener != null) { + sListener.onLocationChanged(location); + } + } + + /** + * provider 的在可用、暂时不可用和无服务三个状态直接切换时触发此函数 + * @param provider 提供者 + * @param status 状态 + * @param extras provider 可选包 + */ + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + if (sListener != null) { + sListener.onStatusChanged(provider, status, extras); + } + switch (status) { + case LocationProvider.AVAILABLE: + LogPrintUtils.dTag(TAG, "当前 GPS 状态为可见状态"); + break; + case LocationProvider.OUT_OF_SERVICE: + LogPrintUtils.dTag(TAG, "当前 GPS 状态为服务区外状态"); + break; + case LocationProvider.TEMPORARILY_UNAVAILABLE: + LogPrintUtils.dTag(TAG, "当前 GPS 状态为暂停服务状态"); + break; + } + } + + /** + * provider 被 enable 时触发此函数, 比如 GPS 被打开 + * @param provider 提供者 + */ + @Override + public void onProviderEnabled(String provider) { + } + + /** + * provider 被 disable 时触发此函数, 比如 GPS 被关闭 + * @param provider 提供者 + */ + @Override + public void onProviderDisabled(String provider) { + } + } + + /** + * detail: 定位改变事件 + * @author Ttt + */ + public interface OnLocationChangeListener { + + /** + * 获取最后一次保留的坐标 + * @param location 坐标 + */ + void getLastKnownLocation(Location location); + + /** + * 当坐标改变时触发此函数, 如果 Provider 传进相同的坐标, 它就不会被触发 + * @param location 坐标 + */ + void onLocationChanged(Location location); + + /** + * provider 的在可用、暂时不可用和无服务三个状态直接切换时触发此函数 + * @param provider 提供者 + * @param status 状态 + * @param extras provider 可选包 + */ + void onStatusChanged(String provider, int status, Bundle extras); // 位置状态发生改变 + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ManifestUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ManifestUtils.java new file mode 100644 index 0000000000..019856e299 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ManifestUtils.java @@ -0,0 +1,369 @@ +package dev.utils.app; + +import android.content.ComponentName; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Android Manifest 工具类 + * @author Ttt + */ +public final class ManifestUtils { + + private ManifestUtils() { + } + + // 日志 TAG + private static final String TAG = ManifestUtils.class.getSimpleName(); + + /** + * 获取 Application meta Data + * @param metaKey meta Key + * @return Application meta Data + */ + public static String getMetaData(final String metaKey) { + try { + return getMetaData(DevUtils.getContext().getPackageName(), metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaData"); + } + return null; + } + + /** + * 获取 Application meta Data + * @param packageName 应用包名 + * @param metaKey meta Key + * @return Application meta Data + */ + public static String getMetaData(final String packageName, final String metaKey) { + try { + ApplicationInfo appInfo = DevUtils.getContext().getPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA); + return appInfo.metaData.getString(metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaData"); + } + return null; + } + + // = + + /** + * 获取 Activity meta Data + * @param clazz Activity.class + * @param metaKey meta Key + * @return Activity meta Data + */ + public static String getMetaDataInActivity(final Class clazz, final String metaKey) { + try { + return getMetaDataInActivity(DevUtils.getContext().getPackageName(), clazz.getCanonicalName(), metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInActivity"); + } + return null; + } + + /** + * 获取 Activity meta Data + * @param name class.getCanonicalName() + * @param metaKey meta Key + * @return Activity meta Data + */ + public static String getMetaDataInActivity(final String name, final String metaKey) { + try { + return getMetaDataInActivity(DevUtils.getContext().getPackageName(), name, metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInActivity"); + } + return null; + } + + /** + * 获取 Activity meta Data + * @param packageName 应用包名 + * @param name class.getCanonicalName() + * @param metaKey meta Key + * @return Activity meta Data + */ + public static String getMetaDataInActivity(final String packageName, final String name, final String metaKey) { + try { + ComponentName componentName = new ComponentName(packageName, name); + ActivityInfo activityInfo = DevUtils.getContext().getPackageManager().getActivityInfo(componentName, PackageManager.GET_META_DATA); + return activityInfo.metaData.getString(metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInActivity"); + } + return null; + } + + // = + + /** + * 获取 Service meta Data + * @param clazz Service.class + * @param metaKey meta Key + * @return Service meta Data + */ + public static String getMetaDataInService(final Class clazz, final String metaKey) { + try { + return getMetaDataInService(DevUtils.getContext().getPackageName(), clazz.getCanonicalName(), metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInService"); + } + return null; + } + + /** + * 获取 Service meta Data + * @param name class.getCanonicalName() + * @param metaKey meta Key + * @return Service meta Data + */ + public static String getMetaDataInService(final String name, final String metaKey) { + try { + return getMetaDataInService(DevUtils.getContext().getPackageName(), name, metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInService"); + } + return null; + } + + /** + * 获取 Service meta Data + * @param packageName 应用包名 + * @param name class.getCanonicalName() + * @param metaKey meta Key + * @return Service meta Data + */ + public static String getMetaDataInService(final String packageName, final String name, final String metaKey) { + try { + ComponentName componentName = new ComponentName(packageName, name); + ServiceInfo serviceInfo = DevUtils.getContext().getPackageManager().getServiceInfo(componentName, PackageManager.GET_META_DATA); + return serviceInfo.metaData.getString(metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInService"); + } + return null; + } + + // = + + /** + * 获取 Receiver meta Data + * @param clazz Receiver.class + * @param metaKey meta Key + * @return Receiver meta Data + */ + public static String getMetaDataInReceiver(final Class clazz, final String metaKey) { + try { + return getMetaDataInReceiver(DevUtils.getContext().getPackageName(), clazz.getCanonicalName(), metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInReceiver"); + } + return null; + } + + /** + * 获取 Receiver meta Data + * @param name class.getCanonicalName() + * @param metaKey meta Key + * @return Receiver meta Data + */ + public static String getMetaDataInReceiver(final String name, final String metaKey) { + try { + return getMetaDataInReceiver(DevUtils.getContext().getPackageName(), name, metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInReceiver"); + } + return null; + } + + /** + * 获取 Receiver meta Data + * @param packageName 应用包名 + * @param name class.getCanonicalName() + * @param metaKey meta Key + * @return Receiver meta Data + */ + public static String getMetaDataInReceiver(final String packageName, final String name, final String metaKey) { + try { + ComponentName componentName = new ComponentName(packageName, name); + ActivityInfo receiverInfo = DevUtils.getContext().getPackageManager().getReceiverInfo(componentName, PackageManager.GET_META_DATA); + return receiverInfo.metaData.getString(metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInReceiver"); + } + return null; + } + + // = + + /** + * 获取 ContentProvider meta Data + * @param clazz ContentProvider.class + * @param metaKey meta Key + * @return ContentProvider meta Data + */ + public static String getMetaDataInProvider(final Class clazz, final String metaKey) { + try { + return getMetaDataInProvider(DevUtils.getContext().getPackageName(), clazz.getCanonicalName(), metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInProvider"); + } + return null; + } + + /** + * 获取 ContentProvider meta Data + * @param name class.getCanonicalName() + * @param metaKey meta Key + * @return ContentProvider meta Data + */ + public static String getMetaDataInProvider(final String name, final String metaKey) { + try { + return getMetaDataInProvider(DevUtils.getContext().getPackageName(), name, metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInProvider"); + } + return null; + } + + /** + * 获取 ContentProvider meta Data + * @param packageName 应用包名 + * @param name class.getCanonicalName() + * @param metaKey meta Key + * @return ContentProvider meta Data + */ + public static String getMetaDataInProvider(final String packageName, final String name, final String metaKey) { + try { + ComponentName componentName = new ComponentName(packageName, name); + ProviderInfo providerInfo = DevUtils.getContext().getPackageManager().getProviderInfo(componentName, PackageManager.GET_META_DATA); + return providerInfo.metaData.getString(metaKey); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInProvider"); + } + return null; + } + + // = + + /** + * 获取 APP 版本信息 + * @return 0 = versionName, 1 = versionCode + */ + public static String[] getAppVersion() { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_SIGNATURES); + if (packageInfo != null) { + String versionName = packageInfo.versionName == null ? "null" : packageInfo.versionName; + String versionCode = packageInfo.versionCode + ""; + return new String[]{versionName, versionCode}; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppVersion"); + } + return null; + } + + /** + * 获取 APP versionCode + * @return APP versionCode + */ + public static int getAppVersionCode() { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_SIGNATURES); + if (packageInfo != null) { + return packageInfo.versionCode; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppVersionCode"); + } + return -1; + } + + /** + * 获取 APP versionName + * @return APP versionName + */ + public static String getAppVersionName() { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_SIGNATURES); + if (packageInfo != null) { + return packageInfo.versionName; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppVersionName"); + } + return null; + } + + // = + + /** + * 获取 APP versionCode + * @param packageName 应用包名 + * @return APP versionCode + */ + public static int getAppVersionCode(final String packageName) { + if (isSpace(packageName)) return -1; + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + return packageInfo == null ? -1 : packageInfo.versionCode; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppVersionCode - " + packageName); + return -1; + } + } + + /** + * 获取 APP versionName + * @param packageName 应用包名 + * @return APP versionName + */ + public static String getAppVersionName(final String packageName) { + if (isSpace(packageName)) return null; + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + return packageInfo == null ? null : packageInfo.versionName; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppVersionName - " + packageName); + return null; + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/MemoryUtils.java b/lib/DevApp/src/main/java/dev/utils/app/MemoryUtils.java new file mode 100644 index 0000000000..7aa3d810b5 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/MemoryUtils.java @@ -0,0 +1,223 @@ +package dev.utils.app; + +import android.app.ActivityManager; +import android.content.Context; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.text.format.Formatter; + +import java.io.BufferedReader; +import java.io.FileReader; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 内存信息工具类 + * @author Ttt + */ +public final class MemoryUtils { + + private MemoryUtils() { + } + + // 日志 TAG + private static final String TAG = MemoryUtils.class.getSimpleName(); + // 内存信息文件地址 + private static final String MEM_INFO_PATH = "/proc/meminfo"; + // 获取内存总大小 + private static final String MEMTOTAL = "MemTotal"; + // 获取可用内存 + private static final String MEMAVAILABLE = "MemAvailable"; + + /** + * 获取内存信息 + *
+     *     Print memory info. such as:
+     *     MemTotal:        1864292 kB
+     *     MemFree:          779064 kB
+     *     Buffers:            4540 kB
+     *     Cached:           185656 kB
+     *     SwapCached:        13160 kB
+     *     Active:           435588 kB
+     *     Inactive:         269312 kB
+     *     Active(anon):     386188 kB
+     *     Inactive(anon):   132576 kB
+     *     Active(file):      49400 kB
+     *     Inactive(file):   136736 kB
+     *     Unevictable:        2420 kB
+     *     Mlocked:               0 kB
+     *     HighTotal:       1437692 kB
+     *     HighFree:         520212 kB
+     *     LowTotal:         426600 kB
+     *     LowFree:          258852 kB
+     *     SwapTotal:        511996 kB
+     *     SwapFree:         171876 kB
+     *     Dirty:               412 kB
+     *     Writeback:             0 kB
+     *     AnonPages:        511924 kB
+     *     Mapped:           152368 kB
+     *     Shmem:              1636 kB
+     *     Slab:             109224 kB
+     *     SReclaimable:      75932 kB
+     *     SUnreclaim:        33292 kB
+     *     KernelStack:       13056 kB
+     *     PageTables:        28032 kB
+     *     NFS_Unstable:          0 kB
+     *     Bounce:                0 kB
+     *     WritebackTmp:          0 kB
+     *     CommitLimit:     1444140 kB
+     *     Committed_AS:   25977748 kB
+     *     VmallocTotal:     458752 kB
+     *     VmallocUsed:      123448 kB
+     *     VmallocChunk:     205828 kB
+     * 
+ * @return 内存信息 + */ + public static String printMemoryInfo() { + try { + BufferedReader br = new BufferedReader(new FileReader(MEM_INFO_PATH), 4 * 1024); + StringBuilder builder = new StringBuilder(); + String str; + while ((str = br.readLine()) != null) { + builder.append(str); + } + br.close(); + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "printMemoryInfo"); + } + return null; + } + + /** + * 获取内存信息 + * @return 内存信息 + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static String printMemoryInfo2() { + try { + ActivityManager.MemoryInfo memoryInfo = getMemoryInfo(); + StringBuilder builder = new StringBuilder(); + builder.append("Memory: "); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + builder.append("\ntotalMem: ").append(memoryInfo.totalMem); + } + builder.append("\navailMem: ").append(memoryInfo.availMem); + builder.append("\nlowMemory: ").append(memoryInfo.lowMemory); + builder.append("\nthreshold: ").append(memoryInfo.threshold); + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "printMemoryInfo2"); + } + return null; + } + + /** + * 获取内存信息 + * @return 内存信息 + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static ActivityManager.MemoryInfo getMemoryInfo() { + try { + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); + activityManager.getMemoryInfo(memoryInfo); + return memoryInfo; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMemoryInfo"); + } + return null; + } + + // = + + /** + * 获取可用内存信息 + * @return 可用内存信息 + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static long getAvailMemory() { + try { + // 获取 android 当前可用内存大小 + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); + activityManager.getMemoryInfo(memoryInfo); + // 当前系统的可用内存 + return memoryInfo.availMem; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAvailMemory"); + } + return 0L; + } + + /** + * 获取可用内存信息 ( 格式化 ) + * @return 可用内存信息 + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static String getAvailMemoryFormat() { + return Formatter.formatFileSize(DevUtils.getContext(), getAvailMemory()); + } + + // = + + /** + * 获取总内存大小 + * @return 总内存大小 + */ + public static long getTotalMemory() { + return getMemInfoIype(MEMTOTAL); + } + + /** + * 获取总内存大小 ( 格式化 ) + * @return 总内存大小 + */ + public static String getTotalMemoryFormat() { + return Formatter.formatFileSize(DevUtils.getContext(), getTotalMemory()); + } + + // = + + /** + * 获取可用内存大小 + * @return 可用内存大小 + */ + public static long getMemoryAvailable() { + return getMemInfoIype(MEMAVAILABLE); + } + + /** + * 获取可用内存大小 ( 格式化 ) + * @return 可用内存大小 + */ + public static String getMemoryAvailableFormat() { + return Formatter.formatFileSize(DevUtils.getContext(), getMemoryAvailable()); + } + + /** + * 通过不同 type 获取对应的内存信息 + * @param type 内存类型 + * @return 对应 type 内存信息 + */ + public static long getMemInfoIype(final String type) { + try { + BufferedReader br = new BufferedReader(new FileReader(MEM_INFO_PATH), 4 * 1024); + String str; + while ((str = br.readLine()) != null) { + if (str.contains(type)) { + break; + } + } + br.close(); + // 拆分空格、回车、换行等空白符 + String[] array = str.split("\\s+"); + // 获取系统总内存, 单位是 KB, 乘以 1024 转换为 Byte + return Long.valueOf(array[1]).longValue() * 1024; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMemInfoIype - " + type); + } + return 0L; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/NetWorkUtils.java b/lib/DevApp/src/main/java/dev/utils/app/NetWorkUtils.java new file mode 100644 index 0000000000..b268531492 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/NetWorkUtils.java @@ -0,0 +1,657 @@ +package dev.utils.app; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.NetworkInfo.State; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.support.annotation.RequiresPermission; +import android.telephony.TelephonyManager; +import android.text.format.Formatter; + +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 网络管理工具类 + * @author Ttt + *
+ *     @see 
+ *     所需权限
+ *     
+ *     
+ *     
+ *     
+ * 
+ */ +public final class NetWorkUtils { + + private NetWorkUtils() { + } + + // 日志 TAG + private static final String TAG = NetWorkUtils.class.getSimpleName(); + + /** + * 获取移动网络打开状态 ( 默认属于未打开 ) + * @return {@code true} yes, {@code false} no + */ + public static boolean getMobileDataEnabled() { + try { + // 移动网络开关状态 + boolean mState; + // 属于 5.0 以下的使用 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // 获取网络连接状态 + ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); + // 反射获取方法 + Method method = cManager.getClass().getMethod("getMobileDataEnabled"); + // 调用方法, 获取状态 + mState = (Boolean) method.invoke(cManager); + } else { + TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); + // 反射获取方法 + Method method = telephonyManager.getClass().getDeclaredMethod("getDataEnabled"); + // 调用方法, 获取状态 + mState = (Boolean) method.invoke(telephonyManager); + } + // 返回移动网络开关状态 + return mState; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMobileDataEnabled"); + } + return false; + } + + /** + * 设置移动网络开关 ( 无判断是否已开启移动网络 ) - 实际无效果, 非系统应用无权限 + * @param isOpen 是否打开移动网络 + * @return {@code true} success, {@code false} fail + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public static boolean setMobileDataEnabled(final boolean isOpen) { + try { + // 属于 5.0 以下的使用 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // 获取网络连接状态 + ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); + // 通过反射设置移动网络 + Method mMethod = ConnectivityManager.class.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE); + // 设置移动网络 + mMethod.invoke(cManager, isOpen); + } else { // 需要 android.Manifest.permission.MODIFY_PHONE_STATE 权限, 普通 APP 无法获取 + TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); + // 通过反射设置移动网络 + Method mMethod = telephonyManager.getClass().getDeclaredMethod("setDataEnabled", boolean.class); + // 设置移动网络 + mMethod.invoke(telephonyManager, isOpen); + } + } catch (Exception e) { // 开启移动网络失败 + LogPrintUtils.eTag(TAG, e, "setMobileDataEnabled"); + return false; + } + return true; + } + + /** + * 判断是否连接了网络 + * @return {@code true} yes, {@code false} no + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public static boolean isConnect() { + // 获取手机所有连接管理对象 ( 包括对 wi-fi,net 等连接的管理 ) + try { + ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); + // 版本兼容处理 + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + // 获取网络连接管理的对象 + NetworkInfo nInfo = cManager.getActiveNetworkInfo(); + // 判断是否为 null + if (nInfo != null) { + // 判断当前网络是否已经连接 + if (nInfo.getState() == State.CONNECTED) { + return true; + } + } + } else { + // 获取当前活跃的网络 ( 连接的网络信息 ) + Network network = cManager.getActiveNetwork(); + // 判断是否为 null + if (network != null) { + return true; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isConnect"); + } + return false; + } + + /** + * 获取连接的网络类型 + * @return -1 = 等于未知, 1 = Wifi, 2 = 移动网络 + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public static int getConnectType() { + // 获取手机所有连接管理对象 ( 包括对 wi-fi,net 等连接的管理 ) + try { + // 获取网络连接状态 + ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); + // 版本兼容处理 + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + // 判断连接的是否 wifi + State wifiState = cManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState(); + // 判断是否连接上 + if (wifiState == State.CONNECTED || wifiState == State.CONNECTING) { + return 1; + } else { + // 判断连接的是否移动网络 + State mobileState = cManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState(); + // 判断移动网络是否连接上 + if (mobileState == State.CONNECTED || mobileState == State.CONNECTING) { + return 2; + } + } + } else { + // 获取当前活跃的网络 ( 连接的网络信息 ) + Network network = cManager.getActiveNetwork(); + if (network != null) { + NetworkCapabilities networkCapabilities = cManager.getNetworkCapabilities(network); + // 判断连接的是否 wifi + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + return 1; + } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + // 判断连接的是否移动网络 + return 2; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getConnectType"); + } + return -1; + } + + /** + * 判断是否连接 Wifi( 连接上、连接中 ) + * @return {@code true} yes, {@code false} no + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public static boolean isConnWifi() { + return (getConnectType() == 1); + } + + /** + * 判断是否连接移动网络 ( 连接上、连接中 ) + * @return {@code true} yes, {@code false} no + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public static boolean isConnMobileData() { + return (getConnectType() == 2); + } + + // = + + /** + * detail: 网络连接类型 + * @author Ttt + */ + public enum NetworkType { + NETWORK_WIFI, + NETWORK_4G, + NETWORK_3G, + NETWORK_2G, + NETWORK_UNKNOWN, + NETWORK_NO + } + + /** + * 判断网络是否可用 + * @return {@code true} 可用, {@code false} 不可用 + */ + @Deprecated + @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.INTERNET}) + public static boolean isAvailable() { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + NetworkInfo info = getActiveNetworkInfo(); + return info != null && info.isAvailable(); + } else { + return isAvailableByPing(); + } + } + + /** + * 使用 ping ip 方式判断网络是否可用 + *
+     * 
+ * @return {@code true} yes, {@code false} no + */ + @RequiresPermission(android.Manifest.permission.INTERNET) + public static boolean isAvailableByPing() { + return isAvailableByPing(null); + } + + /** + * 使用 ping ip 方式判断网络是否可用 + * @param ip IP 地址 + * @return {@code true} yes, {@code false} no + */ + @RequiresPermission(android.Manifest.permission.INTERNET) + public static boolean isAvailableByPing(String ip) { + if (ip == null || ip.length() <= 0) { + ip = "223.5.5.5"; // 默认阿里巴巴 DNS + } + // cmd ping ip + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format("ping -c 1 %s", ip), false); + // 打印信息 + if (result.errorMsg != null) { + LogPrintUtils.dTag(TAG, "isAvailableByPing - errorMsg: " + result.errorMsg); + } + if (result.successMsg != null) { + LogPrintUtils.dTag(TAG, "isAvailableByPing - successMsg: " + result.successMsg); + } + // 判断结果, 返回数据不为 null + return result.isSuccess3(); + } + + /** + * 获取活动网络信息 + * @return {@link NetworkInfo} + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public static NetworkInfo getActiveNetworkInfo() { + try { + return ((ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActiveNetworkInfo"); + } + return null; + } + + /** + * 获取活动网络信息 + * @return {@link Network} + */ + @RequiresApi(Build.VERSION_CODES.M) + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public static Network getActiveNetwork() { + try { + return ((ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetwork(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActiveNetwork"); + } + return null; + } + + // = + + /** + * 判断是否 4G 网络 + * @return {@code true} yes, {@code false} no + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public static boolean is4G() { + return getNetworkType() == NetworkType.NETWORK_4G; + } + + /** + * 判断 wifi 是否打开 + * @return {@code true} yes, {@code false} no + */ + public static boolean getWifiEnabled() { + try { + @SuppressLint("WifiManagerLeak") + WifiManager wifiManager = (WifiManager) DevUtils.getContext().getSystemService(Context.WIFI_SERVICE); + return wifiManager.isWifiEnabled(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWifiEnabled"); + } + return false; + } + + /** + * 判断 wifi 数据是否可用 + * @return {@code true} yes, {@code false} no + */ + @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.INTERNET}) + public static boolean isWifiAvailable() { + return getWifiEnabled() && isAvailable(); + } + + /** + * 获取网络运营商名称 - 中国移动、如中国联通、中国电信 + * @return 运营商名称 + */ + public static String getNetworkOperatorName() { + try { + TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); + return telephonyManager != null ? telephonyManager.getNetworkOperatorName() : null; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getNetworkOperatorName"); + } + return null; + } + + // = + + /** + * 获取当前网络类型 + * @return {@link NetworkType} + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public static NetworkType getNetworkType() { + // 默认网络类型 + NetworkType netType = NetworkType.NETWORK_NO; + // 版本兼容处理 + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + // 获取网络信息 + NetworkInfo networkInfo = getActiveNetworkInfo(); + // 判断是否可用 + if (networkInfo != null && networkInfo.isAvailable()) { // 同 getNetworkClass 方法 + // 属于可用则修改为未知 + netType = NetworkType.NETWORK_UNKNOWN; + // 获取类型 + switch (networkInfo.getType()) { + case ConnectivityManager.TYPE_WIFI: // 属于 Wifi + netType = NetworkType.NETWORK_WIFI; + break; + case ConnectivityManager.TYPE_MOBILE: // 属于手机网络 + switch (networkInfo.getSubtype()) { + // = 2G 网络 = + case TelephonyManager.NETWORK_TYPE_GSM: + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_CDMA: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_1xRTT: + case TelephonyManager.NETWORK_TYPE_IDEN: + netType = NetworkType.NETWORK_2G; + break; + // = 3G 网络 = + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_EVDO_0: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + case TelephonyManager.NETWORK_TYPE_EHRPD: + case TelephonyManager.NETWORK_TYPE_HSPAP: + netType = NetworkType.NETWORK_3G; + break; + // = 4G 网络 = + case TelephonyManager.NETWORK_TYPE_LTE: + case TelephonyManager.NETWORK_TYPE_IWLAN: + // case TelephonyManager.NETWORK_TYPE_LTE_CA: // 19 + case 19: + netType = NetworkType.NETWORK_4G; + break; + default: // 其他判断 + try { + // 判断子类名字 + String subtypeName = networkInfo.getSubtypeName(); + // = 3G 网络 = + if (subtypeName.equalsIgnoreCase("TD-SCDMA") + || subtypeName.equalsIgnoreCase("WCDMA") + || subtypeName.equalsIgnoreCase("CDMA2000")) { + netType = NetworkType.NETWORK_3G; + } else { + netType = NetworkType.NETWORK_UNKNOWN; + } + } catch (Exception e) { + // 保存未知 + netType = NetworkType.NETWORK_UNKNOWN; + } + break; + } + break; + } + } + } else { + try { + // 获取网络连接状态 + ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); + // 获取当前活跃的网络 ( 连接的网络信息 ) + Network network = cManager.getActiveNetwork(); + // 防止为 null + if (network != null) { + // 属于可用则修改为未知 + netType = NetworkType.NETWORK_UNKNOWN; + // 获取网络连接信息 + NetworkCapabilities networkCapabilities = cManager.getNetworkCapabilities(network); + // 判断连接类型 + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { // 判断是否连接 wifi + netType = NetworkType.NETWORK_WIFI; + } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { // 判断连接的是否移动网络 + TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); + // 获取网络类型 + int networkType = telephonyManager.getNetworkType(); + // 获取移动网络类型 + switch (getNetworkClass(networkType)) { + case 1: // 2G + netType = NetworkType.NETWORK_2G; + break; + case 2: // 3G + netType = NetworkType.NETWORK_3G; + break; + case 3: // 4G + netType = NetworkType.NETWORK_4G; + break; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getNetworkType"); + } + } + return netType; + } + + /** + * 获取移动网络连接类型 + *
+     *     {@link TelephonyManager#getNetworkClass} hide 方法
+     * 
+ * @param networkType {@link TelephonyManager#getNetworkType} + * @return 0 = 未知, 1 = 2G, 2 = 3G, 3 = 4G + */ + public static int getNetworkClass(final int networkType) { + switch (networkType) { + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_GSM: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_CDMA: + case TelephonyManager.NETWORK_TYPE_1xRTT: + case TelephonyManager.NETWORK_TYPE_IDEN: + return 1; + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_EVDO_0: + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + case TelephonyManager.NETWORK_TYPE_EHRPD: + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + return 2; + case TelephonyManager.NETWORK_TYPE_LTE: + case TelephonyManager.NETWORK_TYPE_IWLAN: + // case TelephonyManager.NETWORK_TYPE_LTE_CA: // 19 + case 19: + return 3; + default: + return 0; + } + } + + /** + * 获取广播 IP 地址 + * @return 广播 IP 地址 + */ + public static String getBroadcastIpAddress() { + try { + Enumeration nis = NetworkInterface.getNetworkInterfaces(); + while (nis.hasMoreElements()) { + NetworkInterface ni = nis.nextElement(); + if (!ni.isUp() || ni.isLoopback()) continue; + List ias = ni.getInterfaceAddresses(); + for (int i = 0; i < ias.size(); i++) { + InterfaceAddress ia = ias.get(i); + InetAddress broadcast = ia.getBroadcast(); + if (broadcast != null) { + return broadcast.getHostAddress(); + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBroadcastIpAddress"); + } + return null; + } + + /** + * 获取域名 IP 地址 + * @param domain 域名 www.baidu.com 不需要加上 http + * @return 域名 IP 地址 + */ + public static String getDomainAddress(final String domain) { + try { + ExecutorService exec = Executors.newCachedThreadPool(); + Future fs = exec.submit(new Callable() { + @Override + public String call() throws Exception { + InetAddress inetAddress; + try { + inetAddress = InetAddress.getByName(domain); + return inetAddress.getHostAddress(); + } catch (UnknownHostException e) { + LogPrintUtils.eTag(TAG, e, "getDomainAddress"); + } + return null; + } + }); + return fs.get(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDomainAddress"); + } + return null; + } + + /** + * 获取 IP 地址 + * @param useIPv4 是否用 IPv4 + * @return IP 地址 + */ + public static String getIPAddress(final boolean useIPv4) { + try { + Enumeration nis = NetworkInterface.getNetworkInterfaces(); + while (nis.hasMoreElements()) { + NetworkInterface ni = nis.nextElement(); + // 防止小米手机返回 10.0.2.15 + if (!ni.isUp()) continue; + for (Enumeration addresses = ni.getInetAddresses(); addresses.hasMoreElements(); ) { + InetAddress inetAddress = addresses.nextElement(); + if (!inetAddress.isLoopbackAddress()) { + String hostAddress = inetAddress.getHostAddress(); + boolean isIPv4 = hostAddress.indexOf(':') < 0; + if (useIPv4) { + if (isIPv4) return hostAddress; + } else { + if (!isIPv4) { + int index = hostAddress.indexOf('%'); + return index < 0 ? hostAddress.toUpperCase() : hostAddress.substring(0, index).toUpperCase(); + } + } + } + } + } + } catch (SocketException e) { + LogPrintUtils.eTag(TAG, e, "getIPAddress"); + } + return null; + } + + /** + * 根据 Wifi 获取网络 IP 地址 + * @return 网络 IP 地址 + */ + @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) + public static String getIpAddressByWifi() { + try { + @SuppressLint("WifiManagerLeak") + WifiManager wifiManager = (WifiManager) DevUtils.getContext().getSystemService(Context.WIFI_SERVICE); + return Formatter.formatIpAddress(wifiManager.getDhcpInfo().ipAddress); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIpAddressByWifi"); + } + return null; + } + + /** + * 根据 Wifi 获取网关 IP 地址 + * @return 网关 IP 地址 + */ + @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) + public static String getGatewayByWifi() { + try { + @SuppressLint("WifiManagerLeak") + WifiManager wifiManager = (WifiManager) DevUtils.getContext().getSystemService(Context.WIFI_SERVICE); + return Formatter.formatIpAddress(wifiManager.getDhcpInfo().gateway); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getGatewayByWifi"); + } + return null; + } + + /** + * 根据 Wifi 获取子网掩码 IP 地址 + * @return 子网掩码 IP 地址 + */ + @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) + public static String getNetMaskByWifi() { + try { + @SuppressLint("WifiManagerLeak") + WifiManager wifiManager = (WifiManager) DevUtils.getContext().getSystemService(Context.WIFI_SERVICE); + return Formatter.formatIpAddress(wifiManager.getDhcpInfo().netmask); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getNetMaskByWifi"); + } + return null; + } + + /** + * 根据 Wifi 获取服务端 IP 地址 + * @return 服务端 IP 地址 + */ + @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) + public static String getServerAddressByWifi() { + try { + @SuppressLint("WifiManagerLeak") + WifiManager wifiManager = (WifiManager) DevUtils.getContext().getSystemService(Context.WIFI_SERVICE); + return Formatter.formatIpAddress(wifiManager.getDhcpInfo().serverAddress); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getServerAddressByWifi"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/NotificationUtils.java b/lib/DevApp/src/main/java/dev/utils/app/NotificationUtils.java new file mode 100644 index 0000000000..64ff2e72e2 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/NotificationUtils.java @@ -0,0 +1,384 @@ +package dev.utils.app; + +import android.app.AppOpsManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.os.Build; +import android.support.annotation.DrawableRes; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 通知栏管理工具类 + * @author Ttt + *
+ *     @see 
+ * 
+ */ +public final class NotificationUtils { + + private NotificationUtils() { + } + + // 日志 TAG + private static final String TAG = NotificationUtils.class.getSimpleName(); + // 通知栏管理类 + private static NotificationManager sNotificationManager = null; + + /** + * 获取通知栏管理对象 + * @return {@link NotificationManager} + */ + public static NotificationManager getNotificationManager() { + if (sNotificationManager == null) { + try { + sNotificationManager = (NotificationManager) DevUtils.getContext().getSystemService(Context.NOTIFICATION_SERVICE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getNotificationManager"); + } + } + return sNotificationManager; + } + + /** + * 检查通知栏权限是否开启 + * 参考 SupportCompat 包中的: NotificationManagerCompat.from(context).areNotificationsEnabled(); + * @return {@code true} yes, {@code false} no + */ + public static boolean isNotificationEnabled() { + Context context = DevUtils.getContext(); + if (context != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).areNotificationsEnabled(); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + try { + AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + ApplicationInfo appInfo = context.getApplicationInfo(); + String pkg = context.getApplicationContext().getPackageName(); + int uid = appInfo.uid; + + Class appOpsClass = Class.forName(AppOpsManager.class.getName()); + Method checkOpNoThrowMethod = appOpsClass.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class); + Field opPostNotificationValue = appOpsClass.getDeclaredField("OP_POST_NOTIFICATION"); + int value = (Integer) opPostNotificationValue.get(Integer.class); + return (Integer) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg) == 0; + } catch (Throwable ignored) { + return true; + } + } else { + return true; + } + } + return false; + } + + // = + + /** + * 移除通知 - 移除所有通知 ( 只是针对当前 Context 下的 Notification) + */ + public static void cancelAll() { + if (getNotificationManager() != null) { + try { + sNotificationManager.cancelAll(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cancelAll"); + } + } + } + + /** + * 移除通知 - 移除标记为 id 的通知 ( 只是针对当前 Context 下的所有 Notification) + * @param args 消息 id 集合 + */ + public static void cancel(final int... args) { + if (getNotificationManager() != null && args != null) { + for (int id : args) { + try { + sNotificationManager.cancel(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cancel - id: " + id); + } + } + } + } + + /** + * 移除通知 - 移除标记为 id 的通知 ( 只是针对当前 Context 下的所有 Notification) + * @param tag 标记 TAG + * @param id 消息 id + */ + public static void cancel(final String tag, final int id) { + if (getNotificationManager() != null && tag != null) { + try { + sNotificationManager.cancel(tag, id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cancel - id: " + id + ", tag: " + tag); + } + } + } + + /** + * 进行通知 + * @param id 消息 id + * @param notification {@link Notification} + * @return {@code true} success, {@code false} fail + */ + public static boolean notify(final int id, final Notification notification) { + if (getNotificationManager() != null && notification != null) { + try { + sNotificationManager.notify(id, notification); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "notify - id: " + id); + } + } + return false; + } + + /** + * 进行通知 + * @param tag 标记 TAG + * @param id 消息 id + * @param notification {@link Notification} + * @return {@code true} success, {@code false} fail + */ + public static boolean notify(final String tag, final int id, final Notification notification) { + if (getNotificationManager() != null && tag != null && notification != null) { + try { + sNotificationManager.notify(tag, id, notification); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "notify - id: " + id + ", tag: " + tag); + } + } + return false; + } + + // ================ + // = 封装外部调用 = + // ================ + +// // 使用自定义 View +// RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.xxx); +// // 设置 View +// Notification.builder.setContent(remoteViews); + + /** + * 获取 PendingIntent + * @param intent {@link Intent} + * @param requestCode 请求 code + * @return {@link PendingIntent} + */ + public static PendingIntent createPendingIntent(final Intent intent, final int requestCode) { + try { + // 跳转 Intent + PendingIntent pendingIntent = PendingIntent.getActivity(DevUtils.getContext(), requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); + return pendingIntent; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createPendingIntent"); + } + return null; + } + + /** + * 创建通知栏对象 + * @param icon 图标 + * @param title 通知栏标题 + * @param msg 通知栏内容 + * @return {@link Notification} + */ + public static Notification createNotification(@DrawableRes final int icon, final String title, final String msg) { + return createNotification(null, icon, title, title, msg, true, VibratePattern.obtain(0, 100, 300), + LightPattern.obtain(Color.WHITE, 1000, 1000)); + } + + /** + * 创建通知栏对象 + * @param icon 图标 + * @param title 通知栏标题 + * @param msg 通知栏内容 + * @param vibratePattern 震动频率 + * @param lightPattern Led 闪灯参数 + * @return {@link Notification} + */ + public static Notification createNotification(@DrawableRes final int icon, final String title, final String msg, + final VibratePattern vibratePattern, final LightPattern lightPattern) { + return createNotification(null, icon, title, title, msg, true, vibratePattern, lightPattern); + } + + /** + * 创建通知栏对象 + * @param pendingIntent {@link PendingIntent} + * @param icon 图标 + * @param ticker 状态栏通知文案 + * @param title 通知栏标题 + * @param msg 通知栏内容 + * @param isAutoCancel 点击是否自动关闭 + * @param vibratePattern 震动频率 + * @param lightPattern Led 闪灯参数 + * @return {@link Notification} + */ + public static Notification createNotification(final PendingIntent pendingIntent, @DrawableRes final int icon, final String ticker, + final String title, final String msg, final boolean isAutoCancel, + final VibratePattern vibratePattern, final LightPattern lightPattern) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + Notification.Builder builder = new Notification.Builder(DevUtils.getContext()); + // 点击通知执行 intent + builder.setContentIntent(pendingIntent); + // 设置图标 + builder.setSmallIcon(icon); + // 设置图标 + builder.setLargeIcon(BitmapFactory.decodeResource(DevUtils.getContext().getResources(), icon)); + // 指定通知的 ticker 内容, 通知被创建的时候, 在状态栏一闪而过, 属于瞬时提示信息 + builder.setTicker(ticker); + // 设置标题 + builder.setContentTitle(title); + // 设置内容 + builder.setContentText(msg); + // 设置消息提醒, 震动 | 声音 + builder.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND); + // 将 AutoCancel 设为 true 后, 当你点击通知栏的 notification 后, 它会自动被取消消失 + builder.setAutoCancel(isAutoCancel); + // 设置时间 + builder.setWhen(System.currentTimeMillis()); + // 设置震动参数 + if (vibratePattern != null && !vibratePattern.isEmpty()) { + builder.setVibrate(vibratePattern.vibrates); + } + // 设置 led 灯参数 + if (lightPattern != null) { + builder.setLights(lightPattern.argb, lightPattern.durationMS, lightPattern.startOffMS); + } + // = 初始化 Notification 对象 = + Notification baseNF; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + baseNF = builder.getNotification(); + } else { + baseNF = builder.build(); + } + return baseNF; + } else { +// // = 初始化通知信息实体类 = +// Notification notification = new Notification(); +// // 设置图标 +// notification.icon = icon; +// // 设置图标 +// notification.largeIcon = BitmapFactory.decodeResource(DevUtils.getContext().getResources(), icon); +// // 指定通知的 ticker 内容, 通知被创建的时候, 在状态栏一闪而过, 属于瞬时提示信息 +// notification.tickerText = title; +// // 设置时间 +// notification.when = System.currentTimeMillis(); +// // 设置消息提醒, 震动 | 声音 +// notification.defaults = Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND; +// // 点击了此通知则取消该通知栏 +// if (isAutoCancel) { +// notification.flags |= Notification.FLAG_AUTO_CANCEL; +// } +// // 设置震动参数 +// if (vibratePattern != null && !vibratePattern.isEmpty()) { +// notification.vibrate = vibratePattern.vibrates; +// } +// // 设置 led 灯参数 +// if (lightPattern != null) { +// try { +// notification.ledARGB = lightPattern.argb; // 控制 LED 灯的颜色, 一般有红绿蓝三种颜色可选 +// notification.ledOffMS = lightPattern.startOffMS; // 指定 LED 灯暗去的时长, 也是以毫秒为单位 +// notification.ledOnMS = lightPattern.durationMS; // 指定 LED 灯亮起的时长, 以毫秒为单位 +// notification.flags = Notification.FLAG_SHOW_LIGHTS; +// } catch (Exception e) { +// } +// } +// // 设置标题内容等 - 已经移除, 现在都是支持 4.0 以上, 不需要兼容处理 +// notification.setLatestEventInfo(DevUtils.getContext(), title, msg, pendingIntent); +// return notification; + } + return null; + } + + /** + * detail: 设置通知栏 Led 灯参数实体类 + * @author Ttt + *
+     *     手机处于锁屏状态时, LED 灯就会不停地闪烁, 提醒用户去查看手机, 下面是绿色的灯光一闪一闪的效果
+     * 
+ */ + public static class LightPattern { + + private int argb = 0; // 控制 LED 灯的颜色, 一般有红绿蓝三种颜色可选 + private int startOffMS = 0; // 指定 LED 灯暗去的时长, 也是以毫秒为单位 + private int durationMS = 0; // 指定 LED 灯亮起的时长, 以毫秒为单位 + + /** + * 构造函数 + * @param argb 颜色值 + * @param startOffMS 开始时间 + * @param durationMS 持续时间 + */ + private LightPattern(int argb, int startOffMS, int durationMS) { + this.argb = argb; + this.startOffMS = startOffMS; + this.durationMS = durationMS; + } + + /** + * 获取 Led 配置参数 + * @param argb 颜色值 + * @param startOffMS 开始时间 + * @param durationMS 持续时间 + * @return {@link LightPattern} + */ + public static LightPattern obtain(final int argb, final int startOffMS, final int durationMS) { + return new LightPattern(argb, startOffMS, durationMS); + } + } + + /** + * detail: 设置通知栏震动参数实体类 + * @author Ttt + *
+     *     vibrate 属性是一个长整型的数组, 用于设置手机静止和震动的时长, 以毫秒为单位
+     *     参数中下标为 0 的值表示手机静止的时长, 下标为 1 的值表示手机震动的时长, 下标为 2 的值又表示手机静止的时长, 以此类推
+     * 
+ */ + public static class VibratePattern { + + // long[] vibrates = { 0, 1000, 1000, 1000 }; + private long[] vibrates = null; + + /** + * 构造函数 + * @param vibrates 震动时间数组 + */ + private VibratePattern(long[] vibrates) { + this.vibrates = vibrates; + } + + /** + * 判断是否为 null + * @return {@code true} is null, {@code false} not null + */ + public boolean isEmpty() { + return (vibrates == null || vibrates.length == 0); + } + + /** + * 获取 震动时间 配置参数 + * @param times 震动时间数组 + * @return {@link VibratePattern} + */ + public static VibratePattern obtain(final long... times) { + return new VibratePattern(times); + } + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/OSUtils.java b/lib/DevApp/src/main/java/dev/utils/app/OSUtils.java similarity index 91% rename from DevLibUtils/src/main/java/dev/utils/app/OSUtils.java rename to lib/DevApp/src/main/java/dev/utils/app/OSUtils.java index c37c46371e..3ad008ea84 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/OSUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/app/OSUtils.java @@ -12,22 +12,26 @@ import dev.utils.LogPrintUtils; /** - * detail: 工具类: OS 系统相关 - * Created by Ttt - * 简介 : 由于国内定制系统的泛滥, 不同定制系统的一些功能或实现方法会有所不同, 如果需要做到足够好的适配工作, 需要 对不同的定制系统做一些专门的适配. - * API : 获取 ROM 类型等 + * detail: OS 系统相关工具类 + * @author Ttt + *
+ *     由于国内定制系统的泛滥, 不同定制系统的一些功能或实现方法会有所不同, 如果需要做到足够好的适配工作, 需要对不同的定制系统做一些专门的适配
+ * 
*/ public final class OSUtils { private OSUtils() { } - // 日志TAG + // 日志 TAG private static final String TAG = OSUtils.class.getSimpleName(); - - /** ROM 类型 */ + // ROM 类型 private static final ROM ROM_TYPE = initRomType(); + // ================ + // = ROM 标识信息 = + // ================ + private static final String KEY_DISPLAY_ID = "ro.build.display.id"; private static final String KEY_BASE_OS_VERSION = "ro.build.version.base_os"; private static final String KEY_CLIENT_ID_BASE = "ro.com.google.clientidbase"; @@ -75,7 +79,7 @@ private OSUtils() { // 乐视 : eui private static final String KEY_EUI_VERSION = "ro.letv.release.version"; // "5.9.023S" private static final String KEY_EUI_VERSION_DATE = "ro.letv.release.version_date"; // "5.9.023S_03111" - private static final String KEY_EUI_NAME = "ro.product.letv_name"; // "乐1s" + private static final String KEY_EUI_NAME = "ro.product.letv_name"; // " 乐 1s" private static final String KEY_EUI_MODEL = "ro.product.letv_model"; // "Letv X500" // 金立 : amigo @@ -102,12 +106,14 @@ private OSUtils() { // 联想 private static final String KEY_LENOVO_DEVICE = "ro.lenovo.device"; // "phone" private static final String KEY_LENOVO_PLATFORM = "ro.lenovo.platform"; // "qualcomm" - private static final String KEY_LENOVO_ADB = "ro.lenovo.adb"; // "apkctl,speedup" + private static final String KEY_LENOVO_ADB = "ro.lenovo.adb"; // "apkctl, speedup" private static final String VALUE_LENOVO_CLIENT_ID_BASE = "android-lenovo"; + // = + /** * 获取 ROM 类型 - * @return ROM + * @return {@link ROM} */ public static ROM getRomType() { return ROM_TYPE; @@ -115,14 +121,15 @@ public static ROM getRomType() { /** * 初始化 ROM 类型 + * @return {@link ROM} */ private static ROM initRomType() { ROM rom = ROM.Other; - FileInputStream is = null; + FileInputStream fis = null; try { Properties buildProperties = new Properties(); - is = new FileInputStream(new File(Environment.getRootDirectory(), "build.prop")); - buildProperties.load(is); + fis = new FileInputStream(new File(Environment.getRootDirectory(), "build.prop")); + buildProperties.load(fis); if (buildProperties.containsKey(KEY_MIUI_VERSION_NANE) || buildProperties.containsKey(KEY_MIUI_VERSION_CODE)) { // MIUI @@ -304,9 +311,9 @@ private static ROM initRomType() { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "initRomType"); } finally { - if (is != null) { + if (fis != null) { try { - is.close(); + fis.close(); } catch (Exception e) { } } @@ -314,20 +321,20 @@ private static ROM initRomType() { return rom; } + /** + * detail: ROM 类型 + * @author Ttt + */ public enum ROM { + MIUI, // 小米 Flyme, // 魅族 EMUI, // 华为 ColorOS, // OPPO FuntouchOS, // vivo - SmartisanOS, // 锤子 EUI, // 乐视 Sense, // HTC AmigoOS, // 金立 - _360OS, // 奇酷360 - NubiaUI, // 努比亚 - H2OS, // 一加 - YunOS, // 阿里巴巴 YuLong, // 酷派 SamSung, // 三星 @@ -336,26 +343,53 @@ public enum ROM { LG, // LG Google, // 原生 + _360OS, // 奇酷 360 + NubiaUI, // 努比亚 + H2OS, // 一加 + YunOS, // 阿里巴巴 + SmartisanOS, // 锤子 - Other; // CyanogenMod, Lewa OS, 百度云OS, Tencent OS, 深度OS, IUNI OS, Tapas OS, Mokee + Other; // CyanogenMod, Lewa OS, 百度云 OS, Tencent OS, 深度 OS, IUNI OS, Tapas OS, Mokee - private int baseVersion = -1; + // ======== + // = 信息 = + // ======== + + // ROM 具体版本号 private String version; + // ROM 版本 + private int baseVersion = -1; - void setVersion(String version) { - this.version = version; + /** + * 获取 ROM 具体版本号, 如 MIUI 9 8.4.26 获取的是 8.4.26 + * @return ROM 具体版本号 + */ + public String getVersion() { + return version; } - void setBaseVersion(int baseVersion) { - this.baseVersion = baseVersion; + /** + * 设置 ROM 具体版本号 + * @param version ROM 具体版本号 + */ + void setVersion(final String version) { + this.version = version; } + /** + * 获取 ROM 版本, 如 MIUI 9 获取的是 9 + * @return ROM 版本 + */ public int getBaseVersion() { return baseVersion; } - public String getVersion() { - return version; + /** + * 设置 ROM 版本 + * @param baseVersion ROM 版本 + */ + void setBaseVersion(final int baseVersion) { + this.baseVersion = baseVersion; } } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/PathUtils.java b/lib/DevApp/src/main/java/dev/utils/app/PathUtils.java new file mode 100644 index 0000000000..7c4ae3bfb8 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/PathUtils.java @@ -0,0 +1,368 @@ +package dev.utils.app; + +import android.os.Build; +import android.os.Environment; + +import java.io.File; + +import dev.DevUtils; + +/** + * detail: 路径相关工具类 + * @author Ttt + */ +public final class PathUtils { + + private PathUtils() { + } + + /** + * 获取 Android 系统根目录 - path /system + * @return /system + */ + public static String getRootPath() { + return getAbsolutePath(Environment.getRootDirectory()); + } + + /** + * 获取 data 目录 - path /data + * @return /data + */ + public static String getDataPath() { + return getAbsolutePath(Environment.getDataDirectory()); + } + + /** + * 获取下载缓存目录 - path data/cache + * @return data/cache + */ + public static String getDownloadCachePath() { + return getAbsolutePath(Environment.getDownloadCacheDirectory()); + } + + /** + * 获取内存应用数据路径 - path /data/data/package + * @return /data/data/package + */ + public static String getInternalAppDataPath() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + return DevUtils.getContext().getApplicationInfo().dataDir; + } + return getAbsolutePath(DevUtils.getContext().getDataDir()); + } + + /** + * 获取内存应用代码缓存路径 - path /data/data/package/code_cache + * @return /data/data/package/code_cache + */ + public static String getInternalAppCodeCacheDir() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return DevUtils.getContext().getApplicationInfo().dataDir + "/code_cache"; + } + return getAbsolutePath(DevUtils.getContext().getCodeCacheDir()); + } + + /** + * 获取内存应用数据库路径 - path /data/data/package/databases + * @return /data/data/package/databases + */ + public static String getInternalAppDbsPath() { + return DevUtils.getContext().getApplicationInfo().dataDir + "/databases"; + } + + /** + * 获取内存应用数据库路径 - path /data/data/package/databases/name + * @param name 数据库路径 + 名称 + * @return /data/data/package/databases/name + */ + public static String getInternalAppDbPath(final String name) { + return getAbsolutePath(DevUtils.getContext().getDatabasePath(name)); + } + + /** + * 获取内存应用文件路径 - path/data/data/package/files + * @return /data/data/package/files + */ + public static String getInternalAppFilesPath() { + return getAbsolutePath(DevUtils.getContext().getFilesDir()); + } + + /** + * 获取内存应用 SP 路径 - path/data/data/package/shared_prefs + * @return /data/data/package/shared_prefs + */ + public static String getInternalAppSpPath() { + return DevUtils.getContext().getApplicationInfo().dataDir + "shared_prefs"; + } + + /** + * 获取内存应用未备份文件路径 - path/data/data/package/no_backup + * @return /data/data/package/no_backup + */ + public static String getInternalAppNoBackupFilesPath() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return DevUtils.getContext().getApplicationInfo().dataDir + "no_backup"; + } + return getAbsolutePath(DevUtils.getContext().getNoBackupFilesDir()); + } + + /** + * 获取外存路径 - path/storage/emulated/0 + * @return /storage/emulated/0 + */ + public static String getExternalStoragePath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(Environment.getExternalStorageDirectory()); + } + + /** + * 获取外存音乐路径 - path/storage/emulated/0/Music + * @return /storage/emulated/0/Music + */ + public static String getExternalMusicPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC)); + } + + /** + * 获取外存播客路径 - path/storage/emulated/0/Podcasts + * @return /storage/emulated/0/Podcasts + */ + public static String getExternalPodcastsPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS)); + } + + /** + * 获取外存铃声路径 - path/storage/emulated/0/Ringtones + * @return /storage/emulated/0/Ringtones + */ + public static String getExternalRingtonesPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES)); + } + + /** + * 获取外存闹铃路径 - path/storage/emulated/0/Alarms + * @return /storage/emulated/0/Alarms + */ + public static String getExternalAlarmsPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS)); + } + + /** + * 获取外存通知路径 - path/storage/emulated/0/Notifications + * @return /storage/emulated/0/Notifications + */ + public static String getExternalNotificationsPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS)); + } + + /** + * 获取外存图片路径 - path/storage/emulated/0/Pictures + * @return /storage/emulated/0/Pictures + */ + public static String getExternalPicturesPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)); + } + + /** + * 获取外存影片路径 - path/storage/emulated/0/Movies + * @return /storage/emulated/0/Movies + */ + public static String getExternalMoviesPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)); + } + + /** + * 获取外存下载路径 - path/storage/emulated/0/Download + * @return /storage/emulated/0/Download + */ + public static String getExternalDownloadsPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)); + } + + /** + * 获取外存数码相机图片路径 - path/storage/emulated/0/DCIM + * @return /storage/emulated/0/DCIM + */ + public static String getExternalDcimPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)); + } + + /** + * 获取外存文档路径 - path/storage/emulated/0/Documents + * @return /storage/emulated/0/Documents + */ + public static String getExternalDocumentsPath() { + if (!isSDCardEnable()) return ""; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return getAbsolutePath(Environment.getExternalStorageDirectory()) + "/Documents"; + } + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)); + } + + /** + * 获取外存应用数据路径 - path/storage/emulated/0/Android/data/package + * @return /storage/emulated/0/Android/data/package + */ + public static String getExternalAppDataPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalCacheDir().getParentFile()); + } + + /** + * 获取外存应用缓存路径 - path/storage/emulated/0/Android/data/package/cache + * @return /storage/emulated/0/Android/data/package/cache + */ + public static String getExternalAppCachePath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalCacheDir()); + } + + /** + * 获取外存应用文件路径 - path/storage/emulated/0/Android/data/package/files + * @return /storage/emulated/0/Android/data/package/files + */ + public static String getExternalAppFilesPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(null)); + } + + /** + * 获取外存应用音乐路径 - path/storage/emulated/0/Android/data/package/files/Music + * @return /storage/emulated/0/Android/data/package/files/Music + */ + public static String getExternalAppMusicPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_MUSIC)); + } + + /** + * 获取外存应用播客路径 - path/storage/emulated/0/Android/data/package/files/Podcasts + * @return /storage/emulated/0/Android/data/package/files/Podcasts + */ + public static String getExternalAppPodcastsPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_PODCASTS)); + } + + /** + * 获取外存应用铃声路径 - path/storage/emulated/0/Android/data/package/files/Ringtones + * @return /storage/emulated/0/Android/data/package/files/Ringtones + */ + public static String getExternalAppRingtonesPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_RINGTONES)); + } + + /** + * 获取外存应用闹铃路径 - path/storage/emulated/0/Android/data/package/files/Alarms + * @return /storage/emulated/0/Android/data/package/files/Alarms + */ + public static String getExternalAppAlarmsPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_ALARMS)); + } + + /** + * 获取外存应用通知路径 - path/storage/emulated/0/Android/data/package/files/Notifications + * @return /storage/emulated/0/Android/data/package/files/Notifications + */ + public static String getExternalAppNotificationsPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_NOTIFICATIONS)); + } + + /** + * 获取外存应用图片路径 - path/storage/emulated/0/Android/data/package/files/Pictures + * @return path /storage/emulated/0/Android/data/package/files/Pictures + */ + public static String getExternalAppPicturesPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)); + } + + /** + * 获取外存应用影片路径 - path/storage/emulated/0/Android/data/package/files/Movies + * @return /storage/emulated/0/Android/data/package/files/Movies + */ + public static String getExternalAppMoviesPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_MOVIES)); + } + + /** + * 获取外存应用下载路径 - path/storage/emulated/0/Android/data/package/files/Download + * @return /storage/emulated/0/Android/data/package/files/Download + */ + public static String getExternalAppDownloadPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)); + } + + /** + * 获取外存应用数码相机图片路径 - path/storage/emulated/0/Android/data/package/files/DCIM + * @return /storage/emulated/0/Android/data/package/files/DCIM + */ + public static String getExternalAppDcimPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_DCIM)); + } + + /** + * 获取外存应用文档路径 - path/storage/emulated/0/Android/data/package/files/Documents + * @return /storage/emulated/0/Android/data/package/files/Documents + */ + public static String getExternalAppDocumentsPath() { + if (!isSDCardEnable()) return ""; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(null)) + "/Documents"; + } + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)); + } + + /** + * 获取外存应用 OBB 路径 - path/storage/emulated/0/Android/obb/package + * @return /storage/emulated/0/Android/obb/package + */ + public static String getExternalAppObbPath() { + if (!isSDCardEnable()) return ""; + return getAbsolutePath(DevUtils.getContext().getObbDir()); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = SDCardUtils = + // =============== + + /** + * 判断内置 SDCard 是否正常挂载 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSDCardEnable() { + return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); + } + + // ============= + // = FileUtils = + // ============= + + /** + * 获取文件绝对路径 + * @param file 文件 + * @return 文件绝对路径 + */ + private static String getAbsolutePath(final File file) { + return file != null ? file.getAbsolutePath() : null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/PhoneUtils.java b/lib/DevApp/src/main/java/dev/utils/app/PhoneUtils.java new file mode 100644 index 0000000000..2b1de748d9 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/PhoneUtils.java @@ -0,0 +1,1012 @@ +package dev.utils.app; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.SystemClock; +import android.provider.ContactsContract; +import android.provider.Settings; +import android.support.annotation.RequiresPermission; +import android.telephony.SmsManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Xml; + +import org.xmlpull.v1.XmlSerializer; + +import java.io.File; +import java.io.FileOutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 手机相关工具类 + * @author Ttt + *
+ *     所需权限
+ *     
+ * 
+ */ +public final class PhoneUtils { + + private PhoneUtils() { + } + + // 日志 TAG + private static final String TAG = PhoneUtils.class.getSimpleName(); + + /** + * 获取 TelephonyManager + * @return {@link TelephonyManager} + */ + public static TelephonyManager getTelephonyManager() { + try { + return (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTelephonyManager"); + } + return null; + } + + /** + * 判断设备是否是手机 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhone() { + try { + TelephonyManager telephonyManager = getTelephonyManager(); + return telephonyManager != null && telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isPhone"); + } + return false; + } + + /** + * 获取 SIM 卡状态 + * @return SIM 卡状态 + */ + public static int getSimState() { + return getSimState(-1); + } + + /** + * 获取 SIM 卡状态 + * @param slotIndex 卡槽索引 + * @return SIM 卡状态 + */ + public static int getSimState(final int slotIndex) { + try { + TelephonyManager telephonyManager = getTelephonyManager(); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) { + return telephonyManager.getSimState(); + } else { // 使用默认卡槽 + if (slotIndex == -1) { + return telephonyManager.getSimState(); + } + // 26 以上有公开 api + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return telephonyManager.getSimState(slotIndex); + } + // 反射调用方法 + Method method = telephonyManager.getClass().getDeclaredMethod("getSimState"); + method.setAccessible(true); + return (Integer) (method.invoke(telephonyManager, slotIndex)); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSimState"); + } + return TelephonyManager.SIM_STATE_UNKNOWN; + } + + /** + * 判断是否装载 SIM 卡 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSimReady() { + return isSimReady(-1); + } + + /** + * 判断是否装载 SIM 卡 + * @param slotIndex 卡槽索引 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSimReady(final int slotIndex) { + return getSimState(slotIndex) == TelephonyManager.SIM_STATE_READY; + } + + /** + * 获取 SIM 卡运营商的国家代码 + * @return SIM 卡运营商的国家代码 + */ + public static String getSimCountryIso() { + try { + return getTelephonyManager().getSimCountryIso(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSimCountryIso"); + } + return null; + } + + /** + * 获取 SIM 卡注册的网络运营商的国家代码 + * @return SIM 卡注册的网络运营商的国家代码 + */ + public static String getNetworkCountryIso() { + try { + return getTelephonyManager().getNetworkCountryIso(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getNetworkCountryIso"); + } + return null; + } + + /** + * 获取 SIM 卡运营商的国家代码 + * @return SIM 卡运营商的国家代码 + */ + public static String getSimCountry() { + try { + TelephonyManager telephonyManager = getTelephonyManager(); + // SIM 卡运营商的国家代码 + String simCountry = telephonyManager.getSimCountryIso(); + // 注册的网络运营商的国家代码 + String networkCountry = telephonyManager.getNetworkCountryIso(); + if (simCountry != null && simCountry.trim().length() != 0) { + return simCountry.trim(); + } else if (networkCountry != null && networkCountry.trim().length() != 0) { + return networkCountry.trim(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSimCountry"); + } + return null; + } + + /** + * 判断 SIM 卡运营商是否国内 + * @return 状态码 1 属于国内 ( 中国 ), 2 属于国外, 3 属于无 SIM 卡 + */ + public static int checkSimCountry() { + try { + String countryCode = getSimCountry(); + // 不等于 null, 表示属于存在 SIM 卡 + if (countryCode != null) { + // zh_CN Locale.SIMPLIFIED_CHINESE + // 截取前面两位属于 zh 表示属于中国 + String country = countryCode.substring(0, 2); + // 如果属于 ch 表示属于国内 + if (country.toLowerCase().equals("cn")) { + return 1; + } else { + return 2; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "checkSimCountry"); + } + return 3; + } + + /** + * 获取 MEID 码 + * @return MEID 码 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getMEID() { + return getMEID(-1); + } + + /** + * 获取 MEID 码 + * @param slotIndex 卡槽索引 + * @return MEID 码 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getMEID(final int slotIndex) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + try { + if (slotIndex == -1) return getTelephonyManager().getMeid(); + return getTelephonyManager().getMeid(slotIndex); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMEID"); + } + } + return null; + } + + /** + * 获取 IMEI 码 + * @return IMEI 码 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getIMEI() { + return getIMEI(-1); + } + + /** + * 获取 IMEI 码 + *
+     *     IMEI 是 International Mobile Equipment Identity ( 国际移动设备标识 ) 的简称
+     *     IMEI 由 15 位数字组成的「电子串号」它与每台手机一一对应, 而且该码是全世界唯一的
+     *     其组成为:
+     *     1. 前 6 位数 (TAC) 是「型号核准号码」一般代表机型
+     *     2. 接着的 2 位数 (FAC) 是「最后装配号」一般代表产地
+     *     3. 之后的 6 位数 (SNR) 是「串号」一般代表生产顺序号
+     *     4. 最后 1 位数 (SP) 通常是「0」为检验码, 目前暂备用
+     * 
+ * @param slotIndex 卡槽索引 + * @return IMEI 码 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getIMEI(final int slotIndex) { + try { + TelephonyManager telephonyManager = getTelephonyManager(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (slotIndex == -1) return telephonyManager.getImei(); + return telephonyManager.getImei(slotIndex); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // 反射调用方法 + Class clazz = telephonyManager.getClass(); + Method method = clazz.getDeclaredMethod("getImei"); + method.setAccessible(true); + return (String) method.invoke(telephonyManager); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIMEI"); + } + return null; + } + + /** + * 获取 IMSI 码 + *
+     *     IMSI 是国际移动用户识别码的简称 (International Mobile Subscriber Identity)
+     *     IMSI 共有 15 位, 其结构如下:
+     *     MCC + MNC + MIN
+     *     MCC: Mobile Country Code, 移动国家码, 共 3 位, 中国为 460
+     *     MNC: Mobile NetworkCode, 移动网络码, 共 2 位
+     *     在中国, 移动的代码为 00 和 02, 联通的代码为 01, 电信的代码为 03
+     *     合起来就是 Android 手机中 APN 配置文件中的代码
+     *     中国移动: 46000 46002 46007
+     *     中国联通: 46001 46006
+     *     中国电信: 46003 46005 46011
+     *     举例, 一个典型的 IMSI 号码为 460030912121001
+     * 
+ * @return IMSI 码 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getIMSI() { + try { + return getTelephonyManager().getSubscriberId(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIMSI"); + } + return null; + } + + /** + * 获取 SIM 卡运营商名称 ( 如: 中国移动、如中国联通、中国电信 ) + * @return SIM 卡运营商名称 + */ + public static String getSimOperatorName() { + try { + return getTelephonyManager().getSimOperatorName(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSimOperatorName"); + } + return null; + } + + /** + * 获取 SIM 卡运营商 MCC + MNC + * @return SIM 卡运营商 MCC + MNC + */ + public static String getSimOperator() { + try { + return getTelephonyManager().getSimOperator(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSimOperator"); + } + return null; + } + + /** + * 通过 IMSI 获取中国运营商简称 + * @return 中国运营商简称 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getChinaOperatorByIMSI() { + return getChinaOperatorByIMSI(getIMSI()); + } + + /** + * 通过 IMSI 获取中国运营商简称 + * @param imsi IMSI 码 + * @return 中国运营商简称 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getChinaOperatorByIMSI(final String imsi) { + if (imsi != null) { + if (imsi.startsWith("46000") || imsi.startsWith("46002") || imsi.startsWith("46007")) { + return "中国移动"; + } else if (imsi.startsWith("46001") || imsi.startsWith("46006")) { + return "中国联通"; + } else if (imsi.startsWith("46003") || imsi.startsWith("46005") || imsi.startsWith("46011")) { + return "中国电信"; + } + } + return null; + } + + /** + * 获取 SIM 卡中国运营商简称 + * @return SIM 卡中国运营商简称 + */ + public static String getChinaOperatorBySimOperator() { + return getChinaOperatorBySimOperator(getSimOperator()); + } + + /** + * 获取 SIM 卡中国运营商简称 + * @param simOperator SIM 卡运营商 MCC + MNC + * @return SIM 卡中国运营商简称 + */ + public static String getChinaOperatorBySimOperator(final String simOperator) { + if (simOperator != null) { + if (simOperator.equals("46000") || simOperator.equals("46002") || simOperator.equals("46007")) { + return "中国移动"; + } else if (simOperator.equals("46001") || simOperator.equals("46006")) { + return "中国联通"; + } else if (simOperator.equals("46003") || simOperator.equals("46005") || simOperator.equals("46011")) { + return "中国电信"; + } + } + return null; + } + + /** + * 获取手机类型 + *
+     *     {@link TelephonyManager#PHONE_TYPE_NONE} 0 手机制式未知
+     *     {@link TelephonyManager#PHONE_TYPE_GSM} 1 手机制式为 GSM, 移动和联通
+     *     {@link TelephonyManager#PHONE_TYPE_CDMA} 2 手机制式为 CDMA, 电信
+     *     {@link TelephonyManager#PHONE_TYPE_SIP} 3 手机制式为 SIP
+     * 
+ * @return 手机类型 + */ + public static int getPhoneType() { + try { + return getTelephonyManager().getPhoneType(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPhoneType"); + } + return TelephonyManager.PHONE_TYPE_NONE; + } + + /** + * 获取设备 id + * @return 设备 id + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getDeviceId() { + return getDeviceId(-1); + } + + /** + * 获取设备 id + * @param slotIndex 卡槽索引 + * @return 设备 id + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getDeviceId(final int slotIndex) { + try { + TelephonyManager telephonyManager = getTelephonyManager(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (slotIndex == -1) return telephonyManager.getDeviceId(); + return telephonyManager.getDeviceId(slotIndex); + } + return telephonyManager.getDeviceId(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDeviceId"); + } + return null; + } + + /** + * 获取 Android id + *
+     *     在设备首次启动时, 系统会随机生成一个 64 位的数字, 并把这个数字以十六进制字符串的形式保存下来
+     *     这个十六进制的字符串就是 ANDROID_ID, 当设备被 wipe 后该值会被重置
+     * 
+ * @return Android id + */ + public static String getAndroidId() { + try { + return Settings.Secure.getString(DevUtils.getContext().getContentResolver(), Settings.Secure.ANDROID_ID); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAndroidId"); + } + return null; + } + + /** + * 获取设备序列号 + * @return 设备序列号 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getSerialNumber() { + try { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? Build.getSerial() : Build.SERIAL; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSerialNumber"); + } + return null; + } + + /** + * 获取 SIM 卡序列号 + * @return SIM 卡序列号 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getSimSerialNumber() { + try { + return getTelephonyManager().getSimSerialNumber(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSimSerialNumber"); + } + return null; + } + + /** + * 获取设备唯一 UUID + * @return 设备唯一 UUID + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getUUID() { + String deviceId = getDeviceId() + ""; + String androidId = getAndroidId() + ""; + String serialNumber = getSerialNumber() + ""; + // 生成唯一关联 uuid + UUID deviceUUID = new UUID(androidId.hashCode(), ((long) deviceId.hashCode() << 32) | serialNumber.hashCode()); + return deviceUUID.toString(); + } + + /** + * 获取手机状态信息 + *
+     *     DeviceId(IMEI) = 99000311726612
+     *     DeviceSoftwareVersion = 00
+     *     Line1Number =
+     *     NetworkCountryIso = cn
+     *     NetworkOperator = 46003
+     *     NetworkOperatorName = 中国电信
+     *     NetworkType = 6
+     *     PhoneType = 2
+     *     SimCountryIso = cn
+     *     SimOperator = 46003
+     *     SimOperatorName = 中国电信
+     *     SimSerialNumber = 89860315045710604022
+     *     SimState = 5
+     *     SubscriberId(IMSI) = 460030419724900
+     *     VoiceMailNumber = *86
+     * 
+ * @return 手机状态信息 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static String getPhoneStatus() { + try { + TelephonyManager telephonyManager = getTelephonyManager(); + if (telephonyManager == null) return ""; + StringBuilder builder = new StringBuilder(); + builder.append("DeviceId(IMEI) = " + telephonyManager.getDeviceId()); + builder.append("\nDeviceSoftwareVersion = " + telephonyManager.getDeviceSoftwareVersion()); + builder.append("\nLine1Number = " + telephonyManager.getLine1Number()); + builder.append("\nNetworkCountryIso = " + telephonyManager.getNetworkCountryIso()); + builder.append("\nNetworkOperator = " + telephonyManager.getNetworkOperator()); + builder.append("\nNetworkOperatorName = " + telephonyManager.getNetworkOperatorName()); + builder.append("\nNetworkType = " + telephonyManager.getNetworkType()); + builder.append("\nPhoneType = " + telephonyManager.getPhoneType()); + builder.append("\nSimCountryIso = " + telephonyManager.getSimCountryIso()); + builder.append("\nSimOperator = " + telephonyManager.getSimOperator()); + builder.append("\nSimOperatorName = " + telephonyManager.getSimOperatorName()); + builder.append("\nSimSerialNumber = " + telephonyManager.getSimSerialNumber()); + builder.append("\nSimState = " + telephonyManager.getSimState()); + builder.append("\nSubscriberId(IMSI) = " + telephonyManager.getSubscriberId() + "(" + getChinaOperatorByIMSI() + ")"); + builder.append("\nVoiceMailNumber = " + telephonyManager.getVoiceMailNumber()); + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPhoneStatus"); + } + return ""; + } + + /** + * 跳至拨号界面 + * @param phoneNumber 电话号码 + * @return {@code true} success, {@code false} fail + */ + public static boolean dial(final String phoneNumber) { + Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber)); + if (isIntentAvailable(intent)) { + DevUtils.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + return true; + } + return false; + } + + /** + * 拨打电话 + * @param phoneNumber 电话号码 + * @return {@code true} success, {@code false} fail + */ + @RequiresPermission(android.Manifest.permission.CALL_PHONE) + public static boolean call(final String phoneNumber) { + Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNumber)); + if (isIntentAvailable(intent)) { + DevUtils.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + return true; + } + return false; + } + + /** + * 跳至发送短信界面 + * @param phoneNumber 接收号码 + * @param content 短信内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean sendSms(final String phoneNumber, final String content) { + Uri uri = Uri.parse("smsto:" + phoneNumber); + Intent intent = new Intent(Intent.ACTION_SENDTO, uri); + if (isIntentAvailable(intent)) { + intent.putExtra("sms_body", content); + DevUtils.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + return true; + } + return false; + } + + /** + * 发送短信 + * @param phoneNumber 接收号码 + * @param content 短信内容 + */ + @RequiresPermission(android.Manifest.permission.SEND_SMS) + public static void sendSmsSilent(final String phoneNumber, final String content) { + if (TextUtils.isEmpty(content)) return; + PendingIntent sentIntent = PendingIntent.getBroadcast(DevUtils.getContext(), 0, new Intent("send"), 0); + SmsManager smsManager = SmsManager.getDefault(); + if (content.length() >= 70) { + List ms = smsManager.divideMessage(content); + for (String str : ms) { + smsManager.sendTextMessage(phoneNumber, null, str, sentIntent, null); + } + } else { + smsManager.sendTextMessage(phoneNumber, null, content, sentIntent, null); + } + } + + /** + * 打开手机联系人界面点击联系人后便获取该号码 + *
+     *     @Override
+     *     protected void onActivityResult (int requestCode, int resultCode, Intent data) {
+     *          super.onActivityResult(requestCode, resultCode, data);
+     *          if (data != null) {
+     *              Uri uri = data.getData();
+     *              String num = null;
+     *              // 创建内容解析者
+     *              ContentResolver resolver = getContentResolver();
+     *              Cursor cursor = resolver.query(uri, null, null, null, null);
+     *              while (cursor.moveToNext()) {
+     *                  num = cursor.getString(cursor.getColumnIndex("data1"));
+     *              }
+     *              cursor.close();
+     *              num = num.replaceAll("-", "");
+     *          }
+     *      }
+     * 
+ * @param activity {@link Activity} + */ + public static void getContactNum(final Activity activity) { + if (activity != null && !activity.isFinishing()) { + Intent intent = new Intent(); + intent.setAction("android.intent.action.PICK"); + intent.setType("vnd.android.cursor.dir/phone_v2"); + activity.startActivityForResult(intent, 0); + } + } + + /** + * 获取手机联系人信息 + * @return 手机联系人信息 + */ + @RequiresPermission(allOf = {android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.READ_CONTACTS}) + public static List> getAllContactInfo() { + List> list = new ArrayList<>(); + // 游标 + Cursor cursor = null; + try { + // 1. 获取内容解析者 + ContentResolver resolver = DevUtils.getContext().getContentResolver(); + // 2. 获取内容提供者的地址 com.android.contacts + // raw_contacts 表的地址 raw_contacts + // view_data 表的地址 data + // 3. 生成查询地址 + Uri raw_uri = Uri.parse("content://com.android.contacts/raw_contacts"); + Uri date_uri = Uri.parse("content://com.android.contacts/data"); + // 4. 查询操作, 先查询 raw_contacts, 查询 contact_id + // projection 查询的字段 + cursor = resolver.query(raw_uri, new String[]{"contact_id"}, null, null, null); + // 5. 解析 cursor + if (cursor != null) { + while (cursor.moveToNext()) { + // 6. 获取查询的数据 + String contact_id = cursor.getString(0); + // cursor.getString(cursor.getColumnIndex("contact_id")); + // 判断 contact_id 是否为空 + if (!TextUtils.isEmpty(contact_id)) { + // 7. 根据 contact_id 查询 view_data 表中的数据 + // selection 查询条件 + // selectionArgs 查询条件的参数 + // sortOrder 排序 + // 空指针 1. null 方法、2. 参数为 null + Cursor c = resolver.query(date_uri, new String[]{"data1", "mimetype"}, + "raw_contact_id=?", new String[]{contact_id}, null); + Map map = new HashMap<>(); + // 8. 解析 c + if (c != null) { + while (c.moveToNext()) { + // 9. 获取数据 + String data1 = c.getString(0); + String mimetype = c.getString(1); + // 10. 根据类型去判断获取的 data1 数据并保存 + if (mimetype.equals("vnd.android.cursor.item/phone_v2")) { + map.put("phone", data1); // 电话 + } else if (mimetype.equals("vnd.android.cursor.item/name")) { + map.put("name", data1); // 姓名 + } + } + } + // 11. 添加到集合中数据 + list.add(map); + // 12. 关闭 cursor + if (c != null) { + c.close(); + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAllContactInfo"); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return list; + } + + /** + * 获取手机联系人信息 + * @return 手机联系人信息 + */ + public static List> getAllContactInfo2() { + List> list = new ArrayList<>(); + try { + Cursor cursor = DevUtils.getContext().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, + null, null, null, null); + while (cursor.moveToNext()) { + Map map = new HashMap<>(); + // 电话号码 + String phoneNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)) + .trim().replaceAll(" ", ""); + // 手机联系人名字 + String phoneName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)).trim(); + // 保存手机号 + map.put("phone", phoneNumber); + // 保存名字 + map.put("name", phoneName); + // 保存数据 + list.add(map); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAllContactInfo2"); + } + return list; + } + + /** + * 获取手机短信并保存到 xml 中 + * @param filePath 文件路径 /sdcard/xxx.xml + */ + @RequiresPermission(allOf = {android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_SMS}) + public static void getAllSMS(final String filePath) { + // 游标 + Cursor cursor = null; + try { + // 1. 获取短信 + // 1.1 获取内容解析者 + ContentResolver resolver = DevUtils.getContext().getContentResolver(); + // 1.2 获取内容提供者地址 sms, sms 表的地址 null 不写 + // 1.3 获取查询路径 + Uri uri = Uri.parse("content://sms"); + // 1.4 查询操作 + // projection 查询的字段 + // selection 查询的条件 + // selectionArgs 查询条件的参数 + // sortOrder 排序 + cursor = resolver.query(uri, new String[]{"address", "date", "type", "body"}, null, null, null); + // 设置最大进度 + int count = cursor.getCount(); // 获取短信的个数 + // 2. 备份短信 + // 2.1 获取 xml 序列器 + XmlSerializer xmlSerializer = Xml.newSerializer(); + // 2.2 设置 xml 文件保存的路径 + // os 保存的位置 + // encoding 编码格式 + xmlSerializer.setOutput(new FileOutputStream(new File(filePath)), "utf-8"); + // 2.3 设置头信息 + // standalone 是否独立保存 + xmlSerializer.startDocument("utf-8", true); + // 2.4 设置根标签 + xmlSerializer.startTag(null, "smss"); + // 1.5 解析 cursor + while (cursor.moveToNext()) { + SystemClock.sleep(1000); + // 2.5 设置短信的标签 + xmlSerializer.startTag(null, "sms"); + // 2.6 设置文本内容的标签 + xmlSerializer.startTag(null, "address"); + String address = cursor.getString(0); + // 2.7 设置文本内容 + xmlSerializer.text(address); + xmlSerializer.endTag(null, "address"); + xmlSerializer.startTag(null, "date"); + String date = cursor.getString(1); + xmlSerializer.text(date); + xmlSerializer.endTag(null, "date"); + xmlSerializer.startTag(null, "type"); + String type = cursor.getString(2); + xmlSerializer.text(type); + xmlSerializer.endTag(null, "type"); + xmlSerializer.startTag(null, "body"); + String body = cursor.getString(3); + xmlSerializer.text(body); + xmlSerializer.endTag(null, "body"); + xmlSerializer.endTag(null, "sms"); + } + xmlSerializer.endTag(null, "smss"); + xmlSerializer.endDocument(); + // 2.8 将数据刷新到文件中 + xmlSerializer.flush(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAllSMS"); + } + } + + // ============ + // = 双卡模块 = + // ============ + + /** + * detail: 双卡双待神机 IMSI、IMSI、PhoneType 信息实体类 + * @author Ttt + */ + public static class TeleInfo { + public String imsi_1; + public String imsi_2; + public String imei_1; + public String imei_2; + public int phoneType_1; + public int phoneType_2; + + /** + * 打印 TeleInfo 信息 + * @return TeleInfo 信息 + */ + public String printInfo() { + return "TeleInfo {" + + "imsi_1='" + imsi_1 + '\'' + + ", imsi_2='" + imsi_2 + '\'' + + ", imei_1='" + imei_1 + '\'' + + ", imei_2='" + imei_2 + '\'' + + ", phoneType_1=" + phoneType_1 + + ", phoneType_2=" + phoneType_2 + + '}'; + } + } + + /** + * 获取 MTK 神机的双卡 IMSI、IMSI 信息 + * @return MTK 神机的双卡 IMSI、IMSI 信息 + */ + public static TeleInfo getMtkTeleInfo() { + TeleInfo teleInfo = new TeleInfo(); + try { + Class phone = Class.forName("com.android.internal.telephony.Phone"); + + Field fields1 = phone.getField("GEMINI_SIM_1"); + fields1.setAccessible(true); + int simId_1 = (Integer) fields1.get(null); + + Field fields2 = phone.getField("GEMINI_SIM_2"); + fields2.setAccessible(true); + int simId_2 = (Integer) fields2.get(null); + + TelephonyManager telephonyManager = getTelephonyManager(); + Method getSubscriberIdGemini = TelephonyManager.class.getDeclaredMethod("getSubscriberIdGemini", int.class); + String imsi_1 = (String) getSubscriberIdGemini.invoke(telephonyManager, simId_1); + String imsi_2 = (String) getSubscriberIdGemini.invoke(telephonyManager, simId_2); + teleInfo.imsi_1 = imsi_1; + teleInfo.imsi_2 = imsi_2; + + Method getDeviceIdGemini = TelephonyManager.class.getDeclaredMethod("getDeviceIdGemini", int.class); + String imei_1 = (String) getDeviceIdGemini.invoke(telephonyManager, simId_1); + String imei_2 = (String) getDeviceIdGemini.invoke(telephonyManager, simId_2); + + teleInfo.imei_1 = imei_1; + teleInfo.imei_2 = imei_2; + + Method getPhoneTypeGemini = TelephonyManager.class.getDeclaredMethod("getPhoneTypeGemini", int.class); + int phoneType_1 = (Integer) getPhoneTypeGemini.invoke(telephonyManager, simId_1); + int phoneType_2 = (Integer) getPhoneTypeGemini.invoke(telephonyManager, simId_2); + teleInfo.phoneType_1 = phoneType_1; + teleInfo.phoneType_2 = phoneType_2; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMtkTeleInfo"); + } + return teleInfo; + } + + /** + * 获取 MTK 神机的双卡 IMSI、IMSI 信息 + * @return MTK 神机的双卡 IMSI、IMSI 信息 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static TeleInfo getMtkTeleInfo2() { + TeleInfo teleInfo = new TeleInfo(); + try { + TelephonyManager tm = getTelephonyManager(); + Class phone = Class.forName("com.android.internal.telephony.Phone"); + Field fields1 = phone.getField("GEMINI_SIM_1"); + fields1.setAccessible(true); + int simId_1 = (Integer) fields1.get(null); + Field fields2 = phone.getField("GEMINI_SIM_2"); + fields2.setAccessible(true); + int simId_2 = (Integer) fields2.get(null); + + Method getDefault = TelephonyManager.class.getMethod("getDefault", int.class); + TelephonyManager tm1 = (TelephonyManager) getDefault.invoke(tm, simId_1); + TelephonyManager tm2 = (TelephonyManager) getDefault.invoke(tm, simId_2); + + String imsi_1 = tm1.getSubscriberId(); + String imsi_2 = tm2.getSubscriberId(); + teleInfo.imsi_1 = imsi_1; + teleInfo.imsi_2 = imsi_2; + + String imei_1 = tm1.getDeviceId(); + String imei_2 = tm2.getDeviceId(); + teleInfo.imei_1 = imei_1; + teleInfo.imei_2 = imei_2; + + int phoneType_1 = tm1.getPhoneType(); + int phoneType_2 = tm2.getPhoneType(); + teleInfo.phoneType_1 = phoneType_1; + teleInfo.phoneType_2 = phoneType_2; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMtkTeleInfo2"); + } + return teleInfo; + } + + /** + * 获取 高通 神机的双卡 IMSI、IMSI 信息 + * @return 高通 神机的双卡 IMSI、IMSI 信息 + */ + public static TeleInfo getQualcommTeleInfo() { + TeleInfo teleInfo = new TeleInfo(); + try { + TelephonyManager telephonyManager = getTelephonyManager(); + Class simTMclass = Class.forName("android.telephony.MSimTelephonyManager"); + @SuppressWarnings("WrongConstant") + Object sim = DevUtils.getContext().getSystemService("phone_msim"); + int simId_1 = 0; + int simId_2 = 1; + + Method getSubscriberId = simTMclass.getMethod("getSubscriberId", int.class); + String imsi_1 = (String) getSubscriberId.invoke(sim, simId_1); + String imsi_2 = (String) getSubscriberId.invoke(sim, simId_2); + teleInfo.imsi_1 = imsi_1; + teleInfo.imsi_2 = imsi_2; + + Method getDeviceId = simTMclass.getMethod("getDeviceId", int.class); + String imei_1 = (String) getDeviceId.invoke(sim, simId_1); + String imei_2 = (String) getDeviceId.invoke(sim, simId_2); + teleInfo.imei_1 = imei_1; + teleInfo.imei_2 = imei_2; + + Method getDataState = simTMclass.getMethod("getDataState"); + int phoneType_1 = telephonyManager.getDataState(); + int phoneType_2 = (Integer) getDataState.invoke(sim); + teleInfo.phoneType_1 = phoneType_1; + teleInfo.phoneType_2 = phoneType_2; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getQualcommTeleInfo"); + } + return teleInfo; + } + + /** + * 获取 展讯 神机的双卡 IMSI、IMSI 信息 + * @return 展讯 神机的双卡 IMSI、IMSI 信息 + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static TeleInfo getSpreadtrumTeleInfo() { + TeleInfo teleInfo = new TeleInfo(); + try { + TelephonyManager tm1 = getTelephonyManager(); + String imsi_1 = tm1.getSubscriberId(); + String imei_1 = tm1.getDeviceId(); + int phoneType_1 = tm1.getPhoneType(); + teleInfo.imsi_1 = imsi_1; + teleInfo.imei_1 = imei_1; + teleInfo.phoneType_1 = phoneType_1; + + Class phoneFactory = Class.forName("com.android.internal.telephony.PhoneFactory"); + Method getServiceName = phoneFactory.getMethod("getServiceName", String.class, int.class); + getServiceName.setAccessible(true); + String spreadTmService = (String) getServiceName.invoke(phoneFactory, Context.TELEPHONY_SERVICE, 1); + + TelephonyManager tm2 = (TelephonyManager) DevUtils.getContext().getSystemService(spreadTmService); + String imsi_2 = tm2.getSubscriberId(); + String imei_2 = tm2.getDeviceId(); + int phoneType_2 = tm2.getPhoneType(); + teleInfo.imsi_2 = imsi_2; + teleInfo.imei_2 = imei_2; + teleInfo.phoneType_2 = phoneType_2; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSpreadtrumTeleInfo"); + } + return teleInfo; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = IntentUtils = + // =============== + + /** + * 判断 Intent 是否可用 + * @param intent {@link Intent} + * @return {@code true} yes, {@code false} no + */ + private static boolean isIntentAvailable(final Intent intent) { + if (intent == null) return false; + try { + return DevUtils.getContext().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isIntentAvailable"); + } + return false; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/PowerManagerUtils.java b/lib/DevApp/src/main/java/dev/utils/app/PowerManagerUtils.java new file mode 100644 index 0000000000..6bb43b6791 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/PowerManagerUtils.java @@ -0,0 +1,174 @@ +package dev.utils.app; + +import android.app.Activity; +import android.content.Context; +import android.os.Build; +import android.os.PowerManager; +import android.view.Window; +import android.view.WindowManager; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 电源管理工具类 + * @author Ttt + *
+ *     所需权限
+ *     
+ * 
+ */ +public final class PowerManagerUtils { + + // 日志 TAG + private static final String TAG = PowerManagerUtils.class.getSimpleName(); + // PowerManagerUtils 实例 + private static PowerManagerUtils sInstance; + + /** + * 获取 PowerManagerUtils 实例 + * @return {@link PowerManagerUtils} + */ + public static PowerManagerUtils getInstance() { + if (sInstance == null) { + sInstance = new PowerManagerUtils(); + } + return sInstance; + } + + // 电源管理类 + private PowerManager mPowerManager; + // 电源管理锁 + private PowerManager.WakeLock mWakeLock; + + /** + * 构造函数 + */ + private PowerManagerUtils() { + try { + // 获取系统服务 + mPowerManager = (PowerManager) DevUtils.getContext().getSystemService(Context.POWER_SERVICE); + // 电源管理锁 + mWakeLock = mPowerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.FULL_WAKE_LOCK, TAG); + } catch (Exception e) { + } + } + + /** + * 屏幕是否打开 ( 亮屏 ) + * @return {@code true} yes, {@code false} no + */ + public boolean isScreenOn() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR_MR1) { + return false; + } else { + if (mPowerManager == null) { + return false; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { + return mPowerManager.isInteractive(); + } + return mPowerManager.isScreenOn(); + } + } + + /** + * 唤醒 / 点亮 屏幕 + */ + public void turnScreenOn() { + if (mWakeLock != null && !mWakeLock.isHeld()) { + mWakeLock.acquire(); + } + } + + /** + * 释放屏幕锁, 允许休眠时间自动黑屏 + */ + public void turnScreenOff() { + if (mWakeLock != null && mWakeLock.isHeld()) { + try { + mWakeLock.release(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "turnScreenOff"); + } + } + } + + /** + * 获取 PowerManager.WakeLock + * @return {@link PowerManager.WakeLock} + */ + public PowerManager.WakeLock getWakeLock() { + return mWakeLock; + } + + /** + * 设置 PowerManager.WakeLock + * @param wakeLock {@link PowerManager.WakeLock} + */ + public void setWakeLock(final PowerManager.WakeLock wakeLock) { + this.mWakeLock = wakeLock; + } + + /** + * 获取 PowerManager + * @return {@link PowerManager} + */ + public PowerManager getPowerManager() { + return mPowerManager; + } + + /** + * 设置 PowerManager + * @param powerManager {@link PowerManager} + */ + public void setPowerManager(final PowerManager powerManager) { + this.mPowerManager = powerManager; + } + + /** + * 设置屏幕常亮 + * @param activity {@link Activity} + */ + public static void setBright(final Activity activity) { + if (activity != null) { + setBright(activity.getWindow()); + } + } + + /** + * 设置屏幕常亮 + * @param window {@link Activity#getWindow()} + */ + public static void setBright(final Window window) { + if (window != null) { + window.setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } + + /** + * 设置 WakeLock 常亮 + *
+     *     {@link Activity#onResume()} 调用 setWakeLockToBright()
+     *     {@link Activity#onPause()} 调用 mWakeLock.release()
+     * 
+ * @return {@link PowerManager.WakeLock} + */ + public static PowerManager.WakeLock setWakeLockToBright() { + try { + // onResume() + PowerManager.WakeLock mWakeLock = PowerManagerUtils.getInstance().getPowerManager() + .newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG); + mWakeLock.acquire(); // 常量, 持有不黑屏 + +// // onPause() +// if (mWakeLock != null) { +// mWakeLock.release(); // 释放资源, 到休眠时间自动黑屏 +// } + return mWakeLock; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setWakeLockToBright"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ProcessUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ProcessUtils.java new file mode 100644 index 0000000000..7701421d35 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ProcessUtils.java @@ -0,0 +1,317 @@ +package dev.utils.app; + +import android.app.ActivityManager; +import android.app.usage.UsageStats; +import android.app.usage.UsageStatsManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.provider.Settings; +import android.support.annotation.RequiresPermission; +import android.text.TextUtils; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 进程相关工具类 + * @author Ttt + *
+ *     所需权限
+ *     
+ *     
+ * 
+ */ +public final class ProcessUtils { + + private ProcessUtils() { + } + + // 日志 TAG + private static final String TAG = ProcessUtils.class.getSimpleName(); + + /** + * 销毁自身进程 + */ + public static void kill() { + kill(android.os.Process.myPid()); + } + + /** + * 销毁进程 + * @param pid 进程 id + */ + public static void kill(final int pid) { + android.os.Process.killProcess(pid); + } + + /** + * 判断是否当前进程 + * @return {@code true} yes, {@code false} no + */ + public static boolean isCurProcess() { + try { + return DevUtils.getContext().getPackageName().equals(getCurProcessName()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isCurProcess"); + } + return false; + } + + /** + * 获取当前进程名 + * @return 进程名 + */ + public static String getCurProcessName() { + try { + // 获取自身进程 id + int pid = android.os.Process.myPid(); + // 判断全部运行中的进程 + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + List lists = activityManager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + if (appProcess.pid == pid) { + return appProcess.processName; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getCurProcessName"); + } + return null; + } + + /** + * 获取进程 id 对应的进程名 + * @param pid 进程 id + * @return 进程名 + */ + public static String getProcessName(final int pid) { + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline")); + String processName = br.readLine(); + if (!TextUtils.isEmpty(processName)) { + processName = processName.trim(); + } + return processName; + } catch (Throwable throwable) { + LogPrintUtils.eTag(TAG, throwable, "getProcessName"); + } finally { + if (br != null) { + try { + br.close(); + } catch (Exception e) { + } + } + } + return null; + } + + /** + * 根据包名获取进程 id + * @param packageName 应用包名 + * @return 进程 id + */ + public static int getPid(final String packageName) { + try { + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + List lists = activityManager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + if (appProcess.processName.equals(packageName)) { + return appProcess.pid; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPid"); + } + return 0; + } + + /** + * 根据进程 id 获取进程信息 + * @param pid 进程 id + * @return 进程信息 + */ + public static ActivityManager.RunningAppProcessInfo getRunningAppProcessInfo(final int pid) { + try { + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + List lists = activityManager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + if (appProcess.pid == pid) { + return appProcess; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getRunningAppProcessInfo"); + } + return null; + } + + /** + * 根据包名获取进程信息 + * @param packageName 应用包名 + * @return 进程信息 + */ + public static ActivityManager.RunningAppProcessInfo getRunningAppProcessInfo(final String packageName) { + try { + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + List lists = activityManager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + if (appProcess.processName.equals(packageName)) { + return appProcess; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getRunningAppProcessInfo"); + } + return null; + } + + // = + + /** + * 获取前台线程包名 + * @return 前台应用包名 + */ + @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) + public static String getForegroundProcessName() { + try { + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + if (activityManager == null) return null; + List lists = activityManager.getRunningAppProcesses(); + if (lists != null && lists.size() > 0) { + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { + return appProcess.processName; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getForegroundProcessName"); + } + // SDK 大于 21 时 + if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP) { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); + List listResolves = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + // 无权限 + if (listResolves.size() <= 0) { + return null; + } + + UsageStatsManager usageStatsManager = (UsageStatsManager) DevUtils.getContext().getSystemService(Context.USAGE_STATS_SERVICE); + List listUsageStats = null; + if (usageStatsManager != null) { + long endTime = System.currentTimeMillis(); + long beginTime = endTime - 86400000 * 7; + listUsageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, beginTime, endTime); + } + if (listUsageStats == null || listUsageStats.isEmpty()) return null; + UsageStats recentStats = null; + for (UsageStats usageStats : listUsageStats) { + if (recentStats == null || usageStats.getLastTimeUsed() > recentStats.getLastTimeUsed()) { + recentStats = usageStats; + } + } + return recentStats == null ? null : recentStats.getPackageName(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getForegroundProcessName"); + } + } + return null; + } + + /** + * 获取后台服务进程 + * @return 后台服务进程 + */ + @RequiresPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) + public static Set getAllBackgroundProcesses() { + try { + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + if (activityManager == null) return Collections.emptySet(); + List lists = activityManager.getRunningAppProcesses(); + Set set = new HashSet<>(); + if (lists != null) { + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + Collections.addAll(set, appProcess.pkgList); + } + } + return set; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAllBackgroundProcesses"); + } + return Collections.emptySet(); + } + + /** + * 杀死所有的后台服务进程 + * @return 被暂时杀死的服务集合 + */ + @RequiresPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) + public static Set killAllBackgroundProcesses() { + try { + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + if (activityManager == null) return Collections.emptySet(); + List lists = activityManager.getRunningAppProcesses(); + Set set = new HashSet<>(); + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + for (String packageName : appProcess.pkgList) { + activityManager.killBackgroundProcesses(packageName); + set.add(packageName); + } + } + lists = activityManager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + for (String packageName : appProcess.pkgList) { + set.remove(packageName); + } + } + return set; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "killAllBackgroundProcesses"); + } + return null; + } + + /** + * 杀死后台服务进程 + * @param packageName 应用包名 + * @return {@code true} success, {@code false} fail + */ + @RequiresPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) + public static boolean killBackgroundProcesses(final String packageName) { + try { + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + if (activityManager == null) return false; + List lists = activityManager.getRunningAppProcesses(); + if (lists == null || lists.size() == 0) return true; + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + if (Arrays.asList(appProcess.pkgList).contains(packageName)) { + activityManager.killBackgroundProcesses(packageName); + } + } + lists = activityManager.getRunningAppProcesses(); + if (lists == null || lists.size() == 0) return true; + for (ActivityManager.RunningAppProcessInfo appProcess : lists) { + if (Arrays.asList(appProcess.pkgList).contains(packageName)) { + return false; + } + } + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "killBackgroundProcesses"); + } + return false; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ResourceUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ResourceUtils.java new file mode 100644 index 0000000000..123f52dd16 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ResourceUtils.java @@ -0,0 +1,832 @@ +package dev.utils.app; + +import android.content.ContentResolver; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.support.annotation.AnimRes; +import android.support.annotation.AnimatorRes; +import android.support.annotation.AnyRes; +import android.support.annotation.ArrayRes; +import android.support.annotation.BoolRes; +import android.support.annotation.ColorInt; +import android.support.annotation.ColorRes; +import android.support.annotation.DimenRes; +import android.support.annotation.DrawableRes; +import android.support.annotation.IntegerRes; +import android.support.annotation.RawRes; +import android.support.annotation.StringRes; +import android.support.v4.content.ContextCompat; +import android.util.DisplayMetrics; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 资源文件工具类 + * @author Ttt + */ +public final class ResourceUtils { + + private ResourceUtils() { + } + + // 日志 TAG + private static final String TAG = ResourceUtils.class.getSimpleName(); + + // ================ + // = 快捷获取方法 = + // ================ + + /** + * 获取 Resources + * @return {@link Resources} + */ + public static Resources getResources() { + try { + return DevUtils.getContext().getResources(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getResources"); + } + return null; + } + + /** + * 获取 Resources.Theme + * @return {@link Resources.Theme} + */ + public static Resources.Theme getTheme() { + try { + return DevUtils.getContext().getTheme(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTheme"); + } + return null; + } + + /** + * 获取 AssetManager + * @return {@link AssetManager} + */ + public static AssetManager getAssets() { + try { + return DevUtils.getContext().getAssets(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAssets"); + } + return null; + } + + /** + * 获取 ContentResolver + * @return {@link ContentResolver} + */ + public static ContentResolver getContentResolver() { + try { + return DevUtils.getContext().getContentResolver(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getContentResolver"); + } + return null; + } + + /** + * 获取 DisplayMetrics + * @return {@link DisplayMetrics} + */ + public static DisplayMetrics getDisplayMetrics() { + try { + return DevUtils.getContext().getResources().getDisplayMetrics(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDisplayMetrics"); + } + return null; + } + + /** + * 获取 Configuration + * @return {@link Configuration} + */ + public static Configuration getConfiguration() { + try { + return DevUtils.getContext().getResources().getConfiguration(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getConfiguration"); + } + return null; + } + + /** + * 获取 ColorStateList + * @param id resource identifier of a {@link ColorStateList} + * @return {@link ColorStateList} + */ + public static ColorStateList getColorStateList(@ColorRes final int id) { + try { + return ContextCompat.getColorStateList(DevUtils.getContext(), id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getColorStateList"); + } + return null; + } + + /** + * 获取 String + * @param id R.string.id + * @return String + */ + public static String getString(@StringRes final int id) { + try { + return DevUtils.getContext().getResources().getString(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getString"); + } + return null; + } + + /** + * 获取 String + * @param id R.string.id + * @param formatArgs 格式化参数 + * @return String + */ + public static String getString(@StringRes final int id, final Object... formatArgs) { + try { + return DevUtils.getContext().getResources().getString(id, formatArgs); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getString"); + } + return null; + } + + /** + * 获取 Color + * @param colorId R.color.id + * @return Color + */ + public static int getColor(@ColorRes final int colorId) { + try { + return ContextCompat.getColor(DevUtils.getContext(), colorId); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getColor"); + } + return -1; + } + + /** + * 获取 Drawable + * @param drawableId R.drawable.id + * @return {@link Drawable} + */ + public static Drawable getDrawable(@DrawableRes final int drawableId) { + try { + return ContextCompat.getDrawable(DevUtils.getContext(), drawableId); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDrawable"); + } + return null; + } + + /** + * 获取指定颜色 Drawable + * @param color 颜色值 + * @return 指定颜色 Drawable + */ + public static ColorDrawable getColorDrawable(@ColorInt final int color) { + try { + return new ColorDrawable(color); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getColorDrawable"); + } + return null; + } + + /** + * 获取十六进制颜色值 Drawable + * @param color 十六进制颜色值 + * @return 十六进制颜色值 Drawable + */ + public static ColorDrawable getColorDrawable(final String color) { + try { + return new ColorDrawable(Color.parseColor(color)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getColorDrawable"); + } + return null; + } + + /** + * 获取 Bitmap + * @param resId resource identifier + * @return {@link Bitmap} + */ + public static Bitmap getBitmap(final int resId) { + try { + return BitmapFactory.decodeResource(DevUtils.getContext().getResources(), resId); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmap"); + } + return null; + } + + /** + * 获取 Bitmap + * @param resId resource identifier + * @param options {@link BitmapFactory.Options} + * @return {@link Bitmap} + */ + public static Bitmap getBitmap(final int resId, final BitmapFactory.Options options) { + try { + return BitmapFactory.decodeResource(DevUtils.getContext().getResources(), resId, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmap"); + } + return null; + } + + /** + * 获取 Dimension + * @param id resource identifier + * @return Dimension + */ + public static float getDimension(@DimenRes final int id) { + try { + return DevUtils.getContext().getResources().getDimension(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDimension"); + } + return 0f; + } + + /** + * 获取 Boolean + * @param id resource identifier + * @return Boolean + */ + public static boolean getBoolean(@BoolRes final int id) { + try { + return DevUtils.getContext().getResources().getBoolean(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBoolean"); + } + return false; + } + + /** + * 获取 Integer + * @param id resource identifier + * @return Integer + */ + public static int getInteger(@IntegerRes final int id) { + try { + return DevUtils.getContext().getResources().getInteger(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getInteger"); + } + return -1; + } + + /** + * 获取 Animation + * @param id resource identifier + * @return XmlResourceParser + */ + public static XmlResourceParser getAnimation(@AnimatorRes @AnimRes final int id) { + try { + return DevUtils.getContext().getResources().getAnimation(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAnimation"); + } + return null; + } + + /** + * 获取给定资源标识符的全名 + * @param id resource identifier + * @return Integer + */ + public static String getResourceName(@AnyRes final int id) { + try { + return DevUtils.getContext().getResources().getResourceName(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getResourceName"); + } + return null; + } + + /** + * 获取 int[] + * @param id resource identifier + * @return int[] + */ + public static int[] getIntArray(@ArrayRes final int id) { + try { + return DevUtils.getContext().getResources().getIntArray(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIntArray"); + } + return null; + } + + /** + * 获取 String[] + * @param id resource identifier + * @return String[] + */ + public static String[] getStringArray(@ArrayRes final int id) { + try { + return DevUtils.getContext().getResources().getStringArray(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getStringArray"); + } + return null; + } + + /** + * 获取 CharSequence[] + * @param id resource identifier + * @return CharSequence[] + */ + public static CharSequence[] getTextArray(@ArrayRes final int id) { + try { + return DevUtils.getContext().getResources().getTextArray(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTextArray"); + } + return null; + } + + // = + + /** + * 获取 layout id + * @param resName layout xml fileName + * @return layout id + */ + public static int getLayoutId(final String resName) { + return getIdentifier(resName, "layout"); + } + + /** + * 获取 drawable id + * @param resName drawable name + * @return drawable id + */ + public static int getDrawableId(final String resName) { + return getIdentifier(resName, "drawable"); + } + + /** + * 获取 mipmap id + * @param resName mipmap name + * @return mipmap id + */ + public static int getMipmapId(final String resName) { + return getIdentifier(resName, "mipmap"); + } + + /** + * 获取 menu id + * @param resName menu name + * @return menu id + */ + public static int getMenuId(final String resName) { + return getIdentifier(resName, "menu"); + } + + /** + * 获取 raw id + * @param resName raw name + * @return raw id + */ + public static int getRawId(final String resName) { + return getIdentifier(resName, "raw"); + } + + /** + * 获取 anim id + * @param resName anim xml fileName + * @return anim id + */ + public static int getAnimId(final String resName) { + return getIdentifier(resName, "anim"); + } + + /** + * 获取 color id + * @param resName color name + * @return color id + */ + public static int getColorId(final String resName) { + return getIdentifier(resName, "color"); + } + + /** + * 获取 dimen id + * @param resName dimen name + * @return dimen id + */ + public static int getDimenId(final String resName) { + return getIdentifier(resName, "dimen"); + } + + /** + * 获取 attr id + * @param resName attr name + * @return attr id + */ + public static int getAttrId(final String resName) { + return getIdentifier(resName, "attr"); + } + + /** + * 获取 style id + * @param resName style name + * @return style id + */ + public static int getStyleId(final String resName) { + return getIdentifier(resName, "style"); + } + + /** + * 获取 styleable id + * @param resName styleable name + * @return styleable id + */ + public static int getStyleableId(final String resName) { + return getIdentifier(resName, "styleable"); + } + + /** + * 获取 id + * @param resName id name + * @return id + */ + public static int getId(final String resName) { + return getIdentifier(resName, "id"); + } + + /** + * 获取 string id + * @param resName string name + * @return string id + */ + public static int getStringId(final String resName) { + return getIdentifier(resName, "string"); + } + + /** + * 获取 bool id + * @param resName bool name + * @return bool id + */ + public static int getBoolId(final String resName) { + return getIdentifier(resName, "bool"); + } + + /** + * 获取 integer id + * @param resName integer name + * @return integer id + */ + public static int getIntegerId(final String resName) { + return getIdentifier(resName, "integer"); + } + + /** + * 获取资源 id + * @param resName 资源名 + * @param defType 资源类型 + * @return 资源 id + */ + public static int getIdentifier(final String resName, final String defType) { + try { + return getIdentifier(resName, defType, DevUtils.getContext().getPackageName()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIdentifier - " + resName + ": " + defType); + } + return 0; + } + + /** + * 获取资源 id + * @param resName 资源名 + * @param defType 资源类型 + * @param packageName 应用包名 + * @return 资源 id + */ + public static int getIdentifier(final String resName, final String defType, final String packageName) { + try { + return DevUtils.getContext().getResources().getIdentifier(resName, defType, packageName); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIdentifier - " + packageName + " " + resName + ": " + defType); + } + return 0; + } + + // = + + /** + * 获取 AssetManager 指定资源 InputStream + * @param fileName 文件名 + * @return {@link InputStream} + */ + public static InputStream openAssetsResource(final String fileName) { + try { + return DevUtils.getContext().getAssets().open(fileName); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openAssetsResource"); + } + return null; + } + + /** + * 获取对应资源 InputStream + * @param id resource identifier + * @return {@link InputStream} + */ + public static InputStream openRawResource(@RawRes final int id) { + try { + return DevUtils.getContext().getResources().openRawResource(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openRawResource"); + } + return null; + } + + /** + * 获取对应资源 AssetFileDescriptor + * @param id resource identifier + * @return {@link AssetFileDescriptor} + */ + public static AssetFileDescriptor openRawResourceFd(@RawRes final int id) { + try { + return DevUtils.getContext().getResources().openRawResourceFd(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openRawResourceFd"); + } + return null; + } + + // ================ + // = 读取资源文件 = + // ================ + + /** + * 获取 Assets 资源文件数据 + *
+     *     直接传入文件名、文件夹 / 文件名 等
+     *     根目录 a.txt
+     *     子目录 /www/a.html
+     * 
+ * @param fileName 文件名 + * @return 文件 byte[] 数据 + */ + public static byte[] readBytesFromAssets(final String fileName) { + InputStream is = null; + try { + is = DevUtils.getContext().getResources().getAssets().open(fileName); + int length = is.available(); + byte[] buffer = new byte[length]; + is.read(buffer); + is.close(); + return buffer; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "readBytesFromAssets"); + } finally { + if (is != null) { + try { + is.close(); + } catch (Exception e) { + } + } + } + return null; + } + + /** + * 获取 Assets 资源文件数据 + * @param fileName 文件名 + * @return 文件字符串内容 + */ + public static String readStringFromAssets(final String fileName) { + try { + return new String(readBytesFromAssets(fileName), "UTF-8"); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "readStringFromAssets"); + } + return null; + } + + // = + + /** + * 获取 Raw 资源文件数据 + * @param resId 资源 id + * @return 文件 byte[] 数据 + */ + public static byte[] readBytesFromRaw(@RawRes final int resId) { + InputStream is = null; + try { + is = DevUtils.getContext().getResources().openRawResource(resId); + int length = is.available(); + byte[] buffer = new byte[length]; + is.read(buffer); + is.close(); + return buffer; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "readBytesFromRaw"); + } finally { + if (is != null) { + try { + is.close(); + } catch (Exception e) { + } + } + } + return null; + } + + /** + * 获取 Raw 资源文件数据 + * @param resId 资源 id + * @return 文件字符串内容 + */ + public static String readStringFromRaw(@RawRes final int resId) { + try { + return new String(readBytesFromRaw(resId), "UTF-8"); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "readStringFromRaw"); + } + return null; + } + + // = + + /** + * 获取 Assets 资源文件数据 ( 返回 List 一行的全部内容属于一个索引 ) + * @param fileName 文件名 + * @return {@link List} + */ + public static List geFileToListFromAssets(final String fileName) { + InputStream is = null; + BufferedReader br = null; + try { + is = DevUtils.getContext().getResources().getAssets().open(fileName); + br = new BufferedReader(new InputStreamReader(is)); + + List lists = new ArrayList<>(); + String line; + while ((line = br.readLine()) != null) { + lists.add(line); + } + return lists; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "geFileToListFromAssets"); + } finally { + if (is != null) { + try { + is.close(); + } catch (Exception e) { + } + } + if (br != null) { + try { + br.close(); + } catch (Exception e) { + } + } + } + return null; + } + + /** + * 获取 Raw 资源文件数据 ( 返回 List 一行的全部内容属于一个索引 ) + * @param resId 资源 id + * @return {@link List} + */ + public static List geFileToListFromRaw(@RawRes final int resId) { + InputStream is = null; + BufferedReader br = null; + try { + is = DevUtils.getContext().getResources().openRawResource(resId); + br = new BufferedReader(new InputStreamReader(is)); + + List lists = new ArrayList<>(); + String line; + while ((line = br.readLine()) != null) { + lists.add(line); + } + return lists; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "geFileToListFromRaw"); + } finally { + if (is != null) { + try { + is.close(); + } catch (Exception e) { + } + } + if (br != null) { + try { + br.close(); + } catch (Exception e) { + } + } + } + return null; + } + + // = + + /** + * 获取 Assets 资源文件数据并保存到本地 + * @param fileName 文件名 + * @param file 文件保存地址 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveAssetsFormFile(final String fileName, final File file) { + try { + // 获取 Assets 文件 + InputStream is = DevUtils.getContext().getResources().getAssets().open(fileName); + // 存入 SDCard + FileOutputStream fos = new FileOutputStream(file); + // 设置数据缓冲 + byte[] buffer = new byte[1024]; + // 创建输入输出流 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int len; + while ((len = is.read(buffer)) != -1) { + baos.write(buffer, 0, len); + } + // 保存数据 + byte[] bytes = baos.toByteArray(); + // 写入保存的文件 + fos.write(bytes); + // 关闭流 + baos.close(); + is.close(); + // = + fos.flush(); + fos.close(); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "saveAssetsFormFile"); + } + return false; + } + + /** + * 获取 Raw 资源文件数据并保存到本地 + * @param resId 资源 id + * @param file 文件保存地址 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveRawFormFile(@RawRes final int resId, final File file) { + try { + // 获取 raw 文件 + InputStream is = DevUtils.getContext().getResources().openRawResource(resId); + // 存入 SDCard + FileOutputStream fos = new FileOutputStream(file); + // 设置数据缓冲 + byte[] buffer = new byte[1024]; + // 创建输入输出流 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int len; + while ((len = is.read(buffer)) != -1) { + baos.write(buffer, 0, len); + } + // 保存数据 + byte[] bytes = baos.toByteArray(); + // 写入保存的文件 + fos.write(bytes); + // 关闭流 + baos.close(); + is.close(); + // = + fos.flush(); + fos.close(); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "saveRawFormFile"); + } + return false; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/SDCardUtils.java b/lib/DevApp/src/main/java/dev/utils/app/SDCardUtils.java new file mode 100644 index 0000000000..7e2b0cba1d --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/SDCardUtils.java @@ -0,0 +1,473 @@ +package dev.utils.app; + +import android.content.Context; +import android.os.Build; +import android.os.Environment; +import android.os.StatFs; +import android.os.storage.StorageManager; +import android.text.format.Formatter; + +import java.io.File; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: SDCard 工具类 + * @author Ttt + */ +public final class SDCardUtils { + + private SDCardUtils() { + } + + // 日志 TAG + private static final String TAG = SDCardUtils.class.getSimpleName(); + + /** + * 判断内置 SDCard 是否正常挂载 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSDCardEnable() { + return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); + } + + /** + * 获取内置 SDCard File + * @return {@link File} + */ + public static File getSDCardFile() { + return Environment.getExternalStorageDirectory(); + } + + /** + * 获取内置 SDCard 绝对路径 + *
+     *     结尾无添加 File.separator
+     * 
+ * @return 内置 SDCard 绝对路径 + */ + public static String getSDCardPath() { + return Environment.getExternalStorageDirectory().getAbsolutePath(); + } + + /** + * 获取内置 SDCard 绝对路径 + *
+     *     结尾添加 File.separator
+     * 
+ * @return 内置 SDCard 绝对路径 + */ + public static String getSDCardPathSeparator() { + return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator; + } + + // = + + /** + * 获取内置 SDCard 中指定文件路径 + * @param filePath 文件路径 + * @return 内置 SDCard 中指定文件路径 + */ + public static File getSDCardFile(final String filePath) { + if (filePath == null) return null; + return new File(getSDCardPath(), filePath); + } + + /** + * 获取内置 SDCard 中指定文件路径 + * @param filePath 文件路径 + * @return 内置 SDCard 中指定文件路径 + */ + public static String getSDCardPath(final String filePath) { + if (filePath == null) return null; + return new File(getSDCardPath(), filePath).getAbsolutePath(); + } + + // = + + /** + * 判断 SDCard 是否可用 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSDCardEnablePath() { + return !getSDCardPaths().isEmpty(); + } + + /** + * 获取 SDCard 路径 + * @return SDCard 路径 + */ + @SuppressWarnings("TryWithIdenticalCatches") + public static List getSDCardPaths() { + List listPaths = new ArrayList<>(); + try { + StorageManager storageManager = (StorageManager) DevUtils.getContext().getSystemService(Context.STORAGE_SERVICE); + Method getVolumePathsMethod = StorageManager.class.getMethod("getVolumePaths"); + getVolumePathsMethod.setAccessible(true); + Object invoke = getVolumePathsMethod.invoke(storageManager); + listPaths = Arrays.asList((String[]) invoke); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSDCardPaths"); + } + return listPaths; + } + + /** + * 获取 SDCard 路径 + * @param removable {@code true} 外置 SDCard, {@code false} 内置 SDCard + * @return SDCard 路径 + */ + @SuppressWarnings("TryWithIdenticalCatches") + public static List getSDCardPaths(final boolean removable) { + List listPaths = new ArrayList<>(); + try { + StorageManager storageManager = (StorageManager) DevUtils.getContext().getSystemService(Context.STORAGE_SERVICE); + Class storageVolumeClazz = Class.forName("android.os.storage.StorageVolume"); + Method getVolumeList = StorageManager.class.getMethod("getVolumeList"); + Method getPath = storageVolumeClazz.getMethod("getPath"); + Method isRemovable = storageVolumeClazz.getMethod("isRemovable"); + Object result = getVolumeList.invoke(storageManager); + final int length = Array.getLength(result); + for (int i = 0; i < length; i++) { + Object storageVolumeElement = Array.get(result, i); + String path = (String) getPath.invoke(storageVolumeElement); + boolean res = (Boolean) isRemovable.invoke(storageVolumeElement); + if (removable == res) { + listPaths.add(path); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSDCardPaths"); + } + return listPaths; + } + + // ============ + // = 空间信息 = + // ============ + + /** + * 获取内置 SDCard 空间总大小 + * @return 内置 SDCard 空间总大小 + */ + public static String getAllBlockSizeFormat() { + try { + long size = getAllBlockSize(Environment.getExternalStorageDirectory().getPath()); + // 格式化 + return Formatter.formatFileSize(DevUtils.getContext(), size); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAllBlockSizeFormat"); + return null; + } + } + + /** + * 获取内置 SDCard 空闲空间大小 + * @return 内置 SDCard 空闲空间大小 + */ + public static String getAvailableBlocksFormat() { + try { + long size = getAvailableBlocks(Environment.getExternalStorageDirectory().getPath()); + // 格式化 + return Formatter.formatFileSize(DevUtils.getContext(), size); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAvailableBlocksFormat"); + return null; + } + } + + /** + * 获取内置 SDCard 已使用空间大小 + * @return 内置 SDCard 已使用空间大小 + */ + public static String getUsedBlocksFormat() { + try { + long size = getUsedBlocks(Environment.getExternalStorageDirectory().getPath()); + // 格式化 + return Formatter.formatFileSize(DevUtils.getContext(), size); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getUsedBlocksFormat"); + return null; + } + } + + // = + + /** + * 获取内置 SDCard 空间总大小 + * @return 内置 SDCard 空间总大小 + */ + public static long getAllBlockSize() { + try { + return getAllBlockSize(Environment.getExternalStorageDirectory().getPath()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAllBlockSize"); + return 0L; + } + } + + /** + * 获取内置 SDCard 空闲空间大小 + * @return 内置 SDCard 空闲空间大小 + */ + public static long getAvailableBlocks() { + try { + return getAvailableBlocks(Environment.getExternalStorageDirectory().getPath()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAvailableBlocks"); + return 0L; + } + } + + /** + * 获取内置 SDCard 已使用空间大小 + * @return 内置 SDCard 已使用空间大小 + */ + public static long getUsedBlocks() { + try { + return getUsedBlocks(Environment.getExternalStorageDirectory().getPath()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getUsedBlocks"); + return 0L; + } + } + + /** + * 返回内置 SDCard 空间大小信息 + * @return long[], 0 = 总空间大小, 1 = 空闲空间大小, 2 = 已使用空间大小 + */ + public static long[] getBlockSizeInfos() { + try { + return getBlockSizeInfos(Environment.getExternalStorageDirectory().getPath()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBlockSizeInfos"); + return null; + } + } + + // = + + /** + * 获取对应路径的空间总大小 + * @param path 路径 + * @return 对应路径的空间总大小 + */ + public static long getAllBlockSize(final String path) { + try { + // 获取路径的存储空间信息 + StatFs statFs = new StatFs(path); + // 单个数据块的大小、数据块数量 + long blockSize, blockCount; + // 版本兼容 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + blockSize = statFs.getBlockSizeLong(); + blockCount = statFs.getBlockCountLong(); + } else { + blockSize = statFs.getBlockSize(); + blockCount = statFs.getBlockCount(); + } + // 返回空间总大小 + return (blockCount * blockSize); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAllBlockSize"); + } + return 0L; + } + + /** + * 获取对应路径的空闲空间大小 + * @param path 路径 + * @return 对应路径的空闲空间大小 + */ + public static long getAvailableBlocks(final String path) { + try { + // 获取路径的存储空间信息 + StatFs statFs = new StatFs(path); + // 单个数据块的大小、空闲的数据块数量 + long blockSize, availableBlocks; + // 版本兼容 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + blockSize = statFs.getBlockSizeLong(); + availableBlocks = statFs.getAvailableBlocksLong(); + } else { + blockSize = statFs.getBlockSize(); + availableBlocks = statFs.getAvailableBlocks(); + } + // 返回空闲空间 + return (availableBlocks * blockSize); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAvailableBlocks"); + } + return 0L; + } + + /** + * 获取对应路径已使用空间大小 + * @param path 路径 + * @return 对应路径已使用空间大小 + */ + public static long getUsedBlocks(final String path) { + try { + // 获取路径的存储空间信息 + StatFs statFs = new StatFs(path); + // 单个数据块的大小、数据块数量、空闲的数据块数量 + long blockSize, blockCount, availableBlocks; + // 版本兼容 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + blockSize = statFs.getBlockSizeLong(); + blockCount = statFs.getBlockCountLong(); + availableBlocks = statFs.getAvailableBlocksLong(); + } else { + blockSize = statFs.getBlockSize(); + blockCount = statFs.getBlockCount(); + availableBlocks = statFs.getAvailableBlocks(); + } + // 返回已使用空间大小 + return ((blockCount - availableBlocks) * blockSize); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getUsedBlocks"); + } + return 0L; + } + + /** + * 返回对应路径的空间大小信息 + * @param path 路径 + * @return long[], 0 = 总空间大小, 1 = 空闲空间大小, 2 = 已使用空间大小 + */ + public static long[] getBlockSizeInfos(final String path) { + try { + // 获取路径的存储空间信息 + StatFs statFs = new StatFs(path); + // 单个数据块的大小、数据块数量、空闲的数据块数量 + long blockSize, blockCount, availableBlocks; + // 版本兼容 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + blockSize = statFs.getBlockSizeLong(); + blockCount = statFs.getBlockCountLong(); + availableBlocks = statFs.getAvailableBlocksLong(); + } else { + blockSize = statFs.getBlockSize(); + blockCount = statFs.getBlockCount(); + availableBlocks = statFs.getAvailableBlocks(); + } + // 计算空间信息 + long[] blocks = new long[3]; + blocks[0] = blockSize * blockCount; + blocks[1] = blockSize * availableBlocks; + blocks[2] = ((blockCount - availableBlocks) * blockSize); + // 返回空间信息 + return blocks; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBlockSizeInfos"); + } + return null; + } + + // = + + /** + * 获取 APP Cache 文件夹地址 + * @return APP Cache 文件夹地址 + */ + public static String getDiskCacheDir() { + String cachePath; + if (isSDCardEnable()) { // 判断 SDCard 是否挂载 + cachePath = DevUtils.getContext().getExternalCacheDir().getPath(); + } else { + cachePath = DevUtils.getContext().getCacheDir().getPath(); + } + // 防止不存在目录文件, 自动创建 + createFolder(cachePath); + // 返回文件存储地址 + return cachePath; + } + + /** + * 获取 APP Cache 路径 File + * @param filePath 文件路径 + * @return APP Cache 路径 File + */ + public static File getCacheFile(final String filePath) { + return getFile(getCachePath(filePath)); + } + + /** + * 获取 APP Cache 路径 + * @param filePath 文件路径 + * @return APP Cache 路径 + */ + public static String getCachePath(final String filePath) { + if (filePath == null) return null; + // 获取缓存地址 + String cachePath = new File(getDiskCacheDir(), filePath).getAbsolutePath(); + // 防止不存在目录文件, 自动创建 + createFolder(cachePath); + // 返回缓存地址 + return cachePath; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = FileUtils = + // ============= + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFile(final String filePath) { + return getFileByPath(filePath); + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param dirPath 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + private static boolean createFolder(final String dirPath) { + return createFolder(getFileByPath(dirPath)); + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param file 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + private static boolean createFolder(final File file) { + if (file != null) { + try { + // 当这个文件夹不存在的时候则创建文件夹 + if (!file.exists()) { + // 允许创建多级目录 + return file.mkdirs(); + // 这个无法创建多级目录 + // rootFile.mkdir(); + } + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createFolder"); + } + } + return false; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ScreenUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ScreenUtils.java new file mode 100644 index 0000000000..74e850c033 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ScreenUtils.java @@ -0,0 +1,630 @@ +package dev.utils.app; + +import android.app.Activity; +import android.app.KeyguardManager; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Build; +import android.provider.Settings; +import android.support.annotation.RequiresApi; +import android.support.annotation.RequiresPermission; +import android.util.DisplayMetrics; +import android.view.Surface; +import android.view.Window; +import android.view.WindowManager; + +import java.lang.reflect.Method; +import java.text.DecimalFormat; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 屏幕相关工具类 + * @author Ttt + *
+ *     计算屏幕尺寸
+ *     @see 
+ *     

+ * 所需权限 + * + *
+ */ +public final class ScreenUtils { + + private ScreenUtils() { + } + + // 日志 TAG + private static final String TAG = ScreenUtils.class.getSimpleName(); + + /** + * 获取 WindowManager + * @return {@link WindowManager} + */ + public static WindowManager getWindowManager() { + try { + return (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWindowManager"); + } + return null; + } + + /** + * 获取 DisplayMetrics + * @return {@link DisplayMetrics} + */ + public static DisplayMetrics getDisplayMetrics() { + try { + WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); + if (windowManager != null) { + DisplayMetrics displayMetrics = new DisplayMetrics(); + windowManager.getDefaultDisplay().getMetrics(displayMetrics); + return displayMetrics; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDisplayMetrics"); + } + return null; + } + + // ============ + // = 宽高获取 = + // ============ + + /** + * 获取屏幕宽度 + * @return 屏幕宽度 + */ + public static int getScreenWidth() { + return getScreenWidthHeight()[0]; + } + + /** + * 获取屏幕高度 + * @return 屏幕高度 + */ + public static int getScreenHeight() { + return getScreenWidthHeight()[1]; + } + + // = + + /** + * 获取屏幕宽高 + * @return int[], 0 = 宽度, 1 = 高度 + */ + public static int[] getScreenWidthHeight() { + try { + WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); + if (windowManager == null) { + DisplayMetrics displayMetrics = DevUtils.getContext().getResources().getDisplayMetrics(); + return new int[]{displayMetrics.widthPixels, displayMetrics.heightPixels}; + } + Point point = new Point(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + windowManager.getDefaultDisplay().getRealSize(point); + } else { + windowManager.getDefaultDisplay().getSize(point); + } + return new int[]{point.x, point.y}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getScreenWidthHeight"); + } + return new int[]{0, 0}; + } + + /** + * 获取屏幕宽高 + * @return {@link Point}, point.x 宽, point.y 高 + */ + public static Point getScreenWidthHeightToPoint() { + try { + WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); + if (windowManager == null) { + DisplayMetrics displayMetrics = DevUtils.getContext().getResources().getDisplayMetrics(); + return new Point(displayMetrics.widthPixels, displayMetrics.heightPixels); + } + Point point = new Point(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + windowManager.getDefaultDisplay().getRealSize(point); + } else { + windowManager.getDefaultDisplay().getSize(point); + } + return point; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getScreenWidthHeightToPoint"); + } + return null; + } + + // = + + /** + * 获取屏幕分辨率 + * @return 屏幕分辨率 + */ + public static String getScreenSize() { + return getScreenSize("x"); + } + + /** + * 获取屏幕分辨率 + * @param symbol 拼接符号 + * @return 屏幕分辨率 + */ + public static String getScreenSize(final String symbol) { + try { + // 获取分辨率 + int[] widthHeight = getScreenWidthHeight(); + // 返回分辨率信息 + return widthHeight[1] + symbol + widthHeight[0]; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getScreenSize"); + } + return "unknown"; + } + + /** + * 获取屏幕英寸 - 例 5.5 英寸 + * @return 屏幕英寸 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) + public static String getScreenSizeOfDevice() { + try { + Point point = new Point(); + DisplayMetrics displayMetrics = new DisplayMetrics(); + WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); + windowManager.getDefaultDisplay().getRealSize(point); + windowManager.getDefaultDisplay().getMetrics(displayMetrics); + // 计算尺寸 + double x = Math.pow(point.x / displayMetrics.xdpi, 2); + double y = Math.pow(point.y / displayMetrics.ydpi, 2); + double screenInches = Math.sqrt(x + y); + // 转换大小 + return new DecimalFormat("#.0").format(screenInches); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getScreenSizeOfDevice"); + } + return "unknown"; + } + + // = + + /** + * 获取屏幕密度 + * @return 屏幕密度 + */ + public static float getDensity() { + try { + DisplayMetrics displayMetrics = getDisplayMetrics(); + if (displayMetrics != null) { + // 屏幕密度, 如 (0.75 / 1.0 / 1.5 / 2.0) + return displayMetrics.density; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDensity"); + } + return 0; + } + + /** + * 获取屏幕密度 dpi + * @return 屏幕密度 dpi + */ + public static int getDensityDpi() { + try { + DisplayMetrics displayMetrics = getDisplayMetrics(); + if (displayMetrics != null) { + // 屏幕密度 DPI, 如 (120 / 160 / 240 / 320) + return displayMetrics.densityDpi; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDensityDpi"); + } + return 0; + } + + /** + * 获取屏幕缩放密度 + * @return 屏幕缩放密度 + */ + public static float getScaledDensity() { + try { + DisplayMetrics displayMetrics = getDisplayMetrics(); + if (displayMetrics != null) { + return displayMetrics.scaledDensity; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getScaledDensity"); + } + return 0f; + } + + /** + * 获取 X 轴 dpi + * @return X 轴 dpi + */ + public static float getXDpi() { + try { + DisplayMetrics displayMetrics = getDisplayMetrics(); + if (displayMetrics != null) { + return displayMetrics.xdpi; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getXDpi"); + } + return 0f; + } + + /** + * 获取 Y 轴 dpi + * @return Y 轴 dpi + */ + public static float getYDpi() { + try { + DisplayMetrics displayMetrics = getDisplayMetrics(); + if (displayMetrics != null) { + return displayMetrics.ydpi; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getYDpi"); + } + return 0f; + } + + /** + * 获取宽度比例 dpi 基准 + * @return 宽度比例 dpi 基准 + */ + public static float getWidthDpi() { + try { + DisplayMetrics displayMetrics = getDisplayMetrics(); + if (displayMetrics != null) { + return displayMetrics.widthPixels / displayMetrics.density; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWidthDpi"); + } + return 0f; + } + + /** + * 获取高度比例 dpi 基准 + * @return 高度比例 dpi 基准 + */ + public static float getHeightDpi() { + try { + DisplayMetrics displayMetrics = getDisplayMetrics(); + if (displayMetrics != null) { + return displayMetrics.heightPixels / displayMetrics.density; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getHeightDpi"); + } + return 0f; + } + + /** + * 获取屏幕信息 + * @return 屏幕信息 + */ + public static String getScreenInfo() { + StringBuilder builder = new StringBuilder(); + DisplayMetrics displayMetrics = getDisplayMetrics(); + if (displayMetrics != null) { + try { + int heightPixels = displayMetrics.heightPixels; + int widthPixels = displayMetrics.widthPixels; + + float xdpi = displayMetrics.xdpi; + float ydpi = displayMetrics.ydpi; + int densityDpi = displayMetrics.densityDpi; + + float density = displayMetrics.density; + float scaledDensity = displayMetrics.scaledDensity; + + float heightDpi = heightPixels / density; + float widthDpi = widthPixels / density; + // = + builder.append("heightPixels: " + heightPixels + "px"); + builder.append("\nwidthPixels: " + widthPixels + "px"); + + builder.append("\nxdpi: " + xdpi + "dpi"); + builder.append("\nydpi: " + ydpi + "dpi"); + builder.append("\ndensityDpi: " + densityDpi + "dpi"); + + builder.append("\ndensity: " + density); + builder.append("\nscaledDensity: " + scaledDensity); + + builder.append("\nheightDpi: " + heightDpi + "dpi"); + builder.append("\nwidthDpi: " + widthDpi + "dpi"); + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getScreenInfo"); + } + } + return builder.toString(); + } + + // = + + /** + * 设置禁止截屏 + * @param activity {@link Activity} + */ + public static void setWindowSecure(final Activity activity) { + try { + // 禁止截屏 + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setWindowSecure"); + } + } + + /** + * 设置屏幕为全屏 + * @param activity {@link Activity} + */ + public static void setFullScreen(final Activity activity) { + try { + // 隐藏标题 + activity.requestWindowFeature(Window.FEATURE_NO_TITLE); + // 设置全屏 + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setFullScreen"); + } + } + + /** + * 设置屏幕为横屏 + *
+     *     还有一种就是在 Activity 中加属性 android:screenOrientation="landscape"
+     *     不设置 Activity 的 android:configChanges 时
+     *     切屏会重新调用各个生命周期, 切横屏时会执行一次, 切竖屏时会执行两次
+     *     设置 Activity 的 android:configChanges="orientation" 时
+     *     切屏还是会重新调用各个生命周期, 切横、竖屏时只会执行一次
+     *     设置 Activity 的 android:configChanges="orientation|keyboardHidden|screenSize"
+     *     4.0 以上必须带最后一个参数时
+     *     切屏不会重新调用各个生命周期, 只会执行 onConfigurationChanged 方法
+     * 
+ * @param activity {@link Activity} + */ + public static void setLandscape(final Activity activity) { + try { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setLandscape"); + } + } + + /** + * 设置屏幕为竖屏 + * @param activity {@link Activity} + */ + public static void setPortrait(final Activity activity) { + try { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPortrait"); + } + } + + /** + * 判断是否横屏 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLandscape() { + try { + return DevUtils.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isLandscape"); + } + return false; + } + + /** + * 判断是否竖屏 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPortrait() { + try { + return DevUtils.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isPortrait"); + } + return false; + } + + /** + * 切换屏幕方向 + * @param activity {@link Activity} + * @return {@code true} 横屏, {@code false} 竖屏 + */ + public static boolean toggleScreenOrientation(final Activity activity) { + try { + // 判断是否竖屏 + if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + return true; // 切换横屏, 并且表示属于横屏 + } else { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + return false; // 切换竖屏, 并且表示属于竖屏 + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "toggleScreenOrientation"); + } + return false; + } + + // = + + /** + * 获取屏幕旋转角度 + * @param activity {@link Activity} + * @return 屏幕旋转角度 + */ + public static int getScreenRotation(final Activity activity) { + try { + switch (activity.getWindowManager().getDefaultDisplay().getRotation()) { + case Surface.ROTATION_0: + return 0; + case Surface.ROTATION_90: + return 90; + case Surface.ROTATION_180: + return 180; + case Surface.ROTATION_270: + return 270; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getScreenRotation"); + } + return 0; + } + + /** + * 判断是否锁屏 + * @return {@code true} yes, {@code false} no + */ + public static boolean isScreenLock() { + try { + KeyguardManager keyguardManager = (KeyguardManager) DevUtils.getContext().getSystemService(Context.KEYGUARD_SERVICE); + return keyguardManager != null && keyguardManager.inKeyguardRestrictedInputMode(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isScreenLock"); + } + return false; + } + + /** + * 判断是否是平板 + * @return {@code true} yes, {@code false} no + */ + public static boolean isTablet() { + try { + return (DevUtils.getContext().getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isTablet"); + } + return false; + } + + // = + + /** + * 获取状态栏的高度 ( 无关 android:theme 获取状态栏高度 ) + * @return 状态栏的高度 + */ + public static int getStatusHeight() { + try { + int id = DevUtils.getContext().getResources().getIdentifier("status_bar_height", "dimen", "android"); + return DevUtils.getContext().getResources().getDimensionPixelSize(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getStatusHeight"); + } + return 0; + } + + /** + * 获取应用区域 TitleBar 高度 ( 顶部灰色 TitleBar 高度, 没有设置 android:theme 的 NoTitleBar 时会显示 ) + * @param activity {@link Activity} + * @return 应用区域 TitleBar 高度 + */ + public static int getStatusBarHeight(final Activity activity) { + try { + Rect rect = new Rect(); + activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); + return rect.top; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getStatusBarHeight"); + } + return 0; + } + + /** + * 设置进入休眠时长 + * @param duration 时长 + */ + @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) + public static void setSleepDuration(final int duration) { + try { + Settings.System.putInt(DevUtils.getContext().getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT, duration); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setSleepDuration"); + } + } + + /** + * 获取进入休眠时长 + * @return 进入休眠时长 + */ + public static int getSleepDuration() { + try { + return Settings.System.getInt(DevUtils.getContext().getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSleepDuration"); + return -1; + } + } + + // = + + /** + * 获取底部导航栏高度 + * @return 底部导航栏高度 + */ + public static int getNavigationBarHeight() { + try { + Resources resources = DevUtils.getContext().getResources(); + // 获取对应方向字符串 + String orientation = resources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape"; + // 获取对应的 id + int resourceId = resources.getIdentifier(orientation, "dimen", "android"); + if (resourceId > 0 && checkDeviceHasNavigationBar()) { + return resources.getDimensionPixelSize(resourceId); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getNavigationBarHeight"); + } + return 0; + } + + /** + * 检测是否具有底部导航栏 + *
+     *     一加手机上判断不准确
+     * 
+ * @return {@code true} yes, {@code false} no + */ + public static boolean checkDeviceHasNavigationBar() { + boolean hasNavigationBar = false; + try { + Resources resources = DevUtils.getContext().getResources(); + int id = resources.getIdentifier("config_showNavigationBar", "bool", "android"); + if (id > 0) { + hasNavigationBar = resources.getBoolean(id); + } + try { + Class systemPropertiesClass = Class.forName("android.os.SystemProperties"); + Method method = systemPropertiesClass.getMethod("get", String.class); + String navBarOverride = (String) method.invoke(systemPropertiesClass, "qemu.hw.mainkeys"); + if ("1".equals(navBarOverride)) { + hasNavigationBar = false; + } else if ("0".equals(navBarOverride)) { + hasNavigationBar = true; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "checkDeviceHasNavigationBar - SystemProperties"); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "checkDeviceHasNavigationBar"); + } + return hasNavigationBar; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/ServiceUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ServiceUtils.java similarity index 53% rename from DevLibUtils/src/main/java/dev/utils/app/ServiceUtils.java rename to lib/DevApp/src/main/java/dev/utils/app/ServiceUtils.java index e4deef4cdc..763902e439 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ServiceUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/app/ServiceUtils.java @@ -16,33 +16,29 @@ /** * detail: 服务相关工具类 - * Created by Blankj - * http://blankj.com - * Update to Ttt + * @author Ttt */ public final class ServiceUtils { private ServiceUtils() { } - // 日志TAG + // 日志 TAG private static final String TAG = ServiceUtils.class.getSimpleName(); + // ==================== + // = 判断服务是否运行 = + // ==================== + /** * 判断服务是否运行 - * @param className 完整包名的服务类名 - * @return true : 是, false : 否 + * @param clazz {@link Class} + * @return {@code true} yes, {@code false} no */ - public static boolean isServiceRunning(final String className) { + public static boolean isServiceRunning(final Class clazz) { try { - ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); - if (activityManager == null) return false; - List listInfos = activityManager.getRunningServices(0x7FFFFFFF); - if (listInfos == null || listInfos.size() == 0) return false; - for (RunningServiceInfo rInfo : listInfos) { - if (className.equals(rInfo.service.getClassName())) return true; - } - } catch (Exception e){ + return isServiceRunning(clazz.getName()); + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isServiceRunning"); } return false; @@ -50,13 +46,26 @@ public static boolean isServiceRunning(final String className) { /** * 判断服务是否运行 - * @param cls The service class. - * @return true : 是, false : 否 + * @param className package.ServiceClassName - class.getName() + * @return {@code true} yes, {@code false} no */ - public static boolean isServiceRunning(final Class cls) { - return isServiceRunning(cls.getName()); + public static boolean isServiceRunning(final String className) { + try { + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + if (activityManager == null) return false; + List lists = activityManager.getRunningServices(Integer.MAX_VALUE); + if (lists == null || lists.size() == 0) return false; + for (RunningServiceInfo info : lists) { + if (className.equals(info.service.getClassName())) return true; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isServiceRunning"); + } + return false; } + // = + /** * 获取所有运行的服务 * @return 服务名集合 @@ -65,22 +74,26 @@ public static Set getAllRunningService() { try { ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); if (activityManager == null) return Collections.emptySet(); - List listInfos = activityManager.getRunningServices(0x7FFFFFFF); - if (listInfos == null || listInfos.size() == 0) return null; + List lists = activityManager.getRunningServices(Integer.MAX_VALUE); + if (lists == null || lists.size() == 0) return null; Set names = new HashSet<>(); - for (RunningServiceInfo rInfo : listInfos) { - names.add(rInfo.service.getClassName()); + for (RunningServiceInfo info : lists) { + names.add(info.service.getClassName()); } return names; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getAllRunningService"); } return Collections.emptySet(); } + // ============ + // = 启动服务 = + // ============ + /** * 启动服务 - * @param className 完整包名的服务类名 + * @param className package.ServiceClassName - class.getName() */ public static void startService(final String className) { try { @@ -92,21 +105,25 @@ public static void startService(final String className) { /** * 启动服务 - * @param cls 服务类 + * @param clazz {@link Class} */ - public static void startService(final Class cls) { + public static void startService(final Class clazz) { try { - Intent intent = new Intent(DevUtils.getContext(), cls); + Intent intent = new Intent(DevUtils.getContext(), clazz); DevUtils.getContext().startService(intent); } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "startService"); } } + // ============ + // = 停止服务 = + // ============ + /** * 停止服务 - * @param className 完整包名的服务类名 - * @return true : 停止成功, false : 停止失败 + * @param className package.ServiceClassName - class.getName() + * @return {@code true} success, {@code false} fail */ public static boolean stopService(final String className) { try { @@ -119,31 +136,34 @@ public static boolean stopService(final String className) { /** * 停止服务 - * @param cls 服务类 - * @return true : 停止成功, false : 停止失败 + * @param clazz {@link Class} + * @return {@code true} success, {@code false} fail */ - public static boolean stopService(final Class cls) { + public static boolean stopService(final Class clazz) { try { - Intent intent = new Intent(DevUtils.getContext(), cls); + Intent intent = new Intent(DevUtils.getContext(), clazz); return DevUtils.getContext().stopService(intent); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "stopService"); return false; } } + // ============ + // = 绑定服务 = + // ============ + /** * 绑定服务 - * @param className 完整包名的服务类名 - * @param conn 服务连接对象 - * @param flags 绑定选项 - * ==== - * {@link Context#BIND_AUTO_CREATE} - * {@link Context#BIND_DEBUG_UNBIND} - * {@link Context#BIND_NOT_FOREGROUND} - * {@link Context#BIND_ABOVE_CLIENT} - * {@link Context#BIND_ALLOW_OOM_MANAGEMENT} - * {@link Context#BIND_WAIVE_PRIORITY} + * @param className package.ServiceClassName - class.getName() + * @param conn {@link ServiceConnection} + * @param flags 绑定选项 + * {@link Context#BIND_AUTO_CREATE} + * {@link Context#BIND_DEBUG_UNBIND} + * {@link Context#BIND_NOT_FOREGROUND} + * {@link Context#BIND_ABOVE_CLIENT} + * {@link Context#BIND_ALLOW_OOM_MANAGEMENT} + * {@link Context#BIND_WAIVE_PRIORITY} */ public static void bindService(final String className, final ServiceConnection conn, final int flags) { try { @@ -155,35 +175,38 @@ public static void bindService(final String className, final ServiceConnection c /** * 绑定服务 - * @param cls 服务类 - * @param conn 服务连接对象 + * @param clazz {@link Class} + * @param conn {@link ServiceConnection} * @param flags 绑定选项 - * ==== - * {@link Context#BIND_AUTO_CREATE} - * {@link Context#BIND_DEBUG_UNBIND} - * {@link Context#BIND_NOT_FOREGROUND} - * {@link Context#BIND_ABOVE_CLIENT} - * {@link Context#BIND_ALLOW_OOM_MANAGEMENT} - * {@link Context#BIND_WAIVE_PRIORITY} + * {@link Context#BIND_AUTO_CREATE} + * {@link Context#BIND_DEBUG_UNBIND} + * {@link Context#BIND_NOT_FOREGROUND} + * {@link Context#BIND_ABOVE_CLIENT} + * {@link Context#BIND_ALLOW_OOM_MANAGEMENT} + * {@link Context#BIND_WAIVE_PRIORITY} */ - public static void bindService(final Class cls, final ServiceConnection conn, final int flags) { + public static void bindService(final Class clazz, final ServiceConnection conn, final int flags) { try { - Intent intent = new Intent(DevUtils.getContext(), cls); + Intent intent = new Intent(DevUtils.getContext(), clazz); DevUtils.getContext().bindService(intent, conn, flags); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "bindService"); } } + // ============ + // = 解绑服务 = + // ============ + /** * 解绑服务 - * @param conn 服务连接对象 + * @param conn {@link ServiceConnection} */ public static void unbindService(final ServiceConnection conn) { try { DevUtils.getContext().unbindService(conn); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "unbindService"); } } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ShapeUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ShapeUtils.java new file mode 100644 index 0000000000..398328e838 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ShapeUtils.java @@ -0,0 +1,525 @@ +package dev.utils.app; + +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.Rect; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.ColorRes; +import android.support.annotation.RequiresApi; +import android.support.v4.content.ContextCompat; +import android.text.TextUtils; +import android.view.View; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Shape 工具类 + * @author Ttt + *
+ *     @see 
+ *     @see 
+ * 
+ */ +public final class ShapeUtils { + + // 日志 TAG + private static final String TAG = ShapeUtils.class.getSimpleName(); + // Shape Drawable + private final GradientDrawable mDrawable; + + /** + * 构造函数 + * @param builder {@link Builder} + */ + private ShapeUtils(final Builder builder) { + mDrawable = builder.gradientDrawable; + } + + /** + * 获取 GradientDrawable + * @return {@link GradientDrawable} + */ + public GradientDrawable getDrawable() { + return mDrawable; + } + + /** + * 设置 Drawable 背景 + * @param view {@link View} + */ + public void setDrawable(final View view) { + if (view != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) + view.setBackground(mDrawable); + else + view.setBackgroundDrawable(mDrawable); + } + } + + // = + + /** + * detail: 构造者模式 + * @author Ttt + */ + public static final class Builder { + + // Shape Drawable + private GradientDrawable gradientDrawable = new GradientDrawable(); + + public Builder() { + } + + /** + * 构造函数 + * @param drawable {@link GradientDrawable} + */ + public Builder(final GradientDrawable drawable) { + if (drawable != null) { + this.gradientDrawable = drawable; + } + } + + /** + * build ShapeUtils 对象 + * @return {@link ShapeUtils} + */ + public ShapeUtils build() { + return new ShapeUtils(this); + } + + // ============ + // = 设置圆角 = + // ============ + + /** + * 设置圆角 + * @param radius 圆角大小 + * @return {@link ShapeUtils.Builder} + */ + public Builder setRadius(final float radius) { + if (gradientDrawable != null) { + gradientDrawable.setCornerRadius(radius); + } + return this; + } + + // ============ + // = 设置左边 = + // ============ + + /** + * 设置圆角 + * @param left leftTop、leftBottom 圆角大小 + * @return {@link ShapeUtils.Builder} + */ + public Builder setRadiusLeft(final float left) { + setCornerRadii(left, 0, 0, left); + return this; + } + + /** + * 设置圆角 + * @param leftTop 左上圆角大小 + * @param leftBottom 左下圆角大小 + * @return {@link ShapeUtils.Builder} + */ + public Builder setRadiusLeft(final float leftTop, final float leftBottom) { + setCornerRadii(leftTop, 0, 0, leftBottom); + return this; + } + + // ============ + // = 设置右边 = + // ============ + + /** + * 设置圆角 + * @param right rightTop、rightBottom 圆角大小 + * @return {@link ShapeUtils.Builder} + */ + public Builder setRadiusRight(final float right) { + setCornerRadii(0, right, right, 0); + return this; + } + + /** + * 设置圆角 + * @param rightTop 右上圆角大小 + * @param rightBottom 右下圆角大小 + * @return {@link ShapeUtils.Builder} + */ + public Builder setRadiusRight(final float rightTop, final float rightBottom) { + setCornerRadii(0, rightTop, rightBottom, 0); + return this; + } + + // ================ + // = 圆角内部处理 = + // ================ + + /** + * 设置圆角 ( 统一处理方法 ) + *
+         *     
+         * 
+ * @param leftTop 左上圆角大小 + * @param rightTop 左下圆角大小 + * @param rightBottom 右上圆角大小 + * @param leftBottom 右下圆角大小 + * @return {@link ShapeUtils.Builder} + */ + public Builder setCornerRadii(final float leftTop, final float rightTop, final float rightBottom, final float leftBottom) { + if (gradientDrawable != null) { + // radii 数组分别指定四个圆角的半径, 每个角可以指定 [X_Radius, Y_Radius] + // 四个圆角的顺序为左上、右上、右下、左下, 如果 X_Radius, Y_Radius 为 0 表示还是直角 + gradientDrawable.setCornerRadii(new float[]{leftTop, leftTop, rightTop, rightTop, rightBottom, rightBottom, leftBottom, leftBottom}); + } + return this; + } + + // ======================= + // = 设置背景色 ( 填充 ) = + // ======================= + + /** + * 设置背景色 ( 填充铺满 ) + *
+         *     
+         * 
+ * @param color argb/rgb color + * @return {@link ShapeUtils.Builder} + */ + public Builder setColor(final String color) { + if (gradientDrawable != null && !TextUtils.isEmpty(color)) { + try { + gradientDrawable.setColor(Color.parseColor(color)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setColor"); + } + } + return this; + } + + /** + * 设置背景色 ( 填充铺满 ) + * @param color R.color.id + * @return {@link ShapeUtils.Builder} + */ + public Builder setColor(@ColorRes final int color) { + if (gradientDrawable != null) { + try { + gradientDrawable.setColor(ContextCompat.getColor(DevUtils.getContext(), color)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setColor"); + } + } + return this; + } + + // ================ + // = 设置边框颜色 = + // ================ + + /** + * 设置边框颜色 + *
+         *     描边
+         *     
+         * 
+ * @param width 描边的宽度 + * @param color 描边的颜色 + * @return {@link ShapeUtils.Builder} + */ + public Builder setStroke(final int width, final String color) { + if (gradientDrawable != null) { + try { + gradientDrawable.setStroke(width, Color.parseColor(color)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setStroke"); + } + } + return this; + } + + /** + * 设置边框颜色 + * @param width 描边的宽度 + * @param color 描边的颜色 + * @return {@link ShapeUtils.Builder} + */ + public Builder setStroke(final int width, @ColorRes final int color) { + if (gradientDrawable != null) { + try { + gradientDrawable.setStroke(width, ContextCompat.getColor(DevUtils.getContext(), color)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setStroke"); + } + } + return this; + } + + /** + * 设置边框颜色 + * @param width 描边的宽度 + * @param color 描边的颜色 + * @return {@link ShapeUtils.Builder} + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public Builder setStroke(final int width, final ColorStateList color) { + if (gradientDrawable != null && color != null) { + try { + gradientDrawable.setStroke(width, color); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setStroke"); + } + } + return this; + } + + // ============ + // = 设置大小 = + // ============ + + /** + * 设置大小 + *
+         *     
+         * 
+ * @param width 宽度 + * @param height 高度 + * @return {@link ShapeUtils.Builder} + */ + public Builder setSize(final int width, final int height) { + if (gradientDrawable != null) { + try { + gradientDrawable.setSize(width, height); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setSize"); + } + } + return this; + } + + // ============ + // = 设置边距 = + // ============ + + /** + * 设置边距 + * @param padding Padding + * @return {@link ShapeUtils.Builder} + */ + public Builder setPadding(final int padding) { + if (gradientDrawable != null) { + try { + Rect rect = new Rect(); + rect.left = padding; + rect.top = padding; + rect.right = padding; + rect.bottom = padding; + gradientDrawable.getPadding(rect); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPadding"); + } + } + return this; + } + + // ============ + // = 设置渐变 = + // ============ + + /** + * 获取渐变角度 + * @param angle 角度 + * @return {@link GradientDrawable.Orientation} + */ + public GradientDrawable.Orientation getOrientation(final int angle) { + switch (angle) { + case 0: + return GradientDrawable.Orientation.LEFT_RIGHT; + case 45: + return GradientDrawable.Orientation.BL_TR; + case 90: + return GradientDrawable.Orientation.BOTTOM_TOP; + case 135: + return GradientDrawable.Orientation.BR_TL; + case 180: + return GradientDrawable.Orientation.RIGHT_LEFT; + case 225: + return GradientDrawable.Orientation.TR_BL; + case 270: + return GradientDrawable.Orientation.TOP_BOTTOM; + case 315: + return GradientDrawable.Orientation.TL_BR; + } + return null; + } + + /** + * 设置渐变颜色 + *
+         *     渐变, 这个设置之后一般就不设置 solid 填充色了
+         *     
+         *     

+ * + *
+ * @param colors 渐变颜色 + * @return {@link ShapeUtils.Builder} + */ + public Builder(@ColorInt final int[] colors) { + this(new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors)); + } + + /** + * 设置渐变颜色 + * @param orientation 渐变角度 + * @param colors 渐变颜色 + * @return {@link ShapeUtils.Builder} + */ + public Builder(final GradientDrawable.Orientation orientation, @ColorInt final int[] colors) { + this(new GradientDrawable(orientation, colors)); + } + } + + // ============ + // = 快捷方法 = + // ============ + + /** + * 创建新的 Shape Builder 对象 + * @param radius 圆角大小 + * @param color 背景色 + * @return {@link ShapeUtils.Builder} + */ + public static Builder newBuilder(final float radius, @ColorRes final int color) { + return new Builder().setRadius(radius).setColor(color); + } + + /** + * 创建新的 Shape Builder 对象 + * @param radius 圆角大小 + * @param color 背景色 + * @return {@link ShapeUtils.Builder} + */ + public static Builder newBuilder(final float radius, final String color) { + return new Builder().setRadius(radius).setColor(color); + } + + // = + + /** + * 创建新的 Shape Builder 对象 + *
+     *     leftTop、leftBottom 圆角大小 + 背景色
+     * 
+ * @param left leftTop、leftBottom 圆角大小 + * @param color 背景色 + * @return {@link ShapeUtils.Builder} + */ + public static Builder newBuilderToLeft(final float left, final String color) { + return new Builder().setRadiusLeft(left).setColor(color); + } + + /** + * 创建新的 Shape Builder 对象 + *
+     *     leftTop、leftBottom 圆角大小 + 背景色
+     * 
+ * @param left leftTop、leftBottom 圆角大小 + * @param color 背景色 + * @return {@link ShapeUtils.Builder} + */ + public static Builder newBuilderToLeft(final float left, @ColorRes final int color) { + return new Builder().setRadiusLeft(left).setColor(color); + } + + // = + + /** + * 创建新的 Shape Builder 对象 + *
+     *     rightTop、rightBottom 圆角大小 + 背景色
+     * 
+ * @param right rightTop、rightBottom 圆角大小 + * @param color 背景色 + * @return {@link ShapeUtils.Builder} + */ + public static Builder newBuilderToRight(final float right, final String color) { + return new Builder().setRadiusRight(right).setColor(color); + } + + /** + * 创建新的 Shape Builder 对象 + *
+     *     rightTop、rightBottom 圆角大小 + 背景色
+     * 
+ * @param right rightTop、rightBottom 圆角大小 + * @param color 背景色 + * @return {@link ShapeUtils.Builder} + */ + public static Builder newBuilderToRight(final float right, @ColorRes final int color) { + return new Builder().setRadiusRight(right).setColor(color); + } + + // = + + /** + * 创建渐变的 Shape Builder 对象 + * @param colors 渐变颜色 + * @return {@link ShapeUtils.Builder} + */ + public static Builder newBuilderToGradient(@ColorInt final int[] colors) { + return new Builder(colors); + } + + /** + * 创建渐变的 Shape Builder 对象 + * @param orientation 渐变角度 + * @param colors 渐变颜色 + * @return {@link ShapeUtils.Builder} + */ + public static Builder newBuilderToGradient(final GradientDrawable.Orientation orientation, @ColorInt final int[] colors) { + return new Builder(orientation, colors); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ShellUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ShellUtils.java new file mode 100644 index 0000000000..27e96d9614 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ShellUtils.java @@ -0,0 +1,225 @@ +package dev.utils.app; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.util.List; + +import dev.utils.LogPrintUtils; + +/** + * detail: Shell 命令工具类 + * @author Ttt + */ +public final class ShellUtils { + + private ShellUtils() { + } + + // 日志 TAG + private static final String TAG = ShellUtils.class.getSimpleName(); + // 操作成功状态码 + private static final int SUCCESS = 0; + // 换行符 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + + /** + * 执行 shell 命令 + * @param command 待执行命令 + * @param isRoot 是否以 root 权限执行 + * @return {@link CommandResult} + */ + public static CommandResult execCmd(final String command, final boolean isRoot) { + return execCmd(new String[]{command}, isRoot, true); + } + + /** + * 执行 shell 命令 + * @param commands 多条待执行命令 + * @param isRoot 是否以 root 权限执行 + * @return {@link CommandResult} + */ + public static CommandResult execCmd(final List commands, final boolean isRoot) { + return execCmd(commands == null ? null : commands.toArray(new String[]{}), isRoot, true); + } + + /** + * 执行 shell 命令 + * @param commands 多条待执行命令 + * @param isRoot 是否以 root 权限执行 + * @return {@link CommandResult} + */ + public static CommandResult execCmd(final String[] commands, final boolean isRoot) { + return execCmd(commands, isRoot, true); + } + + /** + * 执行 shell 命令 + * @param command 待执行命令 + * @param isRoot 是否以 root 权限执行 + * @param isNeedResultMsg 是否需要返回结果消息 (error、success message) + * @return {@link CommandResult} + */ + public static CommandResult execCmd(final String command, final boolean isRoot, final boolean isNeedResultMsg) { + return execCmd(new String[]{command}, isRoot, isNeedResultMsg); + } + + /** + * 执行 shell 命令 + * @param commands 多条待执行命令 + * @param isRoot 是否以 root 权限执行 + * @param isNeedResultMsg 是否需要结果消息 (error、success message) + * @return {@link CommandResult} + */ + public static CommandResult execCmd(final List commands, final boolean isRoot, final boolean isNeedResultMsg) { + return execCmd(commands == null ? null : commands.toArray(new String[]{}), isRoot, isNeedResultMsg); + } + + /** + * 执行 shell 命令 + * @param commands 多条待执行命令 + * @param isRoot 是否以 root 权限执行 + * @param isNeedResultMsg 是否需要结果消息 (error、success message) + * @return {@link CommandResult} + */ + public static CommandResult execCmd(final String[] commands, final boolean isRoot, final boolean isNeedResultMsg) { + int result = -1; + if (commands == null || commands.length == 0) { + return new CommandResult(result, null, null); + } + Process process = null; + DataOutputStream dos = null; + String successMsg = null; + String errorMsg = null; + try { + process = Runtime.getRuntime().exec(isRoot ? "su" : "sh"); + dos = new DataOutputStream(process.getOutputStream()); + // 循环写入待执行命令 + for (String command : commands) { + if (command == null) continue; + dos.write(command.getBytes()); + dos.writeBytes(NEW_LINE_STR); + dos.flush(); + } + dos.writeBytes("exit" + NEW_LINE_STR); + dos.flush(); + // 为了避免 Process.waitFor() 导致主线程堵塞问题, 最好读取信息 + if (isNeedResultMsg) { // 如果程序不断在向输出流和错误流写数据, 而 JVM 不读取的话, 当缓冲区满之后将无法继续写入数据, 最终造成阻塞在 waitFor() 这里 + // 读取成功数据 + successMsg = consumeInputStream(new InputStreamReader(process.getInputStream(), "UTF-8")); + // 读取异常数据 + errorMsg = consumeInputStream(new InputStreamReader(process.getErrorStream(), "UTF-8")); + } + // 执行结果状态码 + result = process.waitFor(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "execCmd"); + } finally { + try { + dos.close(); + } catch (Exception e) { + } + if (process != null) { + process.destroy(); + } + } + return new CommandResult(result, successMsg, errorMsg); + } + + /** + * 消费 InputStream 并且返回字符串 + * @param reader {@link InputStreamReader} + * @return 读取流数据 + */ + private static String consumeInputStream(final InputStreamReader reader) { + BufferedReader br = null; + try { + StringBuilder builder = new StringBuilder(); + br = new BufferedReader(reader); + String str; + if ((str = br.readLine()) != null) { + builder.append(str); + while ((str = br.readLine()) != null) { + builder.append(NEW_LINE_STR).append(str); + } + } + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "consumeInputStream"); + } finally { + if (br != null) { + try { + br.close(); + } catch (Exception e) { + } + } + } + return null; + } + + // ============== + // = 对外实体类 = + // ============== + + /** + * detail: 命令执行结果实体类 + * @author Ttt + */ + public final static class CommandResult { + + // 执行结果状态码 + public int result; + // 成功信息 + public String successMsg; + // 错误信息 + public String errorMsg; + + /** + * 构造函数 + * @param result 执行结果状态码 + * @param successMsg 成功信息 + * @param errorMsg 错误信息 + */ + public CommandResult(final int result, final String successMsg, final String errorMsg) { + this.result = result; + this.successMsg = successMsg; + this.errorMsg = errorMsg; + } + + /** + * 判断是否执行成功 + * @return {@code true} yes, {@code false} no + */ + public boolean isSuccess() { + return result == SUCCESS; + } + + /** + * 判断是否执行成功 ( 判断 errorMsg) + * @return {@code true} yes, {@code false} no + */ + public boolean isSuccess2() { + return result == SUCCESS && (errorMsg == null || errorMsg.length() == 0); + } + + /** + * 判断是否执行成功 ( 判断 successMsg) + * @return {@code true} yes, {@code false} no + */ + public boolean isSuccess3() { + return result == SUCCESS && successMsg != null && successMsg.length() != 0; + } + + /** + * 判断是否执行成功 ( 判断 successMsg), 并且 successMsg 是否包含某个字符串 + * @param contains 待校验包含字符串 + * @return {@code true} yes, {@code false} no + */ + public boolean isSuccess4(final String contains) { + if (result == SUCCESS && successMsg != null && successMsg.length() != 0) { + return contains != null && contains.length() != 0 && successMsg.toLowerCase().contains(contains); + } + return false; + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ShortCutUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ShortCutUtils.java new file mode 100644 index 0000000000..a21e73d3af --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ShortCutUtils.java @@ -0,0 +1,335 @@ +package dev.utils.app; + +import android.app.PendingIntent; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.Intent.ShortcutIconResource; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.database.Cursor; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.Build; +import android.support.annotation.AnyRes; + +import java.util.List; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 快捷方式工具类 + * @author Ttt + *
+ *     所需权限
+ *     
+ *     
+ *     
+ *     
+ *     
+ *     

+ * READ_SETTINGS 用于判断是否存在快捷图标 + *

+ * @see
+ * @see + *
+ */ +public final class ShortCutUtils { + + private ShortCutUtils() { + } + + // 日志 TAG + private static final String TAG = ShortCutUtils.class.getSimpleName(); + + // ======================== + // = 检测是否存在快捷方式 = + // ======================== + + /** + * 检测是否存在桌面快捷方式 + * @param name 快捷方式名称 + * @return {@code true} yes, {@code false} no + */ + public static boolean hasShortcut(final String name) { + Cursor cursor = null; + try { + Context context = DevUtils.getContext(); + Uri uri = Uri.parse("content://" + getAuthority(context) + "/favorites?notify=true"); + ContentResolver resolver = context.getContentResolver(); + cursor = resolver.query(uri, new String[]{"title", "iconResource"}, "title=?", new String[]{name}, null); + // 判断是否存在快捷方式 + if (cursor != null && cursor.getCount() > 0) { + return true; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "hasShortcut"); + } finally { + if (cursor != null) { + try { + cursor.close(); + } catch (Exception e) { + } + } + } + return false; + } + + // ==================== + // = 创建桌面快捷方式 = + // ==================== + + /** + * 获取桌面快捷方式点击 Intent + * @param clazz 快捷方式点击 Intent className(class.getName()) + * @return {@link Intent} + */ + public static Intent getShortCutIntent(final Class clazz) { + if (clazz != null) { + return getShortCutIntent(clazz.getName()); + } + return null; + } + + /** + * 获取桌面快捷方式点击 Intent + * @param className 快捷方式点击 Intent className(class.getName()) + * @return {@link Intent} + */ + public static Intent getShortCutIntent(final String className) { + if (className != null) { + try { + // 快捷方式点击 Intent 跳转 + Intent shortcutIntent = new Intent(Intent.ACTION_MAIN); + shortcutIntent.setClassName(DevUtils.getContext(), className); + shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + return shortcutIntent; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getShortCutIntent"); + } + } + return null; + } + + // = + + /** + * 创建桌面快捷方式 + * @param clazz 快捷方式点击 Intent class + * @param name 快捷方式名称 + * @param icon 快捷方式图标 + */ + public static void addShortcut(final Class clazz, final String name, @AnyRes final int icon) { + if (clazz != null) { + addShortcut(clazz.getName(), name, icon); + } + } + + /** + * 创建桌面快捷方式 + * @param className 快捷方式点击 Intent className(class.getName()) + * @param name 快捷方式名称 + * @param icon 快捷方式图标 + */ + public static void addShortcut(final String className, final String name, @AnyRes final int icon) { + if (className != null && name != null) { + try { + // 快捷方式点击 Intent 跳转 + Intent shortcutIntent = new Intent(Intent.ACTION_MAIN); + shortcutIntent.setClassName(DevUtils.getContext(), className); + shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + addShortcut(shortcutIntent, name, icon); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "addShortcut"); + } + } + } + + /** + * 创建桌面快捷方式 + * @param shortcutIntent 快捷方式点击 Intent 跳转 + * @param name 快捷方式名称 + * @param icon 快捷方式图标 + */ + public static void addShortcut(final Intent shortcutIntent, final String name, @AnyRes final int icon) { + addShortcut(shortcutIntent, name, icon, null); + } + + /** + * 创建桌面快捷方式 + * @param shortcutIntent 快捷方式点击 Intent 跳转 + * @param name 快捷方式名称 + * @param icon 快捷方式图标 + * @param pendingIntent 创建结果通知 (Android 8.0) + */ + public static void addShortcut(final Intent shortcutIntent, final String name, @AnyRes final int icon, final PendingIntent pendingIntent) { + if (shortcutIntent != null && name != null) { + // Android 8.0 之前 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + try { + Context context = DevUtils.getContext(); + // 快捷方式图标 + ShortcutIconResource iconRes = ShortcutIconResource.fromContext(context, icon); + // 创建快捷方式 Intent + Intent shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT"); + shortcut.putExtra("duplicate", false); // 不允许重复创建 + shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); // 快捷方式名称 + shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); // 快捷方式点击跳转 + shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes); + // 发送广播, 创建快捷方式 + context.sendBroadcast(shortcut); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "addShortcut"); + } + } else { // Android 8.0 之后 + try { + Context context = DevUtils.getContext(); + // 获取 ShortcutManager + ShortcutManager shortcutManager = (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE); + // 如果默认桌面支持 requestPinShortcut(ShortcutInfo、IntentSender) 方法 + if (shortcutManager != null && shortcutManager.isRequestPinShortcutSupported()) { + // 快捷方式创建相关信息 + ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(context, name.hashCode() + "") + .setIcon(Icon.createWithResource(context, icon)) // 快捷方式图标 + .setShortLabel(name) // 快捷方式名字 + .setIntent(shortcutIntent) // 快捷方式跳转 Intent + .build(); + // 创建快捷方式 + if (pendingIntent != null) { + shortcutManager.requestPinShortcut(shortcutInfo, pendingIntent.getIntentSender()); + } else { + shortcutManager.requestPinShortcut(shortcutInfo, null); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "addShortcut"); + } + } + } + } + + // ==================== + // = 删除桌面快捷方式 = + // ==================== + + /** + * 删除桌面快捷方式 + * @param clazz 快捷方式点击 Intent class + * @param name 快捷方式名称 + */ + public static void deleteShortcut(final Class clazz, final String name) { + if (clazz != null) { + deleteShortcut(clazz.getName(), name); + } + } + + /** + * 删除桌面快捷方式 + *
+     *     Android 6.0 以后因存在安全隐患 Google 移除了 UninstallShortcutReceiver 无法进行删除桌面快捷
+     * 
+ * @param className 快捷方式点击 Intent className(class.getName()) + * @param name 快捷方式名称 + */ + public static void deleteShortcut(final String className, final String name) { + if (className != null && name != null) { + try { + Context context = DevUtils.getContext(); + // 快捷方式 Intent + Intent shortcutIntent = new Intent(Intent.ACTION_MAIN); + shortcutIntent.setClassName(context, className); + // 删除快捷方式 Intent + Intent shortcut = new Intent("com.android.launcher.action.UNINSTALL_SHORTCUT"); + shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); // 快捷方式名称 + shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); + context.sendBroadcast(shortcut); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "deleteShortcut"); + } + } + } + + // ============ + // = 内部方法 = + // ============ + + /** + * 获取 Authority + * @param context {@link Context} + * @return Authority + */ + private static String getAuthority(final Context context) { + // 获取 Authority + String authority = getAuthorityFromPermission(context); + // 如果等于 null + if (authority == null) { + int version = android.os.Build.VERSION.SDK_INT; + if (version < 8) { // Android 2.1.x (API 7) 以及以下的 + authority = "com.android.launcher.settings"; + } else if (version < 19) { // Android 4.4 以下 + authority = "com.android.launcher2.settings"; + } else { // 4.4 以及以上 + authority = "com.android.launcher3.settings"; + } + return authority; + } + return authority; + } + + /** + * 通过权限获取 Authority + * @param context {@link Context} + * @return Authority + */ + private static String getAuthorityFromPermission(final Context context) { + // 权限判断 + String[] permissions = {"com.android.launcher.permission.WRITE_SETTINGS", "com.android.launcher.permission.READ_SETTINGS"}; + // 遍历权限 + for (String permission : permissions) { + try { + String authority = getAuthorityFromPermission(context, permission); + if (authority != null) { + return authority; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAuthorityFromPermission"); + } + } + return null; + } + + /** + * 通过权限获取 Authority + *
+     *     android 默认的 AUTHORITY 在 2.2 以后是 com.android.launcher2.settings
+     *     但是不同的厂商可能会做不同的修改, 由于不同的厂商 uri 的前缀不同
+     *     所以需要去查询 provider 获取真实的 content 的 uri 前缀
+     * 
+ * @param context {@link Context} + * @param permission 权限 + * @return Authority + */ + private static String getAuthorityFromPermission(final Context context, final String permission) { + if (permission != null) { + List lists = context.getPackageManager().getInstalledPackages(PackageManager.GET_PROVIDERS); + if (lists != null) { + for (PackageInfo packageInfo : lists) { + ProviderInfo[] providers = packageInfo.providers; + if (providers != null) { + for (ProviderInfo provider : providers) { + if (permission.equals(provider.readPermission)) + return provider.authority; + if (permission.equals(provider.writePermission)) + return provider.authority; + } + } + } + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/SignaturesUtils.java b/lib/DevApp/src/main/java/dev/utils/app/SignaturesUtils.java new file mode 100644 index 0000000000..a537d44e17 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/SignaturesUtils.java @@ -0,0 +1,350 @@ +package dev.utils.app; + +import android.content.pm.Signature; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import javax.security.auth.x500.X500Principal; + +import dev.utils.LogPrintUtils; + +/** + * detail: 签名工具类 ( 获取 APP 签名信息 ) + * @author Ttt + *
+ *     Android 的 APK 应用签名机制以及读取签名的方法
+ *     @see 
+ * 
+ */ +public final class SignaturesUtils { + + private SignaturesUtils() { + } + + // 日志 TAG + private static final String TAG = SignaturesUtils.class.getSimpleName(); + + /** + * 判断签名是 debug 签名还是 release 签名 + * 检测应用程序是否是用 "CN=Android Debug,O=Android,C=US" 的 debug 信息进行签名 + */ + private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US"); + + /** + * 获取 MD5 签名 + * @param signatures {@link Signature}[] + * @return MD5 签名 + */ + public static String signatureMD5(final Signature[] signatures) { + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + if (signatures != null) { + for (Signature s : signatures) { + if (s != null) { + digest.update(s.toByteArray()); + } + } + } + return toHexString(digest.digest()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "signatureMD5"); + return ""; + } + } + + /** + * 获取签名 SHA1 加密字符串 + * @param signatures {@link Signature}[] + * @return 签名 SHA1 加密字符串 + */ + public static String signatureSHA1(final Signature[] signatures) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + if (signatures != null) { + for (Signature s : signatures) { + if (s != null) { + digest.update(s.toByteArray()); + } + } + } + return toHexString(digest.digest()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "signatureSHA1"); + return ""; + } + } + + /** + * 获取签名 SHA256 加密字符串 + * @param signatures {@link Signature}[] + * @return 签名 SHA256 加密字符串 + */ + public static String signatureSHA256(final Signature[] signatures) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + if (signatures != null) { + for (Signature s : signatures) { + if (s != null) { + digest.update(s.toByteArray()); + } + } + } + return toHexString(digest.digest()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "signatureSHA256"); + return ""; + } + } + + /** + * 判断 debug 签名还是 release 签名 + * @param signatures {@link Signature}[] + * @return {@code true} debug.keystore, {@code false} release.keystore + */ + public static boolean isDebuggable(final Signature[] signatures) { + // 默认属于 debug 签名 + boolean debuggable = true; + if (signatures != null) { + try { + for (int i = 0, len = signatures.length; i < len; i++) { + Signature s = signatures[i]; + if (s != null) { + X509Certificate cert = getX509Certificate(s); + debuggable = cert.getSubjectX500Principal().equals(DEBUG_DN); + if (debuggable) { + break; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isDebuggable"); + } + } + return debuggable; + } + + /** + * 获取证书对象 + * @param signatures {@link Signature}[] + * @return {@link X509Certificate} + */ + public static X509Certificate getX509Certificate(final Signature[] signatures) { + if (signatures != null && signatures.length != 0) { + return getX509Certificate(signatures[0]); + } + return null; + } + + /** + * 获取证书对象 + * @param signature {@link Signature} + * @return {@link X509Certificate} + */ + public static X509Certificate getX509Certificate(final Signature signature) { + if (signature != null) { + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + ByteArrayInputStream bais = new ByteArrayInputStream(signature.toByteArray()); + X509Certificate cert = (X509Certificate) cf.generateCertificate(bais); + return cert; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getX509Certificate"); + } + } + return null; + } + + /** + * 打印签名信息 + * @param signatures {@link Signature}[] + * @return 签名信息 {@link List} + */ + public static List> printSignatureInfo(final Signature[] signatures) { + List> lists = new ArrayList<>(); + try { + // 格式化日期 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // 遍历获取 + for (int i = 0, len = signatures.length; i < len; i++) { + Signature s = signatures[i]; + if (s != null) { + X509Certificate cert = getX509Certificate(s); + + // 证书生成日期 + Date notBefore = cert.getNotBefore(); + // 证书有效期 + Date notAfter = cert.getNotAfter(); + // 设置有效期 + StringBuilder builder = new StringBuilder(); + builder.append(sdf.format(notBefore)); + builder.append(" to "); // 至 + builder.append(sdf.format(notAfter)); + builder.append("\n\n"); + builder.append(notBefore); + builder.append(" to "); + builder.append(notAfter); + // 保存有效期转换信息 + String effectiveStr = builder.toString(); + // 证书是否过期 + boolean effective = false; + try { + cert.checkValidity(); + // CertificateExpiredException - 如果证书已过期 + // CertificateNotYetValidException - 如果证书不再有效 + } catch (CertificateExpiredException ce) { + effective = true; + } catch (CertificateNotYetValidException ce) { + effective = true; + } + // 证书发布方 + String certPrincipal = cert.getIssuerX500Principal().toString(); + // 证书版本号 + String certVersion = cert.getVersion() + ""; + // 证书算法名称 + String certSigalgname = cert.getSigAlgName(); + // 证书算法 OID + String certSigalgoid = cert.getSigAlgOID(); + // 证书机器码 + String certSerialnumber = cert.getSerialNumber().toString(); + // 证书 DER 编码 + String certDercode = null; + try { + // 证书 DER 编码 + certDercode = toHexString(cert.getTBSCertificate()); + } catch (CertificateEncodingException e) { + } + // 证书公钥 + String pubKey = cert.getPublicKey().toString(); + + // 保存信息 + HashMap maps = new HashMap<>(); + maps.put("证书有效期信息", effectiveStr); + maps.put("证书是否过期", effective + ""); + maps.put("证书发布方", certPrincipal); + maps.put("证书版本号", certVersion); + maps.put("证书算法名称", certSigalgname); + maps.put("证书算法 OID", certSigalgoid); + maps.put("证书机器码", certSerialnumber); + maps.put("证书 DER 编码", certDercode); + maps.put("证书公钥", pubKey); + lists.add(maps); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "printSignatureInfo"); + } + return lists; + } + + // = + + /** + * 从 APK 中读取签名 + * @param file 文件 + * @return {@link Signature}[] + */ + public static Signature[] getSignaturesFromApk(final File file) { + try { + Certificate[] certificates = getCertificateFromApk(file); + return new Signature[]{new Signature(certificates[0].getEncoded())}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSignaturesFromApk"); + } + return null; + } + + /** + * 从 APK 中读取签名 + * @param file 文件 + * @return {@link Certificate}[] + */ + public static Certificate[] getCertificateFromApk(final File file) { + try { + JarFile jarFile = new JarFile(file); + JarEntry je = jarFile.getJarEntry("AndroidManifest.xml"); + byte[] readBuffer = new byte[8192]; + return loadCertificates(jarFile, je, readBuffer); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getCertificateFromApk"); + } + return null; + } + + /** + * 加载文件, 获取签名信息 + * @param jarFile {@link JarFile} + * @param jarEntry {@link JarEntry} + * @param readBuffer 文件 Buffer + * @return {@link Certificate}[] + */ + private static Certificate[] loadCertificates(final JarFile jarFile, final JarEntry jarEntry, final byte[] readBuffer) { + try { + InputStream is = jarFile.getInputStream(jarEntry); + while (is.read(readBuffer, 0, readBuffer.length) != -1) { + } + is.close(); + return jarEntry != null ? jarEntry.getCertificates() : null; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "loadCertificates"); + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data) { + return toHexString(data, HEX_DIGITS); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "toHexString"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/SizeUtils.java b/lib/DevApp/src/main/java/dev/utils/app/SizeUtils.java new file mode 100644 index 0000000000..9cda1f2ec9 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/SizeUtils.java @@ -0,0 +1,259 @@ +package dev.utils.app; + +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 大小工具类 (dp, px, sp 转换、View 获取宽高等 ) + * @author Ttt + */ +public final class SizeUtils { + + private SizeUtils() { + } + + // 日志 TAG + private static final String TAG = SizeUtils.class.getSimpleName(); + + /** + * dp 转 px + * @param dpValue 待转换值 + * @return 转换后的值 + */ + public static int dipConvertPx(final float dpValue) { + return (int) dipConvertPxf(dpValue); + } + + /** + * dp 转 px (float) + * @param dpValue 待转换值 + * @return 转换后的值 + */ + public static float dipConvertPxf(final float dpValue) { + try { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, DevUtils.getContext().getResources().getDisplayMetrics()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "dipConvertPxf"); + } + return 0f; + } + + // = + + /** + * px 转 dp + * @param pxValue 待转换值 + * @return 转换后的值 + */ + public static int pxConvertDip(final float pxValue) { + return (int) pxConvertDipf(pxValue); + } + + /** + * px 转 dp (float) + * @param pxValue 待转换值 + * @return 转换后的值 + */ + public static float pxConvertDipf(final float pxValue) { + try { + float scale = DevUtils.getContext().getResources().getDisplayMetrics().density; + return (pxValue / scale); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "pxConvertDipf"); + } + return 0f; + } + + // = + + /** + * sp 转 px + * @param spValue 待转换值 + * @return 转换后的值 + */ + public static int spConvertPx(final float spValue) { + return (int) spConvertPxf(spValue); + } + + /** + * sp 转 px (float) + * @param spValue 待转换值 + * @return 转换后的值 + */ + public static float spConvertPxf(final float spValue) { + try { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, DevUtils.getContext().getResources().getDisplayMetrics()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "spConvertPxf"); + } + return 0f; + } + + // = + + /** + * px 转 sp + * @param pxValue 待转换值 + * @return 转换后的值 + */ + public static int pxConvertSp(final float pxValue) { + return (int) pxConvertSpf(pxValue); + } + + /** + * px 转 sp (float) + * @param pxValue 待转换值 + * @return 转换后的值 + */ + public static float pxConvertSpf(final float pxValue) { + try { + float scale = DevUtils.getContext().getResources().getDisplayMetrics().scaledDensity; + return (pxValue / scale); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "pxConvertSpf"); + } + return 0f; + } + + // = + + /** + * 各种单位转换 - 该方法存在于 TypedValue.applyDimension + * @param unit 单位 + * @param value 值 + * @return 转换后的值 + */ + public static float applyDimension(final int unit, final float value) { + try { + return applyDimension(unit, value, DevUtils.getContext().getResources().getDisplayMetrics()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "applyDimension"); + } + return 0f; + } + + /** + * 各种单位转换 - 该方法存在于 TypedValue.applyDimension + * @param unit 单位 + * @param value 值 + * @param metrics {@link DisplayMetrics} + * @return 转换后的值 + */ + public static float applyDimension(final int unit, final float value, final DisplayMetrics metrics) { + if (metrics != null) { + return TypedValue.applyDimension(unit, value, metrics); + } + return 0f; + } + + // = + + /** + * 在 onCreate 中获取视图的尺寸 - 需回调 onGetSizeListener 接口, 在 onGetSize 中获取 View 宽高 + *
+     *     用法示例如下所示
+     *     

+ * SizeUtils.forceGetViewSize(view, new SizeUtils.onGetSizeListener() { + * Override + * public void onGetSize(final View view) { + * view.getWidth(); + * } + * }); + *
+ * @param view {@link View} + * @param listener {@link onGetSizeListener} + */ + public static void forceGetViewSize(final View view, final onGetSizeListener listener) { + if (view != null) { + view.post(new Runnable() { + @Override + public void run() { + if (listener != null) { + listener.onGetSize(view); + } + } + }); + } + } + + /** + * detail: 获取 View 宽高监听 + * @author Ttt + */ + public interface onGetSizeListener { + + /** + * 获取到 View 宽高监听通知 + * @param view {@link View} + */ + void onGetSize(View view); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = ViewUtils = + // ============= + + /** + * 测量 View + * @param view {@link View} + * @return int[] 0 = 宽度, 1 = 高度 + */ + public static int[] measureView(final View view) { + if (view != null) { + try { + ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); + if (layoutParams == null) { + layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + int widthSpec = ViewGroup.getChildMeasureSpec(0, 0, layoutParams.width); + int height = layoutParams.height; + int heightSpec; + if (height > 0) { + heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + } else { + heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + } + view.measure(widthSpec, heightSpec); + return new int[]{view.getMeasuredWidth(), view.getMeasuredHeight()}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "measureView"); + } + } + return new int[]{0, 0}; + } + + /** + * 获取 View 的宽度 + * @param view {@link View} + * @return View 的宽度 + */ + public static int getMeasuredWidth(final View view) { + if (view != null) { + measureView(view); + return view.getMeasuredWidth(); + } + return 0; + } + + /** + * 获取 View 的高度 + * @param view {@link View} + * @return View 的高度 + */ + public static int getMeasuredHeight(final View view) { + if (view != null) { + measureView(view); + return view.getMeasuredHeight(); + } + return 0; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/SnackbarUtils.java b/lib/DevApp/src/main/java/dev/utils/app/SnackbarUtils.java new file mode 100644 index 0000000000..35ff96793a --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/SnackbarUtils.java @@ -0,0 +1,1658 @@ +package dev.utils.app; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Point; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.LayoutRes; +import android.support.annotation.StringRes; +import android.support.design.widget.Snackbar; +import android.support.v4.app.Fragment; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.lang.ref.WeakReference; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.R; + +/** + * detail: Snackbar 工具类 + * @author Ttt + */ +public final class SnackbarUtils { + + // 日志 TAG + private final String TAG = SnackbarUtils.class.getSimpleName(); + // 持有 弱引用 Snackbar, 防止所属 View 销毁等 + private static WeakReference sSnackbarReference; + // 样式构造对象 + private StyleBuilder mStyleBuilder = new StyleBuilder(); + + /** + * 构造函数 + * @param view {@link View} + */ + private SnackbarUtils(final View view) { + if (view != null) { + try { + sSnackbarReference = new WeakReference<>(Snackbar.make(view, "", Snackbar.LENGTH_SHORT)); + } catch (Exception e) { + } + } + } + + // ============ + // = 构建方法 = + // ============ + + /** + * 获取 SnackbarUtils 对象 + * @param activity {@link Activity} + * @return {@link SnackbarUtils} + */ + public static SnackbarUtils with(final Activity activity) { + View view = null; + if (activity != null && activity.getWindow() != null) { + return new SnackbarUtils(activity.getWindow().getDecorView()); + } + return new SnackbarUtils(view); + } + + /** + * 获取 SnackbarUtils 对象 + * @param fragment {@link Fragment} + * @return {@link SnackbarUtils} + */ + public static SnackbarUtils with(final Fragment fragment) { + return new SnackbarUtils((fragment != null) ? fragment.getView() : null); + } + + /** + * 获取 SnackbarUtils 对象 + * @param window {@link Window} + * @return {@link SnackbarUtils} + */ + public static SnackbarUtils with(final Window window) { + return new SnackbarUtils((window != null) ? window.getDecorView() : null); + } + + /** + * 获取 SnackbarUtils 对象 + * @param view {@link View} + * @return {@link SnackbarUtils} + */ + public static SnackbarUtils with(final View view) { + return new SnackbarUtils(view); + } + + // ============ + // = 样式相关 = + // ============ + + /** + * 获取样式 + * @return {@link StyleBuilder} + */ + public StyleBuilder getStyle() { + return mStyleBuilder; + } + + /** + * 设置样式 + * @param style {@link SnackbarUtils.Style} + * @return {@link SnackbarUtils} + */ + public SnackbarUtils setStyle(final Style style) { + this.mStyleBuilder = new StyleBuilder(style); + return this; + } + + // ============ + // = 对外方法 = + // ============ + + /** + * 获取 Snackbar + * @return {@link Snackbar} + */ + public Snackbar getSnackbar() { + return (sSnackbarReference != null) ? sSnackbarReference.get() : null; + } + + /** + * 获取 Snackbar View + * @return {@link Snackbar#getView()} + */ + public View getSnackbarView() { + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + return snackbar.getView(); + } + return null; + } + + /** + * 获取 Snackbar TextView(snackbar_text) + * @return Snackbar {@link TextView} + */ + public TextView getTextView() { + View view = getSnackbarView(); + if (view != null) { + return view.findViewById(R.id.snackbar_text); + } + return null; + } + + /** + * 获取 Snackbar Action Button(snackbar_action) + *
+     *     右边按钮 ( 如: 撤销 )
+     * 
+ * @return Snackbar {@link Button} + */ + public Button getActionButton() { + View view = getSnackbarView(); + if (view != null) { + return view.findViewById(R.id.snackbar_action); + } + return null; + } + + /** + * 向 Snackbar 布局中添加 View (Google 不建议, 复杂的布局应该使用 DialogFragment 进行展示 ) + * @param layoutId R.layout.id + * @param index 添加索引 + * @return {@link SnackbarUtils} + */ + public SnackbarUtils addView(@LayoutRes final int layoutId, final int index) { + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + try { + // 加载布局文件新建 View + View view = LayoutInflater.from(snackbar.getView().getContext()).inflate(layoutId, null); + return addView(view, index); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "addView"); + } + } + return this; + } + + /** + * 向 Snackbar 布局中添加 View (Google 不建议, 复杂的布局应该使用 DialogFragment 进行展示 ) + * @param view {@link View} + * @param index 添加索引 + * @return {@link SnackbarUtils} + */ + public SnackbarUtils addView(final View view, final int index) { + Snackbar snackbar = getSnackbar(); + if (snackbar != null && view != null) { + try { + // 设置新建布局参数 + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + // 设置新建 View 在 Snackbar 内垂直居中显示 + params.gravity = Gravity.CENTER_VERTICAL; + view.setLayoutParams(params); + ((Snackbar.SnackbarLayout) snackbar.getView()).addView(view, index); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "addView"); + } + } + return this; + } + + /** + * 设置 Snackbar 展示完成、隐藏完成 的监听 + * @param callback {@link Snackbar.Callback} + * @return {@link SnackbarUtils} + */ + public SnackbarUtils setCallback(final Snackbar.Callback callback) { + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + snackbar.addCallback(callback); + } + return this; + } + + // = + + /** + * 设置 Action 按钮文字内容及点击监听 + * @param resId R.string.id + * @param objs 格式化参数 + * @return {@link SnackbarUtils} + */ + public SnackbarUtils setAction(@StringRes final int resId, final Object... objs) { + return setAction(null, resId, objs); + } + + /** + * 设置 Action 按钮文字内容及点击监听 + * @param listener 按钮点击事件 + * @param resId R.string.id + * @param objs 格式化参数 + * @return {@link SnackbarUtils} + */ + public SnackbarUtils setAction(final View.OnClickListener listener, @StringRes final int resId, final Object... objs) { + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + String content = getFormatRes(resId, objs); + if (!TextUtils.isEmpty(content)) { + snackbar.setAction(content, listener); + } + } + return this; + } + + /** + * 设置 Action 按钮文字内容及点击监听 + * @param text 按钮文本 + * @param objs 格式化参数 + * @return {@link SnackbarUtils} + */ + public SnackbarUtils setAction(final String text, final Object... objs) { + return setAction(null, text, objs); + } + + /** + * 设置 Action 按钮文字内容及点击监听 + * @param listener 按钮点击事件 + * @param text 按钮文本 + * @param objs 格式化参数 + * @return {@link SnackbarUtils} + */ + public SnackbarUtils setAction(final View.OnClickListener listener, final String text, final Object... objs) { + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + String content = getFormatString(text, objs); + if (!TextUtils.isEmpty(content)) { + snackbar.setAction(content, listener); + } + } + return this; + } + + // = + + /** + * 关闭 Snackbar + */ + public void dismiss() { + dismiss(true); + } + + /** + * 关闭 Snackbar + * @param setNull 是否设置 null + */ + public void dismiss(final boolean setNull) { + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + snackbar.dismiss(); + if (setNull) + sSnackbarReference = null; + } + } + + // ============ + // = 显示方法 = + // ============ + + /** + * 显示 Short Snackbar + * @param resId R.string.id + * @param objs 格式化参数 + */ + public void showShort(@StringRes final int resId, final Object... objs) { + priShow(getFormatRes(resId, objs), Snackbar.LENGTH_SHORT); + } + + /** + * 显示 Long Snackbar + * @param resId R.string.id + * @param objs 格式化参数 + */ + public void showLong(@StringRes final int resId, final Object... objs) { + priShow(getFormatRes(resId, objs), Snackbar.LENGTH_LONG); + } + + /** + * 显示 Indefinite Snackbar ( 无限时, 一直显示 ) + * @param resId R.string.id + * @param objs 格式化参数 + */ + public void showIndefinite(@StringRes final int resId, final Object... objs) { + priShow(getFormatRes(resId, objs), Snackbar.LENGTH_INDEFINITE); + } + + // = + + /** + * 显示 Short Snackbar + * @param text 显示文本 + * @param objs 格式化参数 + */ + public void showShort(final String text, final Object... objs) { + priShow(getFormatString(text, objs), Snackbar.LENGTH_SHORT); + } + + /** + * 显示 Long Snackbar + * @param text 显示文本 + * @param objs 格式化参数 + */ + public void showLong(final String text, final Object... objs) { + priShow(getFormatString(text, objs), Snackbar.LENGTH_LONG); + } + + /** + * 显示 Indefinite Snackbar ( 无限时, 一直显示 ) + * @param text 显示文本 + * @param objs 格式化参数 + */ + public void showIndefinite(final String text, final Object... objs) { + priShow(getFormatString(text, objs), Snackbar.LENGTH_INDEFINITE); + } + + // ============ + // = 内部方法 = + // ============ + + /** + * 内部显示方法 + * @param text 显示文本 + * @param duration 显示时长 {@link Snackbar#LENGTH_SHORT}、{@link Snackbar#LENGTH_LONG}、{@link Snackbar#LENGTH_INDEFINITE} + */ + private void priShow(final String text, final int duration) { + Snackbar snackbar = getSnackbar(); + if (snackbar != null && !snackbar.isShownOrQueued()) { + // 防止内容为 null + if (!TextUtils.isEmpty(text)) { + // 设置样式 + setSnackbarStyle(snackbar); + try { + // 设置坐标位置 + setSnackbarLocation(snackbar); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "priShow - setSnackbarLocation"); + } + // 显示 SnackBar + snackbar.setText(text).setDuration(duration).show(); + } + } + } + + // = + + /** + * detail: Snackbar 样式配置构造类 + * @author Ttt + */ + public static final class StyleBuilder extends Style { + + // ============ + // = RootView = + // ============ + + // RootView 的重心 + private int rootGravity; + // RootView 背景圆角大小 + private float rootCornerRadius; + @ColorInt // RootView 背景着色颜色 + private int rootBackgroundTintColor; + // RootView 背景图片 + private Drawable rootBackground; + // RootView margin 边距 - new int[] { left, top, right, bottom } + private int[] rootMargin; + // RootView 透明度 + private float rootAlpha = 1.0f; + + // =============================== + // = snackbar_text TextView 相关 = + // =============================== + + // TextView 的重心 + private int textGravity; + @ColorInt // TextView 文本颜色 + private int textColor; + // TextView 字体大小 + private float textSize; + // TextView 最大行数 + private int textMaxLines; + // TextView Ellipsize 效果 + private TextUtils.TruncateAt textEllipsize; + // TextView 字体样式 + private Typeface textTypeface; + // TextView padding 边距 - new int[] { left, top, right, bottom } + private int[] textPadding; + + // =============================== + // = snackbar_action Button 相关 = + // =============================== + + // Action Button 的重心 + private int actionGravity; + @ColorInt // Action Button 文本颜色 + private int actionColor; + // Action Button 字体大小 + private float actionSize; + // Action Button padding 边距 - new int[] { left, top, right, bottom } + private int[] actionPadding; + // RootView 背景圆角大小 + private float actionCornerRadius; + @ColorInt // RootView 背景着色颜色 + private int actionBackgroundTintColor; + // RootView 背景图片 + private Drawable actionBackground; + + // = + + /** + * 构造函数 + */ + public StyleBuilder() { + } + + /** + * 构造函数 + * @param style {@link SnackbarUtils.Style} + */ + public StyleBuilder(final SnackbarUtils.Style style) { + if (style != null) { + + // ============ + // = RootView = + // ============ + + // RootView 的重心 + this.rootGravity = style.getRootGravity(); + // RootView 背景圆角大小 + this.rootCornerRadius = style.getRootCornerRadius(); + // RootView 背景着色颜色 + this.rootBackgroundTintColor = style.getRootBackgroundTintColor(); + // RootView 背景图片 + this.rootBackground = style.getRootBackground(); + // RootView margin 边距 - new int[] { left, top, right, bottom } + this.rootMargin = style.getRootMargin(); + // RootView 透明度 + this.rootAlpha = style.getRootAlpha(); + + // =============================== + // = snackbar_text TextView 相关 = + // =============================== + + // TextView 的重心 + this.textGravity = style.getTextGravity(); + // TextView 文本颜色 + this.textColor = style.getTextColor(); + // TextView 字体大小 + this.textSize = style.getTextSize(); + // TextView 最大行数 + this.textMaxLines = style.getTextMaxLines(); + // TextView Ellipsize 效果 + this.textEllipsize = style.getTextEllipsize(); + // TextView 字体样式 + this.textTypeface = style.getTextTypeface(); + // TextView padding 边距 - new int[] { left, top, right, bottom } + this.textPadding = style.getTextPadding(); + + // =============================== + // = snackbar_action Button 相关 = + // =============================== + + // Action Button 的重心 + this.actionGravity = style.getActionGravity(); + // Action Button 文本颜色 + this.actionColor = style.getActionColor(); + // Action Button 字体大小 + this.actionSize = style.getActionSize(); + // Action Button padding 边距 - new int[] { left, top, right, bottom } + this.actionPadding = style.getActionPadding(); + // RootView 背景圆角大小 + this.actionCornerRadius = style.getActionCornerRadius(); + // RootView 背景着色颜色 + this.actionBackgroundTintColor = style.getActionBackgroundTintColor(); + // RootView 背景图片 + this.actionBackground = style.getActionBackground(); + } + } + + // =========== + // = get/set = + // =========== + + // ============ + // = RootView = + // ============ + + /** + * 获取 RootView 的重心 + * @return RootView 的重心 + */ + @Override + public int getRootGravity() { + return rootGravity; + } + + /** + * 设置 RootView 的重心 + * @param rootGravity RootView 的重心 + * @return {@link StyleBuilder} + */ + public StyleBuilder setRootGravity(int rootGravity) { + this.rootGravity = rootGravity; + return this; + } + + /** + * 获取 RootView 背景圆角大小 + * @return RootView 背景圆角大小 + */ + @Override + public float getRootCornerRadius() { + return rootCornerRadius; + } + + /** + * 设置 RootView 背景圆角大小 + * @param rootCornerRadius RootView 背景圆角大小 + * @return {@link StyleBuilder} + */ + public StyleBuilder setRootCornerRadius(float rootCornerRadius) { + this.rootCornerRadius = rootCornerRadius; + return this; + } + + /** + * 获取 RootView 背景着色颜色 + * @return RootView 背景着色颜色 + */ + @ColorInt + @Override + public int getRootBackgroundTintColor() { + return rootBackgroundTintColor; + } + + /** + * 设置 RootView 背景着色颜色 + * @param rootBackgroundTintColor RootView 背景着色颜色 + * @return {@link StyleBuilder} + */ + public StyleBuilder setRootBackgroundTintColor(@ColorInt int rootBackgroundTintColor) { + this.rootBackgroundTintColor = rootBackgroundTintColor; + return this; + } + + /** + * 获取 RootView 背景图片 + * @return {@link Drawable} + */ + @Override + public Drawable getRootBackground() { + return rootBackground; + } + + /** + * 设置 RootView 背景图片 + * @param rootBackground {@link Drawable} + * @return {@link StyleBuilder} + */ + public StyleBuilder setRootBackground(Drawable rootBackground) { + this.rootBackground = rootBackground; + return this; + } + + /** + * 获取 RootView margin 边距 - new int[] { left, top, right, bottom } + * @return RootView margin[] + */ + @Override + public int[] getRootMargin() { + return rootMargin; + } + + /** + * 设置 RootView margin 边距 + * @param rootMargin margin[] + * @return {@link StyleBuilder} + */ + public StyleBuilder setRootMargin(int[] rootMargin) { + this.rootMargin = rootMargin; + return this; + } + + /** + * 获取 RootView 透明度 + * @return RootView 透明度 + */ + @Override + public float getRootAlpha() { + return rootAlpha; + } + + /** + * 设置 RootView 透明度 + * @param rootAlpha RootView 透明度 + * @return {@link StyleBuilder} + */ + public StyleBuilder setRootAlpha(float rootAlpha) { + this.rootAlpha = rootAlpha; + return this; + } + + // =============================== + // = snackbar_text TextView 相关 = + // =============================== + + /** + * 获取 TextView 的重心 + * @return TextView 的重心 + */ + @Override + public int getTextGravity() { + return textGravity; + } + + /** + * 设置 TextView 的重心 + * @param textGravity TextView 的重心 + * @return {@link StyleBuilder} + */ + public StyleBuilder setTextGravity(int textGravity) { + this.textGravity = textGravity; + return this; + } + + /** + * 获取 TextView 文本颜色 + * @return TextView 文本颜色 + */ + @ColorInt + @Override + public int getTextColor() { + return textColor; + } + + /** + * 设置 TextView 文本颜色 + * @param textColor TextView 文本颜色 + * @return {@link StyleBuilder} + */ + public StyleBuilder setTextColor(@ColorInt int textColor) { + this.textColor = textColor; + return this; + } + + /** + * 获取 TextView 字体大小 + * @return TextView 字体大小 + */ + @Override + public float getTextSize() { + return textSize; + } + + /** + * 设置 TextView 字体大小 + * @param textSize TextView 字体大小 + * @return {@link StyleBuilder} + */ + public StyleBuilder setTextSize(float textSize) { + this.textSize = textSize; + return this; + } + + /** + * 获取 TextView 最大行数 + * @return TextView 最大行数 + */ + @Override + public int getTextMaxLines() { + return textMaxLines; + } + + /** + * 设置 TextView 最大行数 + * @param textMaxLines TextView 最大行数 + * @return {@link StyleBuilder} + */ + public StyleBuilder setTextMaxLines(int textMaxLines) { + this.textMaxLines = textMaxLines; + return this; + } + + /** + * 获取 TextView Ellipsize 效果 + * @return {@link TextUtils.TruncateAt} + */ + @Override + public TextUtils.TruncateAt getTextEllipsize() { + return textEllipsize; + } + + /** + * 设置 TextView Ellipsize 效果 + * @param textEllipsize {@link TextUtils.TruncateAt} Ellipsize + * @return {@link StyleBuilder} + */ + public StyleBuilder setTextEllipsize(TextUtils.TruncateAt textEllipsize) { + this.textEllipsize = textEllipsize; + return this; + } + + /** + * 获取 TextView 字体样式 + * @return {@link Typeface} + */ + @Override + public Typeface getTextTypeface() { + return textTypeface; + } + + /** + * 设置 TextView 字体样式 + * @param textTypeface {@link Typeface} + * @return {@link StyleBuilder} + */ + public StyleBuilder setTextTypeface(Typeface textTypeface) { + this.textTypeface = textTypeface; + return this; + } + + /** + * 获取 TextView padding 边距 - new int[] { left, top, right, bottom } + * @return TextView padding[] + */ + @Override + public int[] getTextPadding() { + return textPadding; + } + + /** + * 设置 TextView padding 边距 + * @param textPadding TextView padding[] + * @return {@link StyleBuilder} + */ + public StyleBuilder setTextPadding(int[] textPadding) { + this.textPadding = textPadding; + return this; + } + + // =============================== + // = snackbar_action Button 相关 = + // =============================== + + /** + * 获取 Action Button 的重心 + * @return Action Button 的重心 + */ + @Override + public int getActionGravity() { + return actionGravity; + } + + /** + * 设置 Action Button 的重心 + * @param actionGravity Action Button 的重心 + * @return {@link StyleBuilder} + */ + public StyleBuilder setActionGravity(int actionGravity) { + this.actionGravity = actionGravity; + return this; + } + + /** + * 获取 Action Button 文本颜色 + * @return Action Button 文本颜色 + */ + @ColorInt + @Override + public int getActionColor() { + return actionColor; + } + + /** + * 设置 Action Button 文本颜色 + * @param actionColor Action Button 文本颜色 + * @return {@link StyleBuilder} + */ + public StyleBuilder setActionColor(@ColorInt int actionColor) { + this.actionColor = actionColor; + return this; + } + + /** + * 获取 Action Button 字体大小 + * @return Action Button 字体大小 + */ + @Override + public float getActionSize() { + return actionSize; + } + + /** + * 设置 Action Button 字体大小 + * @param actionSize Action Button 字体大小 + * @return {@link StyleBuilder} + */ + public StyleBuilder setActionSize(float actionSize) { + this.actionSize = actionSize; + return this; + } + + /** + * 获取 Action Button padding 边距 - new int[] { left, top, right, bottom } + * @return Action Button padding[] + */ + @Override + public int[] getActionPadding() { + return actionPadding; + } + + /** + * 设置 Action Button padding 边距 + * @param actionPadding Action Button padding[] + * @return {@link StyleBuilder} + */ + public StyleBuilder setActionPadding(int[] actionPadding) { + this.actionPadding = actionPadding; + return this; + } + + /** + * 获取 Action Button 背景圆角大小 + * @return Action Button 背景圆角大小 + */ + @Override + public float getActionCornerRadius() { + return actionCornerRadius; + } + + /** + * 设置 Action Button 背景圆角大小 + * @param actionCornerRadius Action Button 背景圆角大小 + * @return {@link StyleBuilder} + */ + public StyleBuilder setActionCornerRadius(float actionCornerRadius) { + this.actionCornerRadius = actionCornerRadius; + return this; + } + + /** + * 获取 Action Button 背景着色颜色 + * @return Action Button 背景着色颜色 + */ + @ColorInt + @Override + public int getActionBackgroundTintColor() { + return actionBackgroundTintColor; + } + + /** + * 设置 Action Button 背景着色颜色 + * @param actionBackgroundTintColor Action Button 背景着色颜色 + * @return {@link StyleBuilder} + */ + public StyleBuilder setActionBackgroundTintColor(@ColorInt int actionBackgroundTintColor) { + this.actionBackgroundTintColor = actionBackgroundTintColor; + return this; + } + + /** + * 获取 Action Button 背景图片 + * @return {@link Drawable} + */ + @Override + public Drawable getActionBackground() { + return actionBackground; + } + + /** + * 设置 Action Button 背景图片 + * @param actionBackground {@link Drawable} + * @return {@link StyleBuilder} + */ + public StyleBuilder setActionBackground(Drawable actionBackground) { + this.actionBackground = actionBackground; + return this; + } + } + + // ============ + // = 其他接口 = + // ============ + + /** + * detail: Snackbar 样式配置 + * @author Ttt + */ + public static abstract class Style { + + // ============ + // = RootView = + // ============ + + /** + * 获取 RootView 的重心 + * @return RootView 的重心 + */ + public int getRootGravity() { + return 0; + } + + /** + * 获取 RootView 背景圆角大小 + * @return RootView 背景圆角大小 + */ + public float getRootCornerRadius() { + return 0f; + } + + /** + * 获取 RootView 背景着色颜色 + * @return RootView 背景着色颜色 + */ + @ColorInt + public int getRootBackgroundTintColor() { + return 0; + } + + /** + * 获取 RootView 背景图片 + * @return {@link Drawable} + */ + public Drawable getRootBackground() { + return null; + } + + /** + * 获取 RootView margin 边距 - new int[] { left, top, right, bottom } + * @return RootView margin[] + */ + public int[] getRootMargin() { + return null; + } + + /** + * 获取 RootView 透明度 + * @return RootView 透明度 + */ + public float getRootAlpha() { + return 1.0f; + } + + // =============================== + // = snackbar_text TextView 相关 = + // =============================== + + /** + * 获取 TextView 的重心 + * @return TextView 的重心 + */ + public int getTextGravity() { + return 0; + } + + /** + * 获取 TextView 文本颜色 + * @return TextView 文本颜色 + */ + @ColorInt + public int getTextColor() { + return 0; + } + + /** + * 获取 TextView 字体大小 + * @return TextView 字体大小 + */ + public float getTextSize() { + return 0f; + } + + /** + * 获取 TextView 最大行数 + * @return TextView 最大行数 + */ + public int getTextMaxLines() { + return 0; + } + + /** + * 获取 TextView Ellipsize 效果 + * @return {@link TextUtils.TruncateAt} + */ + public TextUtils.TruncateAt getTextEllipsize() { + return null; + } + + /** + * 获取 TextView 字体样式 + * @return {@link Typeface} + */ + public Typeface getTextTypeface() { + return null; + } + + /** + * 获取 TextView padding 边距 - new int[] { left, top, right, bottom } + * @return TextView padding[] + */ + public int[] getTextPadding() { + return null; + } + + // =============================== + // = snackbar_action Button 相关 = + // =============================== + + /** + * 获取 Action Button 的重心 + * @return Action Button 的重心 + */ + public int getActionGravity() { + return 0; + } + + /** + * 获取 Action Button 文本颜色 + * @return Action Button 文本颜色 + */ + @ColorInt + public int getActionColor() { + return 0; + } + + /** + * 获取 Action Button 字体大小 + * @return Action Button 字体大小 + */ + public float getActionSize() { + return 0f; + } + + /** + * 获取 Action Button padding 边距 - new int[] { left, top, right, bottom } + * @return Action Button padding[] + */ + public int[] getActionPadding() { + return null; + } + + /** + * 获取 Action Button 背景圆角大小 + * @return Action Button 背景圆角大小 + */ + public float getActionCornerRadius() { + return 0f; + } + + /** + * 获取 Action Button 背景着色颜色 + * @return Action Button 背景着色颜色 + */ + @ColorInt + public int getActionBackgroundTintColor() { + return 0; + } + + /** + * 获取 Action Button 背景图片 + * @return {@link Drawable} + */ + public Drawable getActionBackground() { + return null; + } + } + + // ============ + // = 设置样式 = + // ============ + + /** + * 设置 Snackbar 样式配置 + * @param snackbar {@link Snackbar} + * @return {@link Snackbar} + */ + public Snackbar setSnackbarStyle(final Snackbar snackbar) { + return setSnackbarStyle(snackbar, mStyleBuilder); + } + + /** + * 设置 Snackbar 样式配置 + * @param snackbar {@link Snackbar} + * @param style {@link SnackbarUtils.Style} + * @return {@link Snackbar} + */ + public Snackbar setSnackbarStyle(final Snackbar snackbar, final SnackbarUtils.Style style) { + if (snackbar == null || style == null) { + return snackbar; + } + // 获取显示的 View + View rootView = snackbar.getView(); + if (rootView != null) { + + // ============ + // = RootView = + // ============ + + // 设置 RootView Gravity 处理 + if (style.getRootGravity() != 0) { + try { + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(rootView.getLayoutParams().width, rootView.getLayoutParams().height); + params.gravity = style.getRootGravity(); + rootView.setLayoutParams(params); + } catch (Exception e) { + } + } + + // 设置 RootView margin 边距 + int[] rootMargin = style.getRootMargin(); + if (rootMargin != null && rootMargin.length == 4) { + try { + ViewGroup.LayoutParams params = rootView.getLayoutParams(); + ((ViewGroup.MarginLayoutParams) params).setMargins(rootMargin[0], rootMargin[1], rootMargin[2], rootMargin[3]); + rootView.setLayoutParams(params); + } catch (Exception e) { + } + } + + // 设置 RootView 透明度 + if (style.getRootAlpha() >= 0f) { + float rootAlpha = style.getRootAlpha(); + rootAlpha = rootAlpha >= 1.0f ? 1.0f : (rootAlpha <= 0.0f ? 0.0f : rootAlpha); + rootView.setAlpha(rootAlpha); + } + + // 设置 RootView 背景相关 + // 获取背景图片 + Drawable rootBackgroundDrawable = style.getRootBackground(); + // 如果等于 null + if (rootBackgroundDrawable != null) { + // 设置背景 + setBackground(rootView, rootBackgroundDrawable); + } else { + if (style.getRootBackgroundTintColor() != 0) { + GradientDrawable drawable = new GradientDrawable(); + // 设置背景色 + drawable.setColor(style.getRootBackgroundTintColor()); + // 设置圆角大小 + drawable.setCornerRadius(style.getRootCornerRadius()); + // 设置背景 + setBackground(rootView, drawable); + } + } + + // =============================== + // = snackbar_text TextView 相关 = + // =============================== + + TextView textView = getTextView(); + // 防止 snackbar_text 为 null + if (textView != null) { + + // TextView 的重心 + if (style.getTextGravity() != 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + textView.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY); + } + textView.setGravity(style.getTextGravity()); + } + + // TextView 文本颜色 + if (style.getTextColor() != 0) { + textView.setTextColor(style.getTextColor()); + } + + // TextView 字体大小 + if (style.getTextSize() != 0f) { + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, style.getTextSize()); + } + + // TextView 最大行数 + if (style.getTextMaxLines() >= 1) { + textView.setMaxLines(style.getTextMaxLines()); + } + + // TextView Ellipsize 效果 + if (style.getTextEllipsize() != null) { + textView.setEllipsize(style.getTextEllipsize()); + } + + // TextView 字体样式 + if (style.getTextTypeface() != null) { + textView.setTypeface(style.getTextTypeface()); + } + + // TextView padding 边距 + int[] textPadding = style.getTextPadding(); + if (textPadding != null && textPadding.length == 4) { + textView.setPadding(textPadding[0], textPadding[1], textPadding[2], textPadding[3]); + } + } + + // =============================== + // = snackbar_action Button 相关 = + // =============================== + + Button actionButton = getActionButton(); + // 防止 snackbar_action Button 为 null + if (actionButton != null) { + + // Action Button 的重心 + if (style.getActionGravity() != 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + actionButton.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY); + } + actionButton.setGravity(style.getActionGravity()); + } + + // Action Button 文本颜色 + if (style.getActionColor() != 0) { + actionButton.setTextColor(style.getActionColor()); + } + + // Action Button 字体大小 + if (style.getActionSize() != 0f) { + actionButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, style.getActionSize()); + } + + // Action Button padding 边距 + int[] actionPadding = style.getActionPadding(); + if (actionPadding != null && actionPadding.length == 4) { + actionButton.setPadding(actionPadding[0], actionPadding[1], actionPadding[2], actionPadding[3]); + } + + // 设置 Action Button 背景相关 + // 获取背景图片 + Drawable actionBackgroundDrawable = style.getActionBackground(); + // 如果等于 null + if (actionBackgroundDrawable != null) { + // 设置背景 + setBackground(actionButton, actionBackgroundDrawable); + } else { + if (style.getActionBackgroundTintColor() != 0) { + GradientDrawable drawable = new GradientDrawable(); + // 设置背景色 + drawable.setColor(style.getActionBackgroundTintColor()); + // 设置圆角大小 + drawable.setCornerRadius(style.getActionCornerRadius()); + // 设置背景 + setBackground(actionButton, drawable); + } + } + } + } + return snackbar; + } + + // = + + // View 坐标 + private int[] mViewLocations = null; + // View 高度 + private int mViewHeight = 0; + // 指定 View 坐标, 显示的重心方向 ( 只有 TOP、BOTTOM) + private int mViewGravity = -1; + // 追加向上边距 ( 如: 状态栏高度 ) + private int mAppendTopMargin = 0; + // View 阴影 + private int mShadowMargin = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) ? 2 : 0; + // 判断是否自动计算 ( 如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示 ) + private boolean mAutoCalc = true; + + /** + * 获取阴影边距 + * @return 阴影边距 + */ + public int getShadowMargin() { + return mShadowMargin; + } + + /** + * 设置阴影边距 + * @param shadowMargin 阴影边距 + * @return {@link SnackbarUtils} + */ + public SnackbarUtils setShadowMargin(final int shadowMargin) { + this.mShadowMargin = shadowMargin; + return this; + } + + /** + * 判断是否自动计算边距 ( 如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示 ) + * @return {@code true} yes, {@code false} no + */ + public boolean isAutoCalc() { + return mAutoCalc; + } + + /** + * 设置是否自动计算边距 ( 如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示 ) + * @param autoCalc 是否自动计算边距 + * @return {@link SnackbarUtils} + */ + public SnackbarUtils setAutoCalc(final boolean autoCalc) { + this.mAutoCalc = autoCalc; + return this; + } + + /** + * 清空坐标相关信息 + */ + private void clearLocations() { + // 重置处理 + mViewHeight = 0; + mViewGravity = -1; + mViewLocations = null; + mAppendTopMargin = 0; + } + + /** + * 设置 Snackbar 显示在指定 View 的上方 + * @param targetView 目标 View + * @param appendTopMargin 追加边距 ( 如: 状态栏高度 ) {@link ScreenUtils#getStatusBarHeight} + * @return {@link SnackbarUtils} + */ + public SnackbarUtils above(final View targetView, final int appendTopMargin) { + // 清空重置处理 + clearLocations(); + // 防止为 null + if (targetView != null) { + mViewHeight = targetView.getHeight(); + mViewGravity = Gravity.TOP; + mViewLocations = new int[2]; + mAppendTopMargin = appendTopMargin; + targetView.getLocationOnScreen(mViewLocations); + } + return this; + } + + /** + * 设置 Snackbar 显示在指定 View 的下方 + * @param targetView 目标 View + * @param appendTopMargin 追加边距 ( 如: 状态栏高度 ) {@link ScreenUtils#getStatusBarHeight} + * @return {@link SnackbarUtils} + */ + public SnackbarUtils bellow(final View targetView, final int appendTopMargin) { + // 清空重置处理 + clearLocations(); + // 防止为 null + if (targetView != null) { + mViewHeight = targetView.getHeight(); + mViewGravity = Gravity.BOTTOM; + mViewLocations = new int[2]; + mAppendTopMargin = appendTopMargin; + targetView.getLocationOnScreen(mViewLocations); + } + return this; + } + + /** + * 设置 Snackbar 显示的坐标位置 + * @param snackbar {@link Snackbar} + */ + private void setSnackbarLocation(final Snackbar snackbar) { + if (snackbar == null) return; + // 获取显示的 View + View rootView = snackbar.getView(); + if (rootView != null) { + // = 特殊处理 = + // 属于显示在指定 View 坐标, 对应重心方向 + if (mViewLocations != null && mViewGravity != -1 && mViewHeight > 0) { + // View ( 坐标 ) 边距 + int[] margin = new int[4]; + // 判断 Style 是否为 null + if (mStyleBuilder != null) { + // 默认边距 + int[] rootMargin = mStyleBuilder.getRootMargin(); + if (rootMargin != null && rootMargin.length == 4) { + margin[0] = rootMargin[0]; + margin[2] = rootMargin[2]; + } + } + + // 获取 View 上方距离 + int mViewTop = mViewLocations[1]; + // 获取屏幕高度 + int screenHeight = getScreenHeight(); + // 防止等于 0 + if (screenHeight != 0) { + // 获取测量高度 ( 不一定准确 ) + int measuredHeight = getMeasuredHeight(rootView); + // 判断方向, 在指定坐标上方, 判断是否够空间 + if (mViewGravity == Gravity.TOP) { + // 判断是否超出可显示高度 + if (mViewTop - mShadowMargin - mAppendTopMargin >= measuredHeight) { + // 思路: 没有超出高度, 则正常显示在指定 View 上方 + // 改为布局居下 ( 相反方向 ), 然后设置 bottomMargin 为 屏幕高度 - view mWindowTop + 阴影大小 + // 这样的思路, 主要是只用知道 view 的 Y 轴位置, 然后用屏幕高度减去 Y 得到的就是需要向下的边距, 不需要计算 Snackbar View 高度 + try { + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(rootView.getLayoutParams().width, rootView.getLayoutParams().height); + params.gravity = Gravity.BOTTOM; + rootView.setLayoutParams(params); + } catch (Exception e) { + } + try { + ViewGroup.LayoutParams params = rootView.getLayoutParams(); + ((ViewGroup.MarginLayoutParams) params).setMargins(margin[0], 0, margin[2], screenHeight - mViewTop + mShadowMargin); + rootView.setLayoutParams(params); + } catch (Exception e) { + } + } else { // 超出可视范围 + // 判断是否自动计算处理 + if (mAutoCalc) { + // 思路如上: 超出高度后, 则直接设置居上, 计算边距则 view mWindowTop - 追加边距 ( 状态栏高度 ) + view height, 设置到 View 的下方 + // 计算处理主要是, 只需要知道 view Y 轴位置 + view height - 追加边距 ( 状态栏高度 ) = 需要的边距 + // 为什么需要减 状态栏高度, 是因为 view Y (view mWindowTop) 就包含状态栏的高度信息 + try { + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(rootView.getLayoutParams().width, rootView.getLayoutParams().height); + params.gravity = Gravity.TOP; + rootView.setLayoutParams(params); + } catch (Exception e) { + } + try { + ViewGroup.LayoutParams params = rootView.getLayoutParams(); + ((ViewGroup.MarginLayoutParams) params).setMargins(margin[0], mViewTop - mAppendTopMargin + mViewHeight, margin[2], 0); + rootView.setLayoutParams(params); + } catch (Exception e) { + } + } + } + } else { // 在指定坐标下方 + // 判断是否超出可显示高度 + if (screenHeight - (mViewTop + mShadowMargin + mAppendTopMargin + mViewHeight) >= measuredHeight) { + // 思路: 没有超出高度, 则正常显示在指定 View 下方 + // 并且改为布局居上, 然后设置 topMargin 为 view mWindowTop - ( 阴影大小 + 追加边距 ( 状态栏高度 )) + // 这样的思路, 主要是不居下, 不用知道 Snackbar view 高度, 导致向下边距计算错误, 转换思路从上处理 + try { + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(rootView.getLayoutParams().width, rootView.getLayoutParams().height); + params.gravity = Gravity.TOP; + rootView.setLayoutParams(params); + } catch (Exception e) { + } + try { + ViewGroup.LayoutParams params = rootView.getLayoutParams(); + ((ViewGroup.MarginLayoutParams) params).setMargins(margin[0], mViewTop - (mShadowMargin + mAppendTopMargin), margin[2], 0); + rootView.setLayoutParams(params); + } catch (Exception e) { + } + } else { // 超出可视范围 + // 判断是否自动计算处理 + if (mAutoCalc) { + // 思路如上: 超出高度后, 则直接设置居下, 计算边距则 用屏幕高度 - view mWindowTop + 阴影边距 + // 计算处理的值则是 view mWindowTop 距离底部的边距, 刚好设置 bottomMargin, 实现思路转换处理 + try { + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(rootView.getLayoutParams().width, rootView.getLayoutParams().height); + params.gravity = Gravity.BOTTOM; + rootView.setLayoutParams(params); + } catch (Exception e) { + } + try { + ViewGroup.LayoutParams params = rootView.getLayoutParams(); + ((ViewGroup.MarginLayoutParams) params).setMargins(margin[0], 0, margin[2], screenHeight - mViewTop + mShadowMargin); + rootView.setLayoutParams(params); + } catch (Exception e) { + } + } + } + } + } + } + } + // 清空重置处理 + clearLocations(); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = ScreenUtils = + // =============== + + /** + * 获取屏幕高度 + * @return 屏幕高度 + */ + private int getScreenHeight() { + try { + WindowManager windowManager = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); + if (windowManager == null) { + return DevUtils.getContext().getResources().getDisplayMetrics().heightPixels; + } + Point point = new Point(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + windowManager.getDefaultDisplay().getRealSize(point); + } else { + windowManager.getDefaultDisplay().getSize(point); + } + return point.y; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getScreenHeight"); + } + return 0; + } + + // ============= + // = ViewUtils = + // ============= + + /** + * 测量 View + * @param view {@link View} + * @return int[] 0 = 宽度, 1 = 高度 + */ + private int[] measureView(final View view) { + if (view != null) { + try { + ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); + if (layoutParams == null) { + layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + int widthSpec = ViewGroup.getChildMeasureSpec(0, 0, layoutParams.width); + int height = layoutParams.height; + int heightSpec; + if (height > 0) { + heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + } else { + heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + } + view.measure(widthSpec, heightSpec); + return new int[]{view.getMeasuredWidth(), view.getMeasuredHeight()}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "measureView"); + } + } + return new int[]{0, 0}; + } + + /** + * 获取 View 的高度 + * @param view {@link View} + * @return View 的高度 + */ + private int getMeasuredHeight(final View view) { + if (view != null) { + measureView(view); + return view.getMeasuredHeight(); + } + return 0; + } + + // ================== + // = 数据格式化处理 = + // ================== + + /** + * 获取格式化后的字符串 + * @param format 待格式化字符串 + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + private String getFormatString(final String format, final Object... args) { + if (format == null) return null; + try { + if (args != null && args.length != 0) { + return String.format(format, args); + } else { + return format; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getFormatString"); + } + return null; + } + + /** + * 获取 R.string 资源的格式化字符串 + * @param resId R.string.id + * @param objs 格式化参数 + * @return 格式化后的字符串 + */ + private String getFormatRes(@StringRes final int resId, final Object... objs) { + try { + if (objs != null && objs.length != 0) { + return DevUtils.getContext().getString(resId, objs); + } else { + return DevUtils.getContext().getString(resId); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getFormatRes"); + } + return null; + } + + // = + + /** + * 设置背景 + * @param view {@link View} + * @param drawable 背景 {@link Drawable} + */ + private void setBackground(final View view, final Drawable drawable) { + if (view != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) + view.setBackground(drawable); + else + view.setBackgroundDrawable(drawable); + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/SpannableStringUtils.java b/lib/DevApp/src/main/java/dev/utils/app/SpannableStringUtils.java new file mode 100644 index 0000000000..96b525d4c8 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/SpannableStringUtils.java @@ -0,0 +1,1461 @@ +package dev.utils.app; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BlurMaskFilter; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.Typeface; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.FloatRange; +import android.support.annotation.IntDef; +import android.support.annotation.IntRange; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; +import android.text.Layout; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.method.LinkMovementMethod; +import android.text.style.AbsoluteSizeSpan; +import android.text.style.AlignmentSpan; +import android.text.style.BackgroundColorSpan; +import android.text.style.CharacterStyle; +import android.text.style.ClickableSpan; +import android.text.style.ForegroundColorSpan; +import android.text.style.LeadingMarginSpan; +import android.text.style.LineHeightSpan; +import android.text.style.MaskFilterSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.ReplacementSpan; +import android.text.style.ScaleXSpan; +import android.text.style.StrikethroughSpan; +import android.text.style.StyleSpan; +import android.text.style.SubscriptSpan; +import android.text.style.SuperscriptSpan; +import android.text.style.TypefaceSpan; +import android.text.style.URLSpan; +import android.text.style.UnderlineSpan; +import android.text.style.UpdateAppearance; +import android.widget.TextView; + +import java.io.InputStream; +import java.io.Serializable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: SpannableString 工具类 + * @author Blankj + * @author Ttt + *
+ *     SpannableStringBuilder 工具类 ( 用于设置文字的前景色、背景色、Typeface、粗体、斜体、字号、超链接、删除线、下划线、上下标等 )
+ *     

+ * Android 开发 SpannableString 和 SpannableStringBuilder 的使用详解 + * @see
+ *

+ * Android 强大的 SpannableStringBuilder + * @see
+ *

+ * SpannableString 之富文本显示效果 + * @see
+ *

+ * @see
+ *

+ * SpannableString 与 SpannableStringBuilder 区别就比如 String 和 StringBuilder 一样 + *

+ * setSpan() - int flags + *

+ * Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + * 前后都不包括 ( 在标志位 [start, end) 前后添加文字, 新添加的文字不会有任何设置的属性 ) + *

+ * Spannable.SPAN_EXCLUSIVE_INCLUSIVE + * 前面不包括, 后面包括 ( 在标志位 [start, end) 前添加文字, 新添加的文字不会有任何设置的属性, 后边的添加的文字会带有设置的 what 属性 ) + *

+ * Spannable.SPAN_INCLUSIVE_EXCLUSIVE + * 前面包括, 后面不包括 ( 在标志位 [start, end) 后添加文字, 新添加的文字不会有任何设置的属性, 前边边的添加的文字会带有设置的 what 属性 ) + *

+ * Spannable.SPAN_INCLUSIVE_INCLUSIVE + * 前后都包括 前后都不包括 ( 在标志位 [start, end) 前后添加文字, 新添加的文字会有设置的属性 ) + *
+ */ +public final class SpannableStringUtils { + + // 日志 TAG + private static final String TAG = SpannableStringUtils.class.getSimpleName(); + + // 对齐类型 + public static final int ALIGN_BOTTOM = 0; + public static final int ALIGN_BASELINE = 1; + public static final int ALIGN_CENTER = 2; + public static final int ALIGN_TOP = 3; + + @IntDef({ALIGN_BOTTOM, ALIGN_BASELINE, ALIGN_CENTER, ALIGN_TOP}) + @Retention(RetentionPolicy.SOURCE) + public @interface Align { + } + + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + // 内部 SpannableString 实现类 + private SerializableSpannableStringBuilder mBuilder; + // TextView create setText + private TextView mTextView; + // 中转文本 + private CharSequence mText; + + // 内部标记应用类型 + private int mType; + private final int mTypeCharSequence = 0; + private final int mTypeImage = 1; + private final int mTypeSpace = 2; + + // 默认颜色 + private static final int COLOR_DEFAULT = Color.WHITE; + // 配置参数 + private int flag; // setSpan flag + private int foregroundColor; // 前景色 + private int backgroundColor; // 背景色 + private int lineHeight; // 行高 + private int alignLine; // 行对齐类型 + private int quoteColor; // 引用线颜色 + private int stripeWidth; // 线条宽度 + private int quoteGapWidth; // 间隙宽度 + private int first; // 第一行缩进 + private int rest; // 其他行缩进 + private int bulletColor; // 列表项颜色 + private int bulletRadius; // 列表项半径 + private int bulletGapWidth; // 列表项间隙宽度 + private int fontSize; // 字体大小 px + private boolean fontSizeIsDp; // 字体大小是否使用 dp + private float proportion; // 字体缩放比例 + private float xProportion; // 字体横向缩放比例 + private boolean isStrikethrough; // 是否中划线 + private boolean isUnderline; // 是否下划线 + private boolean isSuperscript; // 是否上标 + private boolean isSubscript; // 是否下标 + private boolean isBold; // 是否加粗 + private boolean isItalic; // 是否斜体 + private boolean isBoldItalic; // 是否粗斜体 + private String fontFamily; // 字体系列 + private Typeface typeface; // 字体 + private Layout.Alignment alignment; // 对齐类型 ( 水平对齐 ) + private int verticalAlign; // 垂直对齐类型 + private ClickableSpan clickSpan; // 点击区域 + private String url; // Url + private float blurRadius; // 模糊程度 + private BlurMaskFilter.Blur style; // 模糊样式 + private Shader shader; // 着色 + private float shadowRadius; // 阴影半径 + private float shadowDx; // X 轴阴影偏移值 + private float shadowDy; // Y 轴阴影偏移值 + private int shadowColor; // 阴影颜色 + private int spaceSize; // 空格大小 + private int spaceColor; // 空格颜色 + private Object[] spans; // 自定义 setSpan 参数 + + // 图片相关 + private Bitmap imageBitmap; // Bitmap + private Drawable imageDrawable; // Drawable + private Uri imageUri; // Uri + private int imageResourceId; // resource id + private int alignImage; // 图片对齐类型 + + // ============ + // = 构造函数 = + // ============ + + /** + * 构造函数 + * @param textView {@link TextView} + */ + private SpannableStringUtils(TextView textView) { + this(); + mTextView = textView; + } + + /** + * 构造函数 + */ + public SpannableStringUtils() { + mBuilder = new SerializableSpannableStringBuilder(); + mText = ""; + mType = -1; + setDefault(); + } + + /** + * 获取持有 TextView SpannableStringUtils + * @param textView {@link TextView} + * @return {@link SpannableStringUtils} + */ + public static SpannableStringUtils with(final TextView textView) { + return new SpannableStringUtils(textView); + } + + // ============ + // = 实现方法 = + // ============ + + /** + * 设置标识 + * @param flag 标识 + *
    + *
  • {@link Spanned#SPAN_INCLUSIVE_EXCLUSIVE}
  • + *
  • {@link Spanned#SPAN_INCLUSIVE_INCLUSIVE}
  • + *
  • {@link Spanned#SPAN_EXCLUSIVE_EXCLUSIVE}
  • + *
  • {@link Spanned#SPAN_EXCLUSIVE_INCLUSIVE}
  • + *
+ * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setFlag(final int flag) { + this.flag = flag; + return this; + } + + /** + * 设置前景色 + * @param color 前景色 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setForegroundColor(@ColorInt final int color) { + this.foregroundColor = color; + return this; + } + + /** + * 设置背景色 + * @param color 背景色 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setBackgroundColor(@ColorInt final int color) { + this.backgroundColor = color; + return this; + } + + /** + * 设置行高 + * @param lineHeight 行高 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setLineHeight(@IntRange(from = 0) final int lineHeight) { + return setLineHeight(lineHeight, ALIGN_CENTER); + } + + /** + * 设置行高 + * @param lineHeight 行高 + * @param align 行对齐类型 + *
    + *
  • {@link Align#ALIGN_TOP }
  • + *
  • {@link Align#ALIGN_CENTER}
  • + *
  • {@link Align#ALIGN_BOTTOM}
  • + *
+ * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setLineHeight(@IntRange(from = 0) final int lineHeight, + @Align final int align) { + this.lineHeight = lineHeight; + this.alignLine = align; + return this; + } + + /** + * 设置引用线的颜色 + * @param color 引用线颜色 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setQuoteColor(@ColorInt final int color) { + return setQuoteColor(color, 2, 2); + } + + /** + * 设置引用线的颜色 + * @param color 引用线颜色 + * @param stripeWidth 线条宽度 + * @param gapWidth 间隙宽度 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setQuoteColor(@ColorInt final int color, + @IntRange(from = 1) final int stripeWidth, + @IntRange(from = 0) final int gapWidth) { + this.quoteColor = color; + this.stripeWidth = stripeWidth; + this.quoteGapWidth = gapWidth; + return this; + } + + /** + * 设置缩进 + * @param first 段落第一行的缩进值 + * @param rest 段落其余行的缩进值 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setLeadingMargin(@IntRange(from = 0) final int first, + @IntRange(from = 0) final int rest) { + this.first = first; + this.rest = rest; + return this; + } + + /** + * 设置列表标记 + * @param gapWidth 列表间隙宽度 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setBullet(@IntRange(from = 0) final int gapWidth) { + return setBullet(0, 3, gapWidth); + } + + /** + * 设置列表标记 + * @param color 列表颜色 + * @param radius 列表半径 + * @param gapWidth 列表间隙宽度 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setBullet(@ColorInt final int color, + @IntRange(from = 0) final int radius, + @IntRange(from = 0) final int gapWidth) { + this.bulletColor = color; + this.bulletRadius = radius; + this.bulletGapWidth = gapWidth; + return this; + } + + /** + * 设置字体尺寸 + * @param size 字体大小 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setFontSize(@IntRange(from = 0) final int size) { + return setFontSize(size, false); + } + + /** + * 设置字体尺寸 + * @param size 字体大小 + * @param isDp 是否使用 dp + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setFontSize(@IntRange(from = 0) final int size, final boolean isDp) { + this.fontSize = size; + this.fontSizeIsDp = isDp; + return this; + } + + /** + * 设置字体比例 + * @param proportion 字体比例 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setFontProportion(final float proportion) { + this.proportion = proportion; + return this; + } + + /** + * 设置字体横向比例 + * @param proportion 字体横向比例 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setFontXProportion(final float proportion) { + this.xProportion = proportion; + return this; + } + + // = + + /** + * 设置删除线 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setStrikethrough() { + this.isStrikethrough = true; + return this; + } + + /** + * 设置下划线 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setUnderline() { + this.isUnderline = true; + return this; + } + + /** + * 设置上标 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setSuperscript() { + this.isSuperscript = true; + return this; + } + + /** + * 设置下标 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setSubscript() { + this.isSubscript = true; + return this; + } + + /** + * 设置粗体 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setBold() { + isBold = true; + return this; + } + + /** + * 设置斜体 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setItalic() { + isItalic = true; + return this; + } + + /** + * 设置粗斜体 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setBoldItalic() { + isBoldItalic = true; + return this; + } + + /** + * 设置字体系列 + * @param fontFamily 字体系列 + *
    + *
  • monospace
  • + *
  • serif
  • + *
  • sans-serif
  • + *
+ * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setFontFamily(@NonNull final String fontFamily) { + this.fontFamily = fontFamily; + return this; + } + + /** + * 设置字体 + * @param typeface {@link Typeface} + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setTypeface(@NonNull final Typeface typeface) { + this.typeface = typeface; + return this; + } + + /** + * 设置水平对齐 + * @param alignment 对齐类型 + *
    + *
  • {@link Layout.Alignment#ALIGN_NORMAL }
  • + *
  • {@link Layout.Alignment#ALIGN_OPPOSITE}
  • + *
  • {@link Layout.Alignment#ALIGN_CENTER }
  • + *
+ * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setHorizontalAlign(@NonNull final Layout.Alignment alignment) { + this.alignment = alignment; + return this; + } + + /** + * 设置垂直对齐 + * @param align 对齐类型 + *
    + *
  • {@link Align#ALIGN_TOP }
  • + *
  • {@link Align#ALIGN_CENTER }
  • + *
  • {@link Align#ALIGN_BASELINE}
  • + *
  • {@link Align#ALIGN_BOTTOM }
  • + *
+ * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setVerticalAlign(@Align final int align) { + this.verticalAlign = align; + return this; + } + + /** + * 设置点击事件 + *
+     *     需设置 {@code view.setMovementMethod(LinkMovementMethod.getInstance())}
+     * 
+ * @param clickSpan {@link ClickableSpan} + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setClickSpan(@NonNull final ClickableSpan clickSpan) { + if (mTextView != null && mTextView.getMovementMethod() == null) { + mTextView.setMovementMethod(LinkMovementMethod.getInstance()); + } + this.clickSpan = clickSpan; + return this; + } + + /** + * 设置超链接 + *
+     *     需设置 {@code view.setMovementMethod(LinkMovementMethod.getInstance())}
+     * 
+ * @param url 超链接 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setUrl(@NonNull final String url) { + if (mTextView != null && mTextView.getMovementMethod() == null) { + mTextView.setMovementMethod(LinkMovementMethod.getInstance()); + } + this.url = url; + return this; + } + + /** + * 设置模糊 + * @param radius 模糊程度 + * @param style 模糊样式 + *
    + *
  • {@link BlurMaskFilter.Blur#NORMAL}
  • + *
  • {@link BlurMaskFilter.Blur#SOLID}
  • + *
  • {@link BlurMaskFilter.Blur#OUTER}
  • + *
  • {@link BlurMaskFilter.Blur#INNER}
  • + *
+ * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setBlur(@FloatRange(from = 0, fromInclusive = false) final float radius, + final BlurMaskFilter.Blur style) { + this.blurRadius = radius; + this.style = style; + return this; + } + + /** + * 设置着色器 + * @param shader {@link Shader} + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setShader(@NonNull final Shader shader) { + this.shader = shader; + return this; + } + + /** + * 设置阴影 + * @param radius 阴影半径 + * @param dx X 轴阴影偏移值 + * @param dy Y 轴阴影偏移值 + * @param shadowColor 阴影颜色 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setShadow(@FloatRange(from = 0, fromInclusive = false) final float radius, + final float dx, final float dy, final int shadowColor) { + this.shadowRadius = radius; + this.shadowDx = dx; + this.shadowDy = dy; + this.shadowColor = shadowColor; + return this; + } + + // = + + /** + * 自定义 setSpan 参数 + * @param spans span 数组参数 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils setSpans(@NonNull final Object... spans) { + if (spans.length > 0) { + this.spans = spans; + } + return this; + } + + /** + * 追加文本 + * @param text 文本内容 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils append(@NonNull final CharSequence text) { + apply(mTypeCharSequence); + mText = text; + return this; + } + + /** + * 追加换行 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendLine() { + apply(mTypeCharSequence); + mText = NEW_LINE_STR; + return this; + } + + /** + * 追加文本 ( 换行 ) + * @param text 文本内容 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendLine(@NonNull final CharSequence text) { + apply(mTypeCharSequence); + mText = text + NEW_LINE_STR; + return this; + } + + // = + + /** + * 追加 Image + * @param bitmap {@link Bitmap} image + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendImage(@NonNull final Bitmap bitmap) { + return appendImage(bitmap, ALIGN_BOTTOM); + } + + /** + * 追加 Image + * @param bitmap {@link Bitmap} image + * @param align 对齐类型 + *
    + *
  • {@link Align#ALIGN_TOP }
  • + *
  • {@link Align#ALIGN_CENTER }
  • + *
  • {@link Align#ALIGN_BASELINE}
  • + *
  • {@link Align#ALIGN_BOTTOM }
  • + *
+ * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendImage(@NonNull final Bitmap bitmap, @Align final int align) { + apply(mTypeImage); + this.imageBitmap = bitmap; + this.alignImage = align; + return this; + } + + /** + * 追加 Image + * @param drawable {@link Drawable} image + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendImage(@NonNull final Drawable drawable) { + return appendImage(drawable, ALIGN_BOTTOM); + } + + /** + * 追加 Image + * @param drawable {@link Drawable} image + * @param align 对齐类型 + *
    + *
  • {@link Align#ALIGN_TOP }
  • + *
  • {@link Align#ALIGN_CENTER }
  • + *
  • {@link Align#ALIGN_BASELINE}
  • + *
  • {@link Align#ALIGN_BOTTOM }
  • + *
+ * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendImage(@NonNull final Drawable drawable, @Align final int align) { + apply(mTypeImage); + this.imageDrawable = drawable; + this.alignImage = align; + return this; + } + + /** + * 追加 Image + * @param uri {@link Uri} image + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendImage(@NonNull final Uri uri) { + return appendImage(uri, ALIGN_BOTTOM); + } + + /** + * 追加 Image + * @param uri {@link Uri} image + * @param align 对齐类型 + *
    + *
  • {@link Align#ALIGN_TOP }
  • + *
  • {@link Align#ALIGN_CENTER }
  • + *
  • {@link Align#ALIGN_BASELINE}
  • + *
  • {@link Align#ALIGN_BOTTOM }
  • + *
+ * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendImage(@NonNull final Uri uri, @Align final int align) { + apply(mTypeImage); + this.imageUri = uri; + this.alignImage = align; + return this; + } + + /** + * 追加 Image + * @param resourceId The resource id of image + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendImage(@DrawableRes final int resourceId) { + return appendImage(resourceId, ALIGN_BOTTOM); + } + + /** + * 追加 Image + * @param resourceId The resource id of image + * @param align 对齐类型 + *
    + *
  • {@link Align#ALIGN_TOP }
  • + *
  • {@link Align#ALIGN_CENTER }
  • + *
  • {@link Align#ALIGN_BASELINE}
  • + *
  • {@link Align#ALIGN_BOTTOM }
  • + *
+ * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendImage(@DrawableRes final int resourceId, @Align final int align) { + apply(mTypeImage); + this.imageResourceId = resourceId; + this.alignImage = align; + return this; + } + + // = + + /** + * 追加空格 + * @param size 空格大小 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendSpace(@IntRange(from = 0) final int size) { + return appendSpace(size, Color.TRANSPARENT); + } + + /** + * 追加空格 + * @param size 空格大小 + * @param color 空格颜色 + * @return {@link SpannableStringUtils} + */ + public SpannableStringUtils appendSpace(@IntRange(from = 0) final int size, @ColorInt final int color) { + apply(mTypeSpace); + this.spaceSize = size; + this.spaceColor = color; + return this; + } + + // = + + /** + * 获取 SpannableStringBuilder + * @return {@link SpannableStringBuilder} + */ + public SpannableStringBuilder get() { + return mBuilder; + } + + /** + * 创建 SpannableStringBuilder + * @return {@link SpannableStringBuilder} + */ + public SpannableStringBuilder create() { + applyLast(); + if (mTextView != null) { + mTextView.setText(mBuilder); + } + return mBuilder; + } + + // ============ + // = 私有方法 = + // ============ + + /** + * 应用效果 + * @param type 效果 ( 应用 ) 类型 + */ + private void apply(final int type) { + applyLast(); + mType = type; + } + + /** + * 应用效果, 并重置配置 + */ + private void applyLast() { + if (mType == mTypeCharSequence) { + updateCharCharSequence(); + } else if (mType == mTypeImage) { + updateImage(); + } else if (mType == mTypeSpace) { + updateSpace(); + } + setDefault(); + } + + /** + * 更新 CharSequence 字符 + */ + private void updateCharCharSequence() { + if (mText.length() == 0) return; + int start = mBuilder.length(); + if (start == 0 && lineHeight != -1) { // bug of LineHeightSpan when first line + mBuilder.append(Character.toString((char) 2)).append("\n") + .setSpan(new AbsoluteSizeSpan(0), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + start = 2; + } + mBuilder.append(mText); + int end = mBuilder.length(); + if (verticalAlign != -1) { + mBuilder.setSpan(new VerticalAlignSpan(verticalAlign), start, end, flag); + } + if (foregroundColor != COLOR_DEFAULT) { + mBuilder.setSpan(new ForegroundColorSpan(foregroundColor), start, end, flag); + } + if (backgroundColor != COLOR_DEFAULT) { + mBuilder.setSpan(new BackgroundColorSpan(backgroundColor), start, end, flag); + } + if (first != -1) { + mBuilder.setSpan(new LeadingMarginSpan.Standard(first, rest), start, end, flag); + } + if (quoteColor != COLOR_DEFAULT) { + mBuilder.setSpan(new CustomQuoteSpan(quoteColor, stripeWidth, quoteGapWidth), start, end, flag); + } + if (bulletColor != COLOR_DEFAULT) { + mBuilder.setSpan(new CustomBulletSpan(bulletColor, bulletRadius, bulletGapWidth), start, end, flag); + } + if (fontSize != -1) { + mBuilder.setSpan(new AbsoluteSizeSpan(fontSize, fontSizeIsDp), start, end, flag); + } + if (proportion != -1) { + mBuilder.setSpan(new RelativeSizeSpan(proportion), start, end, flag); + } + if (xProportion != -1) { + mBuilder.setSpan(new ScaleXSpan(xProportion), start, end, flag); + } + if (lineHeight != -1) { + mBuilder.setSpan(new CustomLineHeightSpan(lineHeight, alignLine), start, end, flag); + } + if (isStrikethrough) { + mBuilder.setSpan(new StrikethroughSpan(), start, end, flag); + } + if (isUnderline) { + mBuilder.setSpan(new UnderlineSpan(), start, end, flag); + } + if (isSuperscript) { + mBuilder.setSpan(new SuperscriptSpan(), start, end, flag); + } + if (isSubscript) { + mBuilder.setSpan(new SubscriptSpan(), start, end, flag); + } + if (isBold) { + mBuilder.setSpan(new StyleSpan(Typeface.BOLD), start, end, flag); + } + if (isItalic) { + mBuilder.setSpan(new StyleSpan(Typeface.ITALIC), start, end, flag); + } + if (isBoldItalic) { + mBuilder.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), start, end, flag); + } + if (fontFamily != null) { + mBuilder.setSpan(new TypefaceSpan(fontFamily), start, end, flag); + } + if (typeface != null) { + mBuilder.setSpan(new CustomTypefaceSpan(typeface), start, end, flag); + } + if (alignment != null) { + mBuilder.setSpan(new AlignmentSpan.Standard(alignment), start, end, flag); + } + if (clickSpan != null) { + mBuilder.setSpan(clickSpan, start, end, flag); + } + if (url != null) { + mBuilder.setSpan(new URLSpan(url), start, end, flag); + } + if (blurRadius != -1) { + mBuilder.setSpan(new MaskFilterSpan(new BlurMaskFilter(blurRadius, style)), start, end, flag); + } + if (shader != null) { + mBuilder.setSpan(new ShaderSpan(shader), start, end, flag); + } + if (shadowRadius != -1) { + mBuilder.setSpan(new ShadowSpan(shadowRadius, shadowDx, shadowDy, shadowColor), start, end, flag); + } + if (spans != null) { + for (Object span : spans) { + mBuilder.setSpan(span, start, end, flag); + } + } + } + + /** + * 更新 Image + */ + private void updateImage() { + int start = mBuilder.length(); + mText = ""; + updateCharCharSequence(); + int end = mBuilder.length(); + if (imageBitmap != null) { + mBuilder.setSpan(new CustomImageSpan(imageBitmap, alignImage), start, end, flag); + } else if (imageDrawable != null) { + mBuilder.setSpan(new CustomImageSpan(imageDrawable, alignImage), start, end, flag); + } else if (imageUri != null) { + mBuilder.setSpan(new CustomImageSpan(imageUri, alignImage), start, end, flag); + } else if (imageResourceId != -1) { + mBuilder.setSpan(new CustomImageSpan(imageResourceId, alignImage), start, end, flag); + } + } + + /** + * 更新 Span + */ + private void updateSpace() { + int start = mBuilder.length(); + mText = "< >"; + updateCharCharSequence(); + int end = mBuilder.length(); + mBuilder.setSpan(new SpaceSpan(spaceSize, spaceColor), start, end, flag); + } + + /** + * 初始化默认值 + */ + private void setDefault() { + flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; + foregroundColor = COLOR_DEFAULT; + backgroundColor = COLOR_DEFAULT; + lineHeight = -1; + quoteColor = COLOR_DEFAULT; + first = -1; + bulletColor = COLOR_DEFAULT; + fontSize = -1; + proportion = -1; + xProportion = -1; + isStrikethrough = false; + isUnderline = false; + isSuperscript = false; + isSubscript = false; + isBold = false; + isItalic = false; + isBoldItalic = false; + fontFamily = null; + typeface = null; + alignment = null; + verticalAlign = -1; + clickSpan = null; + url = null; + blurRadius = -1; + shader = null; + shadowRadius = -1; + spaceSize = -1; + spans = null; + + imageBitmap = null; + imageDrawable = null; + imageUri = null; + imageResourceId = -1; + } + + // ============================ + // = 内部自定义 Span 效果实现 = + // ============================ + + /** + * detail: 垂直对齐 Span + * @author Ttt + */ + static class VerticalAlignSpan extends ReplacementSpan { + + static final int ALIGN_CENTER = 2; + static final int ALIGN_TOP = 3; + + final int mVerticalAlignment; + + VerticalAlignSpan(int verticalAlignment) { + mVerticalAlignment = verticalAlignment; + } + + @Override + public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) { + text = text.subSequence(start, end); + return (int) paint.measureText(text.toString()); + } + + @Override + public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + text = text.subSequence(start, end); + Paint.FontMetricsInt fm = paint.getFontMetricsInt(); + canvas.drawText(text.toString(), x, y - ((y + fm.descent + y + fm.ascent) / 2 - (bottom + top) / 2), paint); + } + } + + /** + * detail: 行高 Span + * @author Ttt + */ + static class CustomLineHeightSpan implements LineHeightSpan { + + private final int height; + + static final int ALIGN_CENTER = 2; + static final int ALIGN_TOP = 3; + + final int mVerticalAlignment; + static Paint.FontMetricsInt sfm; + + CustomLineHeightSpan(int height, int verticalAlignment) { + this.height = height; + mVerticalAlignment = verticalAlignment; + } + + @Override + public void chooseHeight(final CharSequence text, final int start, final int end, + final int spanstartv, final int v, final Paint.FontMetricsInt fm) { + if (sfm == null) { + sfm = new Paint.FontMetricsInt(); + sfm.top = fm.top; + sfm.ascent = fm.ascent; + sfm.descent = fm.descent; + sfm.bottom = fm.bottom; + sfm.leading = fm.leading; + } else { + fm.top = sfm.top; + fm.ascent = sfm.ascent; + fm.descent = sfm.descent; + fm.bottom = sfm.bottom; + fm.leading = sfm.leading; + } + int need = height - (v + fm.descent - fm.ascent - spanstartv); + if (need > 0) { + if (mVerticalAlignment == ALIGN_TOP) { + fm.descent += need; + } else if (mVerticalAlignment == ALIGN_CENTER) { + fm.descent += need / 2; + fm.ascent -= need / 2; + } else { + fm.ascent -= need; + } + } + need = height - (v + fm.bottom - fm.top - spanstartv); + if (need > 0) { + if (mVerticalAlignment == ALIGN_TOP) { + fm.bottom += need; + } else if (mVerticalAlignment == ALIGN_CENTER) { + fm.bottom += need / 2; + fm.top -= need / 2; + } else { + fm.top -= need; + } + } + if (end == ((Spanned) text).getSpanEnd(this)) { + sfm = null; + } + } + } + + /** + * detail: 空格 Span + * @author Ttt + */ + static class SpaceSpan extends ReplacementSpan { + + private final int width; + private final Paint paint = new Paint(); + + private SpaceSpan(final int width) { + this(width, Color.TRANSPARENT); + } + + private SpaceSpan(final int width, final int color) { + super(); + this.width = width; + paint.setColor(color); + paint.setStyle(Paint.Style.FILL); + } + + @Override + public int getSize(@NonNull final Paint paint, final CharSequence text, + @IntRange(from = 0) final int start, + @IntRange(from = 0) final int end, + @Nullable final Paint.FontMetricsInt fm) { + return width; + } + + @Override + public void draw(@NonNull final Canvas canvas, final CharSequence text, + @IntRange(from = 0) final int start, + @IntRange(from = 0) final int end, + final float x, final int top, final int y, final int bottom, + @NonNull final Paint paint) { + canvas.drawRect(x, top, x + width, bottom, this.paint); + } + } + + /** + * detail: 自定义引用样式 Span + * @author Ttt + */ + static class CustomQuoteSpan implements LeadingMarginSpan { + + private final int color; + private final int stripeWidth; + private final int gapWidth; + + private CustomQuoteSpan(final int color, final int stripeWidth, final int gapWidth) { + super(); + this.color = color; + this.stripeWidth = stripeWidth; + this.gapWidth = gapWidth; + } + + public int getLeadingMargin(final boolean first) { + return stripeWidth + gapWidth; + } + + public void drawLeadingMargin(final Canvas c, final Paint p, final int x, final int dir, + final int top, final int baseline, final int bottom, + final CharSequence text, final int start, final int end, + final boolean first, final Layout layout) { + Paint.Style style = p.getStyle(); + int color = p.getColor(); + + p.setStyle(Paint.Style.FILL); + p.setColor(this.color); + + c.drawRect(x, top, x + dir * stripeWidth, bottom, p); + + p.setStyle(style); + p.setColor(color); + } + } + + /** + * detail: 自定义列表项样式 Span + * @author Ttt + */ + static class CustomBulletSpan implements LeadingMarginSpan { + + private final int color; + private final int radius; + private final int gapWidth; + + private Path sBulletPath = null; + + private CustomBulletSpan(final int color, final int radius, final int gapWidth) { + this.color = color; + this.radius = radius; + this.gapWidth = gapWidth; + } + + public int getLeadingMargin(final boolean first) { + return 2 * radius + gapWidth; + } + + public void drawLeadingMargin(final Canvas c, final Paint p, final int x, final int dir, + final int top, final int baseline, final int bottom, + final CharSequence text, final int start, final int end, + final boolean first, final Layout l) { + if (((Spanned) text).getSpanStart(this) == start) { + Paint.Style style = p.getStyle(); + int oldColor = 0; + oldColor = p.getColor(); + p.setColor(color); + p.setStyle(Paint.Style.FILL); + if (c.isHardwareAccelerated()) { + if (sBulletPath == null) { + sBulletPath = new Path(); + // Bullet is slightly better to avoid aliasing artifacts on mdpi devices. + sBulletPath.addCircle(0.0f, 0.0f, radius, Path.Direction.CW); + } + c.save(); + c.translate(x + dir * radius, (top + bottom) / 2.0f); + c.drawPath(sBulletPath, p); + c.restore(); + } else { + c.drawCircle(x + dir * radius, (top + bottom) / 2.0f, radius, p); + } + p.setColor(oldColor); + p.setStyle(style); + } + } + } + + /** + * detail: 自定义字体样式 Span + * @author Ttt + */ + @SuppressLint("ParcelCreator") + static class CustomTypefaceSpan extends TypefaceSpan { + + private final Typeface newType; + + private CustomTypefaceSpan(final Typeface type) { + super(""); + newType = type; + } + + @Override + public void updateDrawState(final TextPaint textPaint) { + apply(textPaint, newType); + } + + @Override + public void updateMeasureState(final TextPaint paint) { + apply(paint, newType); + } + + private void apply(final Paint paint, final Typeface tf) { + int oldStyle; + Typeface old = paint.getTypeface(); + if (old == null) { + oldStyle = 0; + } else { + oldStyle = old.getStyle(); + } + + int fake = oldStyle & ~tf.getStyle(); + if ((fake & Typeface.BOLD) != 0) { + paint.setFakeBoldText(true); + } + + if ((fake & Typeface.ITALIC) != 0) { + paint.setTextSkewX(-0.25f); + } + + paint.getShader(); + paint.setTypeface(tf); + } + } + + /** + * detail: 自定义图片绘制效果 Span + * @author Ttt + */ + static class CustomImageSpan extends CustomDynamicDrawableSpan { + + private Drawable mDrawable; + private Uri mContentUri; + private int mResourceId; + + private CustomImageSpan(final Bitmap bitmap, final int verticalAlignment) { + super(verticalAlignment); + mDrawable = new BitmapDrawable(getContext().getResources(), bitmap); + mDrawable.setBounds(0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); + } + + private CustomImageSpan(final Drawable drawable, final int verticalAlignment) { + super(verticalAlignment); + mDrawable = drawable; + mDrawable.setBounds(0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); + } + + private CustomImageSpan(final Uri uri, final int verticalAlignment) { + super(verticalAlignment); + mContentUri = uri; + } + + private CustomImageSpan(@DrawableRes final int resourceId, final int verticalAlignment) { + super(verticalAlignment); + mResourceId = resourceId; + } + + @Override + public Drawable getDrawable() { + Drawable drawable = null; + if (mDrawable != null) { + drawable = mDrawable; + } else if (mContentUri != null) { + Bitmap bitmap; + try { + InputStream is = getContext().getContentResolver().openInputStream(mContentUri); + bitmap = BitmapFactory.decodeStream(is); + drawable = new BitmapDrawable(getContext().getResources(), bitmap); + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + if (is != null) { + is.close(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "Failed to loaded content " + mContentUri); + } + } else { + try { + drawable = ContextCompat.getDrawable(getContext(), mResourceId); + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "Unable to find resource: " + mResourceId); + } + } + return drawable; + } + } + + /** + * detail: 自定义 Drawable 绘制效果 Span + * @author Ttt + */ + static abstract class CustomDynamicDrawableSpan extends ReplacementSpan { + + static final int ALIGN_BOTTOM = 0; + static final int ALIGN_BASELINE = 1; + static final int ALIGN_CENTER = 2; + static final int ALIGN_TOP = 3; + final int mVerticalAlignment; + + private CustomDynamicDrawableSpan() { + mVerticalAlignment = ALIGN_BOTTOM; + } + + private CustomDynamicDrawableSpan(final int verticalAlignment) { + mVerticalAlignment = verticalAlignment; + } + + public abstract Drawable getDrawable(); + + @Override + public int getSize(@NonNull final Paint paint, final CharSequence text, + final int start, final int end, final Paint.FontMetricsInt fm) { + Drawable drawable = getCachedDrawable(); + Rect rect = drawable.getBounds(); + if (fm != null) { + int lineHeight = fm.bottom - fm.top; + if (lineHeight < rect.height()) { + if (mVerticalAlignment == ALIGN_TOP) { + fm.top = fm.top; + fm.bottom = rect.height() + fm.top; + } else if (mVerticalAlignment == ALIGN_CENTER) { + fm.top = -rect.height() / 2 - lineHeight / 4; + fm.bottom = rect.height() / 2 - lineHeight / 4; + } else { + fm.top = -rect.height() + fm.bottom; + fm.bottom = fm.bottom; + } + fm.ascent = fm.top; + fm.descent = fm.bottom; + } + } + return rect.right; + } + + @Override + public void draw(@NonNull final Canvas canvas, final CharSequence text, + final int start, final int end, final float x, + final int top, final int y, final int bottom, @NonNull final Paint paint) { + Drawable drawable = getCachedDrawable(); + Rect rect = drawable.getBounds(); + canvas.save(); + float transY; + int lineHeight = bottom - top; + if (rect.height() < lineHeight) { + if (mVerticalAlignment == ALIGN_TOP) { + transY = top; + } else if (mVerticalAlignment == ALIGN_CENTER) { + transY = (bottom + top - rect.height()) / 2; + } else if (mVerticalAlignment == ALIGN_BASELINE) { + transY = y - rect.height(); + } else { + transY = bottom - rect.height(); + } + canvas.translate(x, transY); + } else { + canvas.translate(x, top); + } + drawable.draw(canvas); + canvas.restore(); + } + + private Drawable getCachedDrawable() { + WeakReference wr = mDrawableRef; + Drawable drawable = null; + if (wr != null) { + drawable = wr.get(); + } + if (drawable == null) { + drawable = getDrawable(); + mDrawableRef = new WeakReference<>(drawable); + } + return drawable; + } + + private WeakReference mDrawableRef; + } + + /** + * detail: 着色效果 Span + * @author Ttt + */ + static class ShaderSpan extends CharacterStyle implements UpdateAppearance { + private Shader mShader; + + private ShaderSpan(final Shader shader) { + this.mShader = shader; + } + + @Override + public void updateDrawState(final TextPaint tp) { + tp.setShader(mShader); + } + } + + /** + * detail: 阴影效果 Span + * @author Ttt + */ + static class ShadowSpan extends CharacterStyle implements UpdateAppearance { + private float radius; + private float dx, dy; + private int shadowColor; + + private ShadowSpan(final float radius, final float dx, final float dy, final int shadowColor) { + this.radius = radius; + this.dx = dx; + this.dy = dy; + this.shadowColor = shadowColor; + } + + @Override + public void updateDrawState(final TextPaint tp) { + tp.setShadowLayer(radius, dx, dy, shadowColor); + } + } + + // ============ + // = 内部方法 = + // ============ + + /** + * detail: SpannableStringBuilder 内部类, 实现序列化方便传值 + * @author Ttt + */ + private static class SerializableSpannableStringBuilder extends SpannableStringBuilder + implements Serializable { + + private static final long serialVersionUID = 2514562037168478805L; + } + + /** + * 获取 Context + * @return {@link Context} + */ + private static Context getContext() { + return DevUtils.getContext(); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/StateListUtils.java b/lib/DevApp/src/main/java/dev/utils/app/StateListUtils.java new file mode 100644 index 0000000000..abb9984104 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/StateListUtils.java @@ -0,0 +1,341 @@ +package dev.utils.app; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.StateListDrawable; +import android.support.annotation.ColorRes; +import android.support.annotation.DrawableRes; +import android.support.v4.content.ContextCompat; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 颜色状态列表工具类 + * @author Ttt + *
+ *     android:state_active	        是否处于激活状态
+ *     android:state_checkable	    是否可勾选
+ *     android:state_checked	    是否已勾选
+ *     android:state_enabled	    是否可用
+ *     android:state_first	        是否开始状态
+ *     android:state_focused	    是否已获取焦点
+ *     android:state_last	        是否处于结束
+ *     android:state_middle	        是否处于中间
+ *     android:state_pressed	    是否处于按下状态
+ *     android:state_selected	    是否处于选中状态
+ *     android:state_window_focused	是否窗口已获取焦点
+ * 
+ */ +public final class StateListUtils { + + private StateListUtils() { + } + + // 日志 TAG + private static final String TAG = StateListUtils.class.getSimpleName(); + + /** + * 获取 ColorStateList + * @param id resource identifier of a {@link ColorStateList} + * @return {@link ColorStateList} + */ + public static ColorStateList getColorStateList(@ColorRes final int id) { + try { + return ContextCompat.getColorStateList(DevUtils.getContext(), id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getColorStateList"); + } + return null; + } + + // ================ + // = 设置字体颜色 = + // ================ + + // ============== + // = 字符串颜色 = + // ============== + + /** + * 创建 ColorStateList + *
+     *     createColorStateList("#ffffffff", "#ff44e6ff")
+     * 
+ * @param pressed 按下状态 + * @param normal 默认状态 + * @return {@link ColorStateList} + */ + public static ColorStateList createColorStateList(final String pressed, final String normal) { + // 颜色值 + int[] colors = new int[]{Color.parseColor(pressed), Color.parseColor(normal)}; + // 状态值 + int[][] states = new int[2][]; + states[0] = new int[]{android.R.attr.state_pressed}; // 选中状态 + states[1] = new int[]{}; // 默认状态 + // 生成 ColorStateList + return new ColorStateList(states, colors); + } + + /** + * 创建 ColorStateList + * @param selected 选中状态 + * @param pressed 按下状态 + * @param normal 默认状态 + * @return {@link ColorStateList} + */ + public static ColorStateList createColorStateList(final String selected, final String pressed, final String normal) { + // 颜色值 + int[] colors = new int[]{Color.parseColor(selected), Color.parseColor(pressed), Color.parseColor(normal)}; + // 状态值 + int[][] states = new int[3][]; + states[0] = new int[]{android.R.attr.state_selected}; // 选中状态 + states[1] = new int[]{android.R.attr.state_pressed}; // 点击状态 + states[2] = new int[]{}; // 默认状态 + // 生成 ColorStateList + return new ColorStateList(states, colors); + } + + /** + * 创建 ColorStateList + * @param selected 选中状态 + * @param pressed 按下状态 + * @param focused 获取焦点状态 + * @param checked 已勾选状态 + * @param normal 默认状态 + * @return {@link ColorStateList} + */ + public static ColorStateList createColorStateList(final String selected, final String pressed, + final String focused, final String checked, + final String normal) { + // 颜色值 + int[] colors = new int[]{Color.parseColor(selected), Color.parseColor(pressed), + Color.parseColor(focused), Color.parseColor(checked), Color.parseColor(normal)}; + // 状态值 + int[][] states = new int[5][]; + states[0] = new int[]{android.R.attr.state_selected}; // 选中状态 + states[1] = new int[]{android.R.attr.state_pressed}; // 点击状态 + states[2] = new int[]{android.R.attr.state_focused}; // 获取焦点状态 + states[3] = new int[]{android.R.attr.state_checked}; // 选中状态 + states[4] = new int[]{}; // 默认状态 + // 生成 ColorStateList + return new ColorStateList(states, colors); + } + + // ======= + // = int = + // ======= + + /** + * 创建 ColorStateList + *
+     *     createColorStateList(R.color.whilte, R.color.black)
+     * 
+ * @param pressed 按下状态 + * @param normal 默认状态 + * @return {@link ColorStateList} + */ + public static ColorStateList createColorStateList(@ColorRes final int pressed, @ColorRes final int normal) { + Context context = DevUtils.getContext(); + // 颜色值 + int[] colors = new int[2]; + colors[0] = ContextCompat.getColor(context, pressed); + colors[1] = ContextCompat.getColor(context, normal); + // 状态值 + int[][] states = new int[2][]; + states[0] = new int[]{android.R.attr.state_pressed}; + states[1] = new int[]{}; + // 生成 ColorStateList + return new ColorStateList(states, colors); + } + + /** + * 创建 ColorStateList + * @param selected 选中状态 + * @param pressed 按下状态 + * @param normal 默认状态 + * @return {@link ColorStateList} + */ + public static ColorStateList createColorStateList(@ColorRes final int selected, @ColorRes final int pressed, @ColorRes final int normal) { + Context context = DevUtils.getContext(); + // 颜色值 + int[] colors = new int[3]; + colors[0] = ContextCompat.getColor(context, selected); + colors[1] = ContextCompat.getColor(context, pressed); + colors[2] = ContextCompat.getColor(context, normal); + // 状态值 + int[][] states = new int[3][]; + states[0] = new int[]{android.R.attr.state_selected}; + states[1] = new int[]{android.R.attr.state_pressed}; + states[2] = new int[]{}; + // 生成 ColorStateList + return new ColorStateList(states, colors); + } + + /** + * 创建 ColorStateList + * @param selected 选中状态 + * @param pressed 按下状态 + * @param focused 获取焦点状态 + * @param checked 已勾选状态 + * @param normal 默认状态 + * @return {@link ColorStateList} + */ + public static ColorStateList createColorStateList(@ColorRes final int selected, @ColorRes final int pressed, + @ColorRes final int focused, @ColorRes final int checked, + @ColorRes final int normal) { + Context context = DevUtils.getContext(); + // 颜色值 + int[] colors = new int[5]; + colors[0] = ContextCompat.getColor(context, selected); + colors[1] = ContextCompat.getColor(context, pressed); + colors[2] = ContextCompat.getColor(context, focused); + colors[3] = ContextCompat.getColor(context, checked); + colors[4] = ContextCompat.getColor(context, normal); + // 状态值 + int[][] states = new int[5][]; + states[0] = new int[]{android.R.attr.state_selected}; + states[1] = new int[]{android.R.attr.state_pressed}; + states[2] = new int[]{android.R.attr.state_focused}; + states[3] = new int[]{android.R.attr.state_checked}; + states[4] = new int[]{}; + // 生成 ColorStateList + return new ColorStateList(states, colors); + } + + // ========================= + // = 设置背景切换 Drawable = + // ========================= + + /** + * 创建 StateListDrawable + *
+     *     view.setBackground(Drawable)
+     * 
+ * @param pressed 按下状态 + * @param normal 默认状态 + * @return {@link StateListDrawable} + */ + public static StateListDrawable newSelector(@DrawableRes final int pressed, @DrawableRes final int normal) { + Context context = DevUtils.getContext(); + // 获取 Drawable + Drawable pressedDraw = ContextCompat.getDrawable(context, pressed); + Drawable normalDraw = ContextCompat.getDrawable(context, normal); + // 创建 StateListDrawable + StateListDrawable stateListDrawable = new StateListDrawable(); + stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressedDraw); + stateListDrawable.addState(new int[]{}, normalDraw); + return stateListDrawable; + } + + /** + * 创建 StateListDrawable + * @param selected 选中状态 + * @param pressed 按下状态 + * @param normal 默认状态 + * @return {@link StateListDrawable} + */ + public static StateListDrawable newSelector(@DrawableRes final int selected, @DrawableRes final int pressed, @DrawableRes final int normal) { + Context context = DevUtils.getContext(); + // 获取 Drawable + Drawable selectedDraw = ContextCompat.getDrawable(context, selected); + Drawable pressedDraw = ContextCompat.getDrawable(context, pressed); + Drawable normalDraw = ContextCompat.getDrawable(context, normal); + // 创建 StateListDrawable + StateListDrawable stateListDrawable = new StateListDrawable(); + stateListDrawable.addState(new int[]{android.R.attr.state_selected}, selectedDraw); + stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressedDraw); + stateListDrawable.addState(new int[]{}, normalDraw); + return stateListDrawable; + } + + /** + * 创建 StateListDrawable + * @param selected 选中状态 + * @param pressed 按下状态 + * @param focused 获取焦点状态 + * @param checked 已勾选状态 + * @param normal 默认状态 + * @return {@link StateListDrawable} + */ + public static StateListDrawable newSelector(@DrawableRes final int selected, @DrawableRes final int pressed, + @DrawableRes final int focused, @DrawableRes final int checked, + @DrawableRes final int normal) { + Context context = DevUtils.getContext(); + // 获取 Drawable + Drawable selectedDraw = ContextCompat.getDrawable(context, selected); + Drawable pressedDraw = ContextCompat.getDrawable(context, pressed); + Drawable focusedDraw = ContextCompat.getDrawable(context, focused); + Drawable checkedDraw = ContextCompat.getDrawable(context, checked); + Drawable normalDraw = ContextCompat.getDrawable(context, normal); + // 创建 StateListDrawable + StateListDrawable stateListDrawable = new StateListDrawable(); + stateListDrawable.addState(new int[]{android.R.attr.state_selected}, selectedDraw); + stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressedDraw); + stateListDrawable.addState(new int[]{android.R.attr.state_focused}, focusedDraw); + stateListDrawable.addState(new int[]{android.R.attr.state_checked}, checkedDraw); + stateListDrawable.addState(new int[]{}, normalDraw); + return stateListDrawable; + } + + // ========================= + // = 设置背景切换 Drawable = + // ========================= + + /** + * 创建 StateListDrawable + *
+     *     view.setBackground(Drawable)
+     * 
+ * @param pressed 按下状态 + * @param normal 默认状态 + * @return {@link StateListDrawable} + */ + public static StateListDrawable newSelector(final Drawable pressed, final Drawable normal) { + // 创建 StateListDrawable + StateListDrawable stateListDrawable = new StateListDrawable(); + stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressed); + stateListDrawable.addState(new int[]{}, normal); + return stateListDrawable; + } + + /** + * 创建 StateListDrawable + * @param selected 选中状态 + * @param pressed 按下状态 + * @param normal 默认状态 + * @return {@link StateListDrawable} + */ + public static StateListDrawable newSelector(final Drawable selected, final Drawable pressed, final Drawable normal) { + // 创建 StateListDrawable + StateListDrawable stateListDrawable = new StateListDrawable(); + stateListDrawable.addState(new int[]{android.R.attr.state_selected}, selected); + stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressed); + stateListDrawable.addState(new int[]{}, normal); + return stateListDrawable; + } + + /** + * 创建 StateListDrawable + * @param selected 选中状态 + * @param pressed 按下状态 + * @param focused 获取焦点状态 + * @param checked 已勾选状态 + * @param normal 默认状态 + * @return {@link StateListDrawable} + */ + public static StateListDrawable newSelector(final Drawable selected, final Drawable pressed, + final Drawable focused, final Drawable checked, + final Drawable normal) { + // 创建 StateListDrawable + StateListDrawable stateListDrawable = new StateListDrawable(); + stateListDrawable.addState(new int[]{android.R.attr.state_selected}, selected); + stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressed); + stateListDrawable.addState(new int[]{android.R.attr.state_focused}, focused); + stateListDrawable.addState(new int[]{android.R.attr.state_checked}, checked); + stateListDrawable.addState(new int[]{}, normal); + return stateListDrawable; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/TextViewUtils.java b/lib/DevApp/src/main/java/dev/utils/app/TextViewUtils.java new file mode 100644 index 0000000000..1004b7a774 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/TextViewUtils.java @@ -0,0 +1,1871 @@ +package dev.utils.app; + +import android.content.res.ColorStateList; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.RequiresApi; +import android.text.Html; +import android.text.InputFilter; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.View; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +import dev.utils.LogPrintUtils; + +/** + * detail: TextView 工具类 + * @author Ttt + *
+ *     获取字体信息 Paint.FontMetrics
+ *     @see 
+ *     @see 
+ *     

+ * TextView 设置行间距、行高, 以及字间距 + * @see
+ *

+ * android:includeFontPadding + * @see
+ *

+ * 设置文字水平间距: {@link TextViewUtils#setLetterSpacing(View, float)} + * android:letterSpacing + * 设置文字行间距 ( 行高 ): {@link TextViewUtils#setLineSpacing(View, float)}、{@link TextViewUtils#setLineSpacingAndMultiplier(View, float, float)} + * android:lineSpacingExtra + * android:lineSpacingMultiplier + *

+ * setPaintFlags: + * Paint.ANTI_ALIAS_FLAG 抗锯齿标志 + * Paint.FILTER_BITMAP_FLAG 使位图过滤的位掩码标志 + * Paint.DITHER_FLAG 使位图进行有利的抖动的位掩码标志 + * Paint.UNDERLINE_TEXT_FLAG 下划线 + * Paint.STRIKE_THRU_TEXT_FLAG 中划线 + * Paint.FAKE_BOLD_TEXT_FLAG 加粗 + * Paint.LINEAR_TEXT_FLAG 使文本平滑线性扩展的油漆标志 + * Paint.SUBPIXEL_TEXT_FLAG 使文本的亚像素定位的绘图标志 + * Paint.EMBEDDED_BITMAP_TEXT_FLAG 绘制文本时允许使用位图字体的绘图标志 + *
+ */ +public final class TextViewUtils { + + private TextViewUtils() { + } + + // 日志 TAG + private static final String TAG = TextViewUtils.class.getSimpleName(); + + // ================= + // = 获取 TextView = + // ================= + + /** + * 获取 TextView + * @param view {@link View} + * @param 泛型 + * @return {@link TextView} + */ + public static T getTextView(final View view) { + if (view != null) { + try { + return (T) view; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTextView"); + } + } + return null; + } + + // ======== + // = Text = + // ======== + + /** + * 获取 Hint 文本 + * @param textView {@link TextView} + * @param 泛型 + * @return {@link TextView#getHint()} + */ + public static String getHint(final T textView) { + if (textView != null) { + if (textView.getHint() != null) { + return textView.getHint().toString(); + } + } + return null; + } + + /** + * 获取 Hint 文本 + * @param view {@link TextView} + * @return {@link TextView#getHint()} + */ + public static String getHint(final View view) { + return getHint(getTextView(view)); + } + + /** + * 获取文本 + * @param textView {@link TextView} + * @param 泛型 + * @return {@link TextView#getText()} + */ + public static String getText(final T textView) { + if (textView != null) { + return textView.getText().toString(); + } + return null; + } + + /** + * 获取文本 + * @param view {@link TextView} + * @return {@link TextView#getText()} + */ + public static String getText(final View view) { + return getText(getTextView(view)); + } + + // = + + /** + * 获取多个 TextView Hint 文本 + * @param views View(TextView)[] + * @return {@link List} 多个 TextView Hint 文本 + */ + public static List getHints(final View... views) { + List lists = new ArrayList<>(); + if (views != null) { + for (View view : views) { + String text = getHint(view); + if (text != null) { + lists.add(text); + } + } + } + return lists; + } + + /** + * 获取多个 TextView Hint 文本 + * @param views TextView[] + * @param 泛型 + * @return {@link List} 多个 TextView Hint 文本 + */ + public static List getHints(final T... views) { + List lists = new ArrayList<>(); + if (views != null) { + for (T view : views) { + String text = getHint(view); + if (text != null) { + lists.add(text); + } + } + } + return lists; + } + + /** + * 获取多个 TextView 文本 + * @param views View(TextView)[] + * @return {@link List} 多个 TextView 文本 + */ + public static List getTexts(final View... views) { + List lists = new ArrayList<>(); + if (views != null) { + for (View view : views) { + String text = getText(view); + if (text != null) { + lists.add(text); + } + } + } + return lists; + } + + /** + * 获取多个 TextView 文本 + * @param views TextView[] + * @param 泛型 + * @return {@link List} 多个 TextView 文本 + */ + public static List getTexts(final T... views) { + List lists = new ArrayList<>(); + if (views != null) { + for (T view : views) { + String text = getText(view); + if (text != null) { + lists.add(text); + } + } + } + return lists; + } + + // = + + /** + * 设置 Hint 文本 + * @param textView {@link TextView} + * @param text Hint text + * @param 泛型 + */ + public static void setHint(final T textView, final String text) { + if (textView != null) { + textView.setHint(text); + } + } + + /** + * 设置 Hint 文本 + * @param view {@link TextView} + * @param text Hint text + * @return View + */ + public static View setHint(final View view, final String text) { + setHint(getTextView(view), text); + return view; + } + + /** + * 设置文本 + * @param textView {@link TextView} + * @param text TextView text + * @param 泛型 + */ + public static void setText(final T textView, final String text) { + if (textView != null) { + textView.setText(text); + } + } + + /** + * 设置文本 + * @param view {@link TextView} + * @param text TextView text + * @return View + */ + public static View setText(final View view, final String text) { + setText(getTextView(view), text); + return view; + } + + // = + + /** + * 设置多个 TextView 文本 + * @param text TextView text + * @param views View(TextView)[] + */ + public static void setTexts(final String text, final View... views) { + if (views != null) { + for (View view : views) { + setText(view, text); + } + } + } + + /** + * 设置多个 TextView 文本 + * @param text TextView text + * @param views TextView[] + * @param 泛型 + */ + public static void setTexts(final String text, final T... views) { + if (views != null) { + for (T view : views) { + setText(view, text); + } + } + } + + // = + + /** + * 设置 Html 内容 + * @param textView {@link TextView} + * @param content Html content + * @param 泛型 + */ + public static void setHtmlText(final T textView, final String content) { + if (textView != null && content != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + textView.setText(Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY)); + } else { + textView.setText(Html.fromHtml(content)); + } + } + } + + /** + * 设置 Html 内容 + * @param view {@link TextView} + * @param content Html content + * @return View + */ + public static View setHtmlText(final View view, final String content) { + setHtmlText(getTextView(view), content); + return view; + } + + // = + + /** + * 设置多个 TextView Html 内容 + * @param content Html content + * @param views View(TextView)[] + */ + public static void setHtmlTexts(final String content, final View... views) { + if (content != null && views != null) { + for (View view : views) { + setHtmlText(view, content); + } + } + } + + /** + * 设置多个 TextView Html 内容 + * @param content Html content + * @param views TextView[] + * @param 泛型 + */ + public static void setHtmlTexts(final String content, final T... views) { + if (content != null && views != null) { + for (T view : views) { + setHtmlText(view, content); + } + } + } + + // ============ + // = 属性相关 = + // ============ + + /** + * 获取 Hint 字体颜色 + * @param textView {@link TextView} + * @param 泛型 + * @return {@link ColorStateList} + */ + public static ColorStateList getHintTextColors(final T textView) { + if (textView != null) { + return textView.getHintTextColors(); + } + return null; + } + + /** + * 获取 Hint 字体颜色 + * @param view {@link TextView} + * @return {@link ColorStateList} + */ + public static ColorStateList getHintTextColors(final View view) { + return getHintTextColors(getTextView(view)); + } + + // = + + /** + * 设置 Hint 字体颜色 + * @param textView {@link TextView} + * @param color R.color.id + * @param 泛型 + */ + public static void setHintTextColor(final T textView, @ColorInt final int color) { + if (textView != null) { + textView.setHintTextColor(color); + } + } + + /** + * 设置 Hint 字体颜色 + * @param view {@link TextView} + * @param color R.color.id + * @return View + */ + public static View setHintTextColor(final View view, @ColorInt final int color) { + setHintTextColor(getTextView(view), color); + return view; + } + + /** + * 设置 Hint 字体颜色 + * @param textView {@link TextView} + * @param colors {@link ColorStateList} + * @param 泛型 + */ + public static void setHintTextColor(final T textView, final ColorStateList colors) { + if (textView != null) { + textView.setHintTextColor(colors); + } + } + + /** + * 设置 Hint 字体颜色 + * @param view {@link TextView} + * @param colors {@link ColorStateList} + * @return View + */ + public static View setHintTextColor(final View view, final ColorStateList colors) { + setHintTextColor(getTextView(view), colors); + return view; + } + + // = + + /** + * 获取字体颜色 + * @param textView {@link TextView} + * @param 泛型 + * @return {@link ColorStateList} + */ + public static ColorStateList getTextColors(final T textView) { + if (textView != null) { + return textView.getTextColors(); + } + return null; + } + + /** + * 获取字体颜色 + * @param view {@link TextView} + * @return {@link ColorStateList} + */ + public static ColorStateList getTextColors(final View view) { + return getTextColors(getTextView(view)); + } + + // = + + /** + * 设置字体颜色 + * @param textView {@link TextView} + * @param color R.color.id + * @param 泛型 + */ + public static void setTextColor(final T textView, @ColorInt final int color) { + if (textView != null) { + textView.setTextColor(color); + } + } + + /** + * 设置字体颜色 + * @param view {@link TextView} + * @param color R.color.id + * @return View + */ + public static View setTextColor(final View view, @ColorInt final int color) { + setTextColor(getTextView(view), color); + return view; + } + + /** + * 设置字体颜色 + * @param textView {@link TextView} + * @param colors {@link ColorStateList} + * @param 泛型 + */ + public static void setTextColor(final T textView, final ColorStateList colors) { + if (textView != null) { + textView.setTextColor(colors); + } + } + + /** + * 设置字体颜色 + * @param view {@link TextView} + * @param colors {@link ColorStateList} + * @return View + */ + public static View setTextColor(final View view, final ColorStateList colors) { + setTextColor(getTextView(view), colors); + return view; + } + + // = + + /** + * 设置多个 TextView Hint 字体颜色 + * @param color R.color.id + * @param views View(TextView)[] + */ + public static void setHintTextColors(@ColorInt final int color, final View... views) { + if (views != null) { + for (View view : views) { + setHintTextColor(view, color); + } + } + } + + /** + * 设置多个 TextView Hint 字体颜色 + * @param color R.color.id + * @param views TextView[] + * @param 泛型 + */ + public static void setHintTextColors(@ColorInt final int color, final T... views) { + if (views != null) { + for (T view : views) { + setHintTextColor(view, color); + } + } + } + + /** + * 设置多个 TextView Hint 字体颜色 + * @param colors {@link ColorStateList} + * @param views View(TextView)[] + */ + public static void setHintTextColors(final ColorStateList colors, final View... views) { + if (views != null) { + for (View view : views) { + setHintTextColor(view, colors); + } + } + } + + /** + * 设置多个 TextView Hint 字体颜色 + * @param colors {@link ColorStateList} + * @param views TextView[] + * @param 泛型 + */ + public static void setHintTextColors(final ColorStateList colors, final T... views) { + if (views != null) { + for (T view : views) { + setHintTextColor(view, colors); + } + } + } + + // = + + /** + * 设置多个 TextView 字体颜色 + * @param color R.color.id + * @param views View(TextView)[] + */ + public static void setTextColors(@ColorInt final int color, final View... views) { + if (views != null) { + for (View view : views) { + setTextColor(view, color); + } + } + } + + /** + * 设置多个 TextView 字体颜色 + * @param color R.color.id + * @param views TextView[] + * @param 泛型 + */ + public static void setTextColors(@ColorInt final int color, final T... views) { + if (views != null) { + for (T view : views) { + setTextColor(view, color); + } + } + } + + /** + * 设置多个 TextView 字体颜色 + * @param colors {@link ColorStateList} + * @param views View(TextView)[] + */ + public static void setTextColors(final ColorStateList colors, final View... views) { + if (views != null) { + for (View view : views) { + setTextColor(view, colors); + } + } + } + + /** + * 设置多个 TextView 字体颜色 + * @param colors {@link ColorStateList} + * @param views TextView[] + * @param 泛型 + */ + public static void setTextColors(final ColorStateList colors, final T... views) { + if (views != null) { + for (T view : views) { + setTextColor(view, colors); + } + } + } + + // ============ + // = 字体相关 = + // ============ + + /** + * 设置字体 + * @param textView {@link TextView} + * @param typeface {@link Typeface} 字体样式 + * @param 泛型 + */ + public static void setTypeface(final T textView, final Typeface typeface) { + if (textView != null && typeface != null) { + textView.setTypeface(typeface); + } + } + + /** + * 设置字体 + * @param textView {@link TextView} + * @param typeface {@link Typeface} 字体样式 + * @param style 样式 + * @param 泛型 + */ + public static void setTypeface(final T textView, final Typeface typeface, final int style) { + if (textView != null && typeface != null) { + textView.setTypeface(typeface, style); + } + } + + /** + * 设置字体 + * @param view {@link TextView} + * @param typeface {@link Typeface} 字体样式 + * @return View + */ + public static View setTypeface(final View view, final Typeface typeface) { + setTypeface(getTextView(view), typeface); + return view; + } + + /** + * 设置字体 + * @param view {@link TextView} + * @param typeface {@link Typeface} 字体样式 + * @param style 样式 + * @return View + */ + public static View setTypeface(final View view, final Typeface typeface, final int style) { + setTypeface(getTextView(view), typeface, style); + return view; + } + + // = + + /** + * 设置字体大小 - px 像素 + * @param textView {@link TextView} + * @param size 字体大小 + * @param 泛型 + */ + public static void setTextSizeByPx(final T textView, final float size) { + setTextSize(textView, TypedValue.COMPLEX_UNIT_PX, size); + } + + /** + * 设置字体大小 - sp 缩放像素 + * @param textView {@link TextView} + * @param size 字体大小 + * @param 泛型 + */ + public static void setTextSizeBySp(final T textView, final float size) { + setTextSize(textView, TypedValue.COMPLEX_UNIT_SP, size); + } + + /** + * 设置字体大小 - dp 与设备无关的像素 + * @param textView {@link TextView} + * @param size 字体大小 + * @param 泛型 + */ + public static void setTextSizeByDp(final T textView, final float size) { + setTextSize(textView, TypedValue.COMPLEX_UNIT_DIP, size); + } + + /** + * 设置字体大小 - inches 英寸 + * @param textView {@link TextView} + * @param size 字体大小 + * @param 泛型 + */ + public static void setTextSizeByIn(final T textView, final float size) { + setTextSize(textView, TypedValue.COMPLEX_UNIT_IN, size); + } + + // = + + /** + * 设置字体大小 - px 像素 + * @param view {@link TextView} + * @param size 字体大小 + * @return View + */ + public static View setTextSizeByPx(final View view, final float size) { + setTextSize(getTextView(view), TypedValue.COMPLEX_UNIT_PX, size); + return view; + } + + /** + * 设置字体大小 - sp 缩放像素 + * @param view {@link TextView} + * @param size 字体大小 + * @return View + */ + public static View setTextSizeBySp(final View view, final float size) { + setTextSize(getTextView(view), TypedValue.COMPLEX_UNIT_SP, size); + return view; + } + + /** + * 设置字体大小 - dp 与设备无关的像素 + * @param view {@link TextView} + * @param size 字体大小 + * @return View + */ + public static View setTextSizeByDp(final View view, final float size) { + setTextSize(getTextView(view), TypedValue.COMPLEX_UNIT_DIP, size); + return view; + } + + /** + * 设置字体大小 - inches 英寸 + * @param view {@link TextView} + * @param size 字体大小 + * @return View + */ + public static View setTextSizeByIn(final View view, final float size) { + setTextSize(getTextView(view), TypedValue.COMPLEX_UNIT_IN, size); + return view; + } + + // = + + /** + * 设置字体大小 + * @param textView {@link TextView} + * @param unit 字体参数类型 + * @param size 字体大小 + * @param 泛型 + */ + public static void setTextSize(final T textView, final int unit, final float size) { + if (textView != null) { + textView.setTextSize(unit, size); + } + } + + /** + * 设置字体大小 + * @param view {@link TextView} + * @param unit 字体参数类型 + * @param size 字体大小 + * @return View + */ + public static View setTextSize(final View view, final int unit, final float size) { + setTextSize(getTextView(view), unit, size); + return view; + } + + // = + + /** + * 设置多个 TextView 字体大小 + * @param views View(TextView)[] + * @param unit 参数类型 + * @param size 字体大小 + */ + public static void setTextSizes(final View[] views, final int unit, final float size) { + if (views != null) { + for (View view : views) { + setTextSize(view, unit, size); + } + } + } + + /** + * 设置多个 TextView 字体大小 + * @param views TextView[] + * @param unit 参数类型 + * @param size 字体大小 + * @param 泛型 + */ + public static void setTextSizes(final T[] views, final int unit, final float size) { + if (views != null) { + for (T view : views) { + setTextSize(view, unit, size); + } + } + } + + // = + + /** + * 获取 TextView 字体大小 - px + * @param textView {@link TextView} + * @param 泛型 + * @return 字体大小 (px) + */ + public static float getTextSize(final T textView) { + if (textView != null) { + return textView.getTextSize(); + } + return -1f; + } + + /** + * 获取 TextView 字体大小 - px + * @param view {@link TextView} + * @return 字体大小 (px) + */ + public static float getTextSize(final View view) { + return getTextSize(getTextView(view)); + } + + // = + + /** + * 清空 flags + * @param textView {@link TextView} + * @param 泛型 + */ + public static void clearFlags(final T textView) { + if (textView != null) { + textView.setPaintFlags(0); + } + } + + /** + * 清空 flags + * @param view {@link TextView} + * @return View + */ + public static View clearFlags(final View view) { + clearFlags(getTextView(view)); + return view; + } + + // = + + /** + * 设置 TextView 是否加粗 + * @param textView {@link TextView} + * @param isBold {@code true} yes, {@code false} no + * @param 泛型 + */ + public static void setBold(final T textView, final boolean isBold) { + if (textView != null) { + textView.setTypeface(Typeface.defaultFromStyle(isBold ? Typeface.BOLD : Typeface.NORMAL)); + } + } + + /** + * 设置 TextView 是否加粗 + * @param textView {@link TextView} + * @param typeface {@link Typeface} 字体样式 + * @param isBold {@code true} yes, {@code false} no + * @param 泛型 + */ + public static void setBold(final T textView, final Typeface typeface, final boolean isBold) { + if (textView != null && typeface != null) { + textView.setTypeface(typeface, isBold ? Typeface.BOLD : Typeface.NORMAL); + } + } + + /** + * 设置 TextView 是否加粗 + * @param view {@link TextView} + * @param isBold {@code true} yes, {@code false} no + * @return View + */ + public static View setBold(final View view, final boolean isBold) { + setBold(getTextView(view), isBold); + return view; + } + + /** + * 设置 TextView 是否加粗 + * @param view {@link TextView} + * @param typeface {@link Typeface} 字体样式 + * @param isBold {@code true} yes, {@code false} no + * @return View + */ + public static View setBold(final View view, final Typeface typeface, final boolean isBold) { + setBold(getTextView(view), typeface, isBold); + return view; + } + + // = + + /** + * 设置下划线 + * @param textView {@link TextView} + * @param 泛型 + */ + public static void setUnderlineText(final T textView) { + setUnderlineText(textView, true); + } + + /** + * 设置下划线并加清晰 + * @param textView {@link TextView} + * @param isAntiAlias 是否消除锯齿 + * @param 泛型 + */ + public static void setUnderlineText(final T textView, final boolean isAntiAlias) { + if (textView != null) { + textView.setPaintFlags(textView.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); + if (isAntiAlias) { + textView.setPaintFlags(textView.getPaintFlags() | Paint.ANTI_ALIAS_FLAG); + } + } + } + + // = + + /** + * 设置下划线 + * @param view {@link TextView} + * @return View + */ + public static View setUnderlineText(final View view) { + setUnderlineText(getTextView(view), true); + return view; + } + + /** + * 设置下划线并加清晰 + * @param view {@link TextView} + * @param isAntiAlias 是否消除锯齿 + * @return View + */ + public static View setUnderlineText(final View view, final boolean isAntiAlias) { + setUnderlineText(getTextView(view), isAntiAlias); + return view; + } + + // = + + /** + * 设置中划线 + * @param textView {@link TextView} + * @param 泛型 + */ + public static void setStrikeThruText(final T textView) { + setStrikeThruText(textView, true); + } + + /** + * 设置中划线并加清晰 + * @param textView {@link TextView} + * @param isAntiAlias 是否消除锯齿 + * @param 泛型 + */ + public static void setStrikeThruText(final T textView, final boolean isAntiAlias) { + if (textView != null) { + textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + if (isAntiAlias) { + textView.setPaintFlags(textView.getPaintFlags() | Paint.ANTI_ALIAS_FLAG); + } + } + } + + // = + + /** + * 设置中划线 + * @param view {@link TextView} + * @return View + */ + public static View setStrikeThruText(final View view) { + setStrikeThruText(getTextView(view), true); + return view; + } + + /** + * 设置中划线并加清晰 + * @param view {@link TextView} + * @param isAntiAlias 是否消除锯齿 + * @return View + */ + public static View setStrikeThruText(final View view, final boolean isAntiAlias) { + setStrikeThruText(getTextView(view), isAntiAlias); + return view; + } + + // = + + /** + * 设置文字水平间距 + *
+     *     android:letterSpacing
+     * 
+ * @param textView {@link TextView} + * @param letterSpacing 文字水平间距值 + * @param 泛型 + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static void setLetterSpacing(final T textView, final float letterSpacing) { + if (textView != null) { + textView.setLetterSpacing(letterSpacing); + } + } + + /** + * 设置文字水平间距 + * @param view {@link TextView} + * @param letterSpacing 文字水平间距值 + * @return View + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public static View setLetterSpacing(final View view, final float letterSpacing) { + setLetterSpacing(getTextView(view), letterSpacing); + return view; + } + + // = + + /** + * 设置文字行间距 ( 行高 ) + * @param textView {@link TextView} + * @param lineSpacing 文字行间距 ( 行高 ), android:lineSpacingExtra + * @param 泛型 + */ + public static void setLineSpacing(final T textView, final float lineSpacing) { + setLineSpacingAndMultiplier(textView, lineSpacing, 1.0f); + } + + /** + * 设置文字行间距 ( 行高 )、行间距倍数 + * @param textView {@link TextView} + * @param lineSpacing 文字行间距 ( 行高 ), android:lineSpacingExtra + * @param multiplier 行间距倍数, android:lineSpacingMultiplier + * @param 泛型 + */ + public static void setLineSpacingAndMultiplier(final T textView, final float lineSpacing, final float multiplier) { + if (textView != null) { + textView.setLineSpacing(lineSpacing, multiplier); + } + } + + // = + + /** + * 设置文字行间距 ( 行高 ) + * @param view {@link TextView} + * @param lineSpacing 文字行间距 ( 行高 ), android:lineSpacingExtra + * @return View + */ + public static View setLineSpacing(final View view, final float lineSpacing) { + setLineSpacingAndMultiplier(getTextView(view), lineSpacing, 1.0f); + return view; + } + + /** + * 设置文字行间距 ( 行高 )、行间距倍数 + * @param view {@link TextView} + * @param lineSpacing 文字行间距 ( 行高 ), android:lineSpacingExtra + * @param multiplier 行间距倍数, android:lineSpacingMultiplier + * @return View + */ + public static View setLineSpacingAndMultiplier(final View view, final float lineSpacing, final float multiplier) { + setLineSpacingAndMultiplier(getTextView(view), lineSpacing, multiplier); + return view; + } + + // = + + /** + * 设置字体水平方向的缩放 + *
+     *     android:textScaleX
+     * 
+ * @param textView {@link TextView} + * @param size 缩放比例 + * @param 泛型 + */ + public static void setTextScaleX(final T textView, final float size) { + if (textView != null) { + textView.setTextScaleX(size); + } + } + + /** + * 设置字体水平方向的缩放 + * @param view {@link TextView} + * @param size 缩放比例 + * @return View + */ + public static View setTextScaleX(final View view, final float size) { + setTextScaleX(getTextView(view), size); + return view; + } + + // = + + /** + * 设置是否保留字体留白间隙区域 + *
+     *     android:includeFontPadding
+     * 
+ * @param textView {@link TextView} + * @param includepad 是否保留字体留白间隙区域 + * @param 泛型 + */ + public static void setIncludeFontPadding(final T textView, final boolean includepad) { + if (textView != null) { + textView.setIncludeFontPadding(includepad); + } + } + + /** + * 设置是否保留字体留白间隙区域 + * @param view {@link TextView} + * @param includepad 是否保留字体留白间隙区域 + * @return View + */ + public static View setIncludeFontPadding(final View view, final boolean includepad) { + setIncludeFontPadding(getTextView(view), includepad); + return view; + } + + // = + + /** + * 设置输入类型 + * @param textView {@link TextView} + * @param type 类型 + * @param 泛型 + * @return {@link TextView} + */ + public static T setInputType(final T textView, final int type) { + if (textView != null) { + textView.setInputType(type); + } + return textView; + } + + /** + * 设置输入类型 + * @param view {@link TextView} + * @param type 类型 + * @return View + */ + public static View setInputType(final View view, final int type) { + setInputType(getTextView(view), type); + return view; + } + + // = + + /** + * 设置软键盘右下角按钮类型 + * @param textView {@link TextView} + * @param imeOptions 软键盘按钮类型 + * @param 泛型 + * @return {@link TextView} + */ + public static T setImeOptions(final T textView, final int imeOptions) { + if (textView != null) { + textView.setImeOptions(imeOptions); + } + return textView; + } + + /** + * 设置软键盘右下角按钮类型 + * @param view {@link TextView} + * @param imeOptions 软键盘按钮类型 + * @return View + */ + public static View setImeOptions(final View view, final int imeOptions) { + setImeOptions(getTextView(view), imeOptions); + return view; + } + + // = + + /** + * 设置行数 + * @param textView {@link TextView} + * @param lines 行数 + * @param 泛型 + */ + public static void setLines(final T textView, final int lines) { + if (textView != null) { + textView.setLines(lines); + } + } + + /** + * 设置行数 + * @param view {@link TextView} + * @param lines 行数 + * @return View + */ + public static View setLines(final View view, final int lines) { + setLines(getTextView(view), lines); + return view; + } + + // = + + /** + * 设置最大行数 + * @param textView {@link TextView} + * @param maxLines 最大行数 + * @param 泛型 + */ + public static void setMaxLines(final T textView, final int maxLines) { + if (textView != null) { + textView.setMaxLines(maxLines); + } + } + + /** + * 设置最大行数 + * @param view {@link TextView} + * @param maxLines 最大行数 + * @return View + */ + public static View setMaxLines(final View view, final int maxLines) { + setMaxLines(getTextView(view), maxLines); + return view; + } + + // = + + /** + * 设置最小行数 + * @param textView {@link TextView} + * @param minLines 最小行数 + * @param 泛型 + */ + public static void setMinLines(final T textView, final int minLines) { + if (textView != null && minLines > 0) { + textView.setMinLines(minLines); + } + } + + /** + * 设置最小行数 + * @param view {@link TextView} + * @param minLines 最小行数 + * @return View + */ + public static View setMinLines(final View view, final int minLines) { + setMinLines(getTextView(view), minLines); + return view; + } + + // = + + /** + * 设置最大字符宽度限制 + * @param textView {@link TextView} + * @param maxEms 最大字符 + * @param 泛型 + * @return {@link TextView} + */ + public static T setMaxEms(final T textView, final int maxEms) { + if (textView != null && maxEms > 0) { + textView.setMaxEms(maxEms); + } + return textView; + } + + /** + * 设置最大字符宽度限制 + * @param view {@link TextView} + * @param maxEms 最大字符 + * @return View + */ + public static View setMaxEms(final View view, final int maxEms) { + setMaxEms(getTextView(view), maxEms); + return view; + } + + // = + + /** + * 设置最小字符宽度限制 + * @param textView {@link TextView} + * @param minEms 最小字符 + * @param 泛型 + * @return {@link TextView} + */ + public static T setMinEms(final T textView, final int minEms) { + if (textView != null && minEms > 0) { + textView.setMinEms(minEms); + } + return textView; + } + + /** + * 设置最小字符宽度限制 + * @param view {@link TextView} + * @param minEms 最小字符 + * @return View + */ + public static View setMinEms(final View view, final int minEms) { + setMinEms(getTextView(view), minEms); + return view; + } + + // = + + /** + * 设置指定字符宽度 + * @param textView {@link TextView} + * @param ems 字符 + * @param 泛型 + * @return {@link TextView} + */ + public static T setEms(final T textView, final int ems) { + if (textView != null && ems > 0) { + textView.setEms(ems); + } + return textView; + } + + /** + * 设置指定字符宽度 + * @param view {@link TextView} + * @param ems 字符 + * @return View + */ + public static View setEms(final View view, final int ems) { + setEms(getTextView(view), ems); + return view; + } + + // = + + /** + * 设置长度限制 + * @param textView {@link TextView} + * @param maxLength 长度限制 + * @param 泛型 + * @return {@link TextView} + */ + public static T setMaxLength(final T textView, final int maxLength) { + if (textView != null && maxLength > 0) { + // 设置最大长度限制 + InputFilter[] filters = {new InputFilter.LengthFilter(maxLength)}; + textView.setFilters(filters); + } + return textView; + } + + /** + * 设置长度限制 + * @param view {@link TextView} + * @param maxLength 长度限制 + * @return View + */ + public static View setMaxLength(final View view, final int maxLength) { + setMaxLength(getTextView(view), maxLength); + return view; + } + + // = + + /** + * 设置长度限制, 并且设置内容 + * @param textView {@link TextView} + * @param content 文本内容 + * @param maxLength 长度限制 + * @param 泛型 + * @return {@link TextView} + */ + public static T setMaxLengthAndText(final T textView, final String content, final int maxLength) { + setText(setMaxLength(textView, maxLength), content); + return textView; + } + + /** + * 设置长度限制, 并且设置内容 + * @param view {@link TextView} + * @param content 文本内容 + * @param maxLength 长度限制 + * @return View + */ + public static View setMaxLengthAndText(final View view, final String content, final int maxLength) { + return setText(setMaxLength(view, maxLength), content); + } + + // = + + /** + * 设置 Ellipsize 效果 + * @param textView {@link TextView} + * @param where {@link TextUtils.TruncateAt} + * @param 泛型 + */ + public static void setEllipsize(final T textView, final TextUtils.TruncateAt where) { + if (textView != null) { + textView.setEllipsize(where); + } + } + + /** + * 设置 Ellipsize 效果 + * @param view {@link TextView} + * @param where {@link TextUtils.TruncateAt} + * @return View + */ + public static View setEllipsize(final View view, final TextUtils.TruncateAt where) { + setEllipsize(getTextView(view), where); + return view; + } + + // = + + /** + * 设置自动识别文本链接 + * @param textView {@link TextView} + * @param mask {@link android.text.util.Linkify} + * @param 泛型 + */ + public static void setAutoLinkMask(final T textView, final int mask) { + if (textView != null) { + textView.setAutoLinkMask(mask); + } + } + + /** + * 设置自动识别文本链接 + * @param view {@link TextView} + * @param mask {@link android.text.util.Linkify} + * @return View + */ + public static View setAutoLinkMask(final View view, final int mask) { + setAutoLinkMask(getTextView(view), mask); + return view; + } + + // = + + /** + * 设置文本全为大写 + * @param textView {@link TextView} + * @param allCaps 是否全部大写 + * @param 泛型 + */ + public static void setAllCaps(final T textView, final boolean allCaps) { + if (textView != null) { + textView.setAllCaps(allCaps); + } + } + + /** + * 设置文本全为大写 + * @param view {@link TextView} + * @param allCaps 是否全部大写 + * @return View + */ + public static View setAllCaps(final View view, final boolean allCaps) { + setAllCaps(getTextView(view), allCaps); + return view; + } + + // = + + /** + * 设置 Gravity + * @param textView {@link TextView} + * @param gravity {@link android.view.Gravity} + * @param 泛型 + */ + public static void setGravity(final T textView, final int gravity) { + if (textView != null) { + textView.setGravity(gravity); + } + } + + /** + * 设置 Gravity + * @param view {@link TextView} + * @param gravity {@link android.view.Gravity} + * @return View + */ + public static View setGravity(final View view, final int gravity) { + setGravity(getTextView(view), gravity); + return view; + } + + // = + + /** + * 获取 TextView Paint + * @param view {@link TextView} + * @param 泛型 + * @return {@link Paint} + */ + public static Paint getPaint(final View view) { + return getPaint(getTextView(view)); + } + + /** + * 获取 TextView Paint + * @param textView {@link TextView} + * @param 泛型 + * @return {@link Paint} + */ + public static Paint getPaint(final T textView) { + if (textView != null) { + return textView.getPaint(); + } + return null; + } + + /** + * 获取字体高度 + * @param textView {@link TextView} + * @param 泛型 + * @return 字体高度 + */ + public static int getTextHeight(final T textView) { + return getTextHeight(getPaint(textView)); + } + + /** + * 获取字体高度 + * @param paint {@link TextView#getPaint()} + * @return 字体高度 + */ + public static int getTextHeight(final Paint paint) { + if (paint != null) { + // 获取字体高度 + Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); + // 计算内容高度 + int textHeight = (int) Math.ceil((fontMetrics.descent - fontMetrics.ascent)); + // 返回字体高度 + return textHeight; + } + return -1; + } + + // = + + /** + * 获取字体顶部偏移高度 + * @param textView {@link TextView} + * @param 泛型 + * @return 字体顶部偏移高度 + */ + public static int getTextTopOffsetHeight(final T textView) { + return getTextTopOffsetHeight(getPaint(textView)); + } + + /** + * 获取字体顶部偏移高度 + * @param paint {@link TextView#getPaint()} + * @return 字体顶部偏移高度 + */ + public static int getTextTopOffsetHeight(final Paint paint) { + if (paint != null) { + // 获取字体高度 + Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); + // 计算字体偏差 ( 顶部偏差 ) + int baseLine = (int) Math.ceil(Math.abs(fontMetrics.top) - Math.abs(fontMetrics.ascent)); + // 返回顶部偏差 + return baseLine; + } + return -1; + } + + // = + + /** + * 计算字体宽度 + * @param textView {@link TextView} + * @param text 待测量文本 + * @param 泛型 + * @return 字体宽度 + */ + public static float getTextWidth(final T textView, final String text) { + return getTextWidth(getPaint(textView), text); + } + + /** + * 计算字体宽度 + * @param textView {@link TextView} + * @param 泛型 + * @return 字体宽度 + */ + public static float getTextWidth(final T textView) { + return getTextWidth(getPaint(textView), getText(textView)); + } + + /** + * 计算字体宽度 + * @param paint {@link TextView#getPaint()} + * @param text 待测量文本 + * @return 字体宽度 + */ + public static float getTextWidth(final Paint paint, final String text) { + if (paint != null && text != null) { + return paint.measureText(text); + } + return -1f; + } + + // = + + /** + * 计算字体宽度 + * @param view {@link TextView} + * @param text 待测量文本 + * @param start 开始位置 + * @param end 结束位置 + * @return 字体宽度 + */ + public static float getTextWidth(final View view, final String text, final int start, final int end) { + return getTextWidth(getPaint(view), text, start, end); + } + + /** + * 计算字体宽度 + * @param view {@link TextView} + * @param text 待测量文本 + * @param start 开始位置 + * @param end 结束位置 + * @return 字体宽度 + */ + public static float getTextWidth(final View view, final CharSequence text, final int start, final int end) { + return getTextWidth(getPaint(view), text, start, end); + } + + /** + * 计算字体宽度 + * @param view {@link TextView} + * @param text 待测量文本 + * @param start 开始位置 + * @param end 结束位置 + * @return 字体宽度 + */ + public static float getTextWidth(final View view, final char[] text, final int start, final int end) { + return getTextWidth(getPaint(view), text, start, end); + } + + // = + + /** + * 计算字体宽度 + * @param paint {@link TextView#getPaint()} + * @param text 待测量文本 + * @param start 开始位置 + * @param end 结束位置 + * @return 字体宽度 + */ + public static float getTextWidth(final Paint paint, final String text, final int start, final int end) { + if (paint != null && text != null) { + try { + return paint.measureText(text, start, end); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTextWidth"); + } + } + return -1f; + } + + /** + * 计算字体宽度 + * @param paint {@link TextView#getPaint()} + * @param text 待测量文本 + * @param start 开始位置 + * @param end 结束位置 + * @return 字体宽度 + */ + public static float getTextWidth(final Paint paint, final CharSequence text, final int start, final int end) { + if (paint != null && text != null) { + try { + return paint.measureText(text, start, end); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTextWidth"); + } + } + return -1f; + } + + /** + * 计算字体宽度 + * @param paint {@link TextView#getPaint()} + * @param text 待测量文本 + * @param start 开始位置 + * @param end 结束位置 + * @return 字体宽度 + */ + public static float getTextWidth(final Paint paint, final char[] text, final int start, final int end) { + if (paint != null && text != null) { + try { + return paint.measureText(text, start, end); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTextWidth"); + } + } + return -1f; + } + + // = + + /** + * 获取画布中间居中位置 + * @param targetRect {@link Rect} 目标坐标 + * @param paint {@link TextView#getPaint()} + * @return 画布 Y 轴居中位置 + */ + public static int getCenterRectY(final Rect targetRect, final Paint paint) { + if (targetRect != null && paint != null) { + // 获取字体高度 + Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); + // 获取底部 Y 轴居中位置 + return targetRect.top + (targetRect.bottom - targetRect.top) / 2 - (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.top; + // canvas.drawText(testString, targetRect.centerX(), baseline, paint); + } + return -1; + } + + /** + * 通过需要的高度, 计算字体大小 + * @param textHeight 需要的字体高度 + * @return 字体大小 + */ + public static float reckonTextSize(final int textHeight) { + // 创建画笔 + Paint paint = new Paint(); + // 默认字体大小 + float textSize = 90.0f; + // 计算内容高度 + int calcTextHeight = -1; + // 循环计算 + while (true) { + // 设置画笔大小 + paint.setTextSize(textSize); + // 获取字体高度 + Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); + // 计算内容高度 + calcTextHeight = (int) Math.ceil((fontMetrics.descent - fontMetrics.ascent)); + // 符合条件则直接返回 + if (calcTextHeight == textHeight) { + return textSize; + } else if (calcTextHeight > textHeight) { // 如果计算的字体高度大于 + textSize -= 0.5f; + } else { + textSize += 0.5f; + } + } + } + + // = + + /** + * 计算第几位超过宽度 + * @param textView {@link TextView} + * @param text 待测量文本 + * @param width 指定的宽度 + * @param 泛型 + * @return -1 表示没超过, 其他值表示对应的索引位置 + */ + public static int calcTextWidth(final T textView, final String text, final float width) { + return calcTextWidth(getPaint(textView), text, width); + } + + /** + * 计算第几位超过宽度 + * @param textView {@link TextView} + * @param width 指定的宽度 + * @param 泛型 + * @return -1 表示没超过, 其他值表示对应的索引位置 + */ + public static int calcTextWidth(final T textView, final float width) { + return calcTextWidth(getPaint(textView), getText(textView), width); + } + + /** + * 计算第几位超过宽度 + * @param paint {@link TextView#getPaint()} + * @param text 文本内容 + * @param width 指定的宽度 + * @return -1 表示没超过, 其他值表示对应的索引位置 + */ + public static int calcTextWidth(final Paint paint, final String text, final float width) { + if (paint != null && text != null && width > 0) { + // 全部文本宽度 + float allTextWidth = getTextWidth(paint, text); + // 判断是否超过 + if (allTextWidth <= width) return -1; // 表示没超过 + // 获取数据长度 + int length = text.length(); + // 超过长度且只有一个数据, 那么只能是第一个就超过了 + if (length == 1) return 1; + // 二分法寻找最佳位置 + int start = 0; + int end = length; + int mid = 0; + // 判断是否大于位置 + while (start < end) { + mid = (start + end) / 2; + // 获取字体宽度 + float textWidth = getTextWidth(paint, text, 0, mid); + // 如果相等直接返回 + if (textWidth == width) { + return mid; + } else if (textWidth > width) { + end = mid - 1; + } else { + start = mid + 1; + } + } + // 计算最符合的位置 + for (int i = Math.min(Math.min(start, mid), end), len = length; i <= len; i++) { + float textWidth = TextViewUtils.getTextWidth(paint, text, 0, i); + if (textWidth >= width) return i; + } + return start; + } + return -1; + } + + /** + * 计算文本换行行数 + * @param textView {@link TextView} + * @param text 待测量文本 + * @param width 指定的宽度 + * @param 泛型 + * @return 行数 + */ + public static int calcTextLine(final T textView, final String text, final float width) { + return calcTextLine(getPaint(textView), text, width); + } + + /** + * 计算文本行数 + * @param textView {@link TextView} + * @param width 指定的宽度 + * @param 泛型 + * @return 行数 + */ + public static int calcTextLine(final T textView, final float width) { + return calcTextLine(getPaint(textView), getText(textView), width); + } + + /** + * 计算文本行数 + * @param paint {@link TextView#getPaint()} + * @param text 文本内容 + * @param width 指定的宽度 + * @return 行数 + */ + public static int calcTextLine(final Paint paint, final String text, final float width) { + if (paint != null && text != null && width > 0) { + // 全部文本宽度 + float allTextWidth = getTextWidth(paint, text); + // 判断是否超过 + if (allTextWidth <= width) return 1; + int result = (int) (allTextWidth / width); + return ((allTextWidth - width * result == 0f) ? result : result + 1); + } + return 0; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/UriUtils.java b/lib/DevApp/src/main/java/dev/utils/app/UriUtils.java new file mode 100644 index 0000000000..bb8e57f7de --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/UriUtils.java @@ -0,0 +1,221 @@ +package dev.utils.app; + +import android.content.ContentResolver; +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; +import android.support.v4.content.FileProvider; + +import java.io.File; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Uri 工具类 + * @author Ttt + *
+ *     
+ *          
+ *     
+ * 
+ */ +public final class UriUtils { + + private UriUtils() { + } + + // 日志 TAG + private static final String TAG = UriUtils.class.getSimpleName(); + + // ================ + // = FileProvider = + // ================ + + /** + * 获取文件 Uri ( 自动添加包名 ${applicationId}) + * @param file 文件 + * @param fileProvider android:authorities = ${applicationId}.fileProvider + * @return 指定文件 {@link Uri} + */ + public static Uri getUriForFileToName(final File file, final String fileProvider) { + if (file == null || fileProvider == null) return null; + try { + String authority = DevUtils.getContext().getPackageName() + "." + fileProvider; + return getUriForFile(file, authority); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getUriForFileToName"); + return null; + } + } + + /** + * 获取文件 Uri + * @param file 文件 + * @param authority android:authorities + * @return 指定文件 {@link Uri} + */ + public static Uri getUriForFile(final File file, final String authority) { + if (file == null || authority == null) return null; + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return FileProvider.getUriForFile(DevUtils.getContext(), authority, file); + } else { + return Uri.fromFile(file); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getUriForFile"); + return null; + } + } + + // = + + /** + * 通过 Uri 获取文件路径 + * @param uri {@link Uri} + * @return 文件路径 + */ + public static String getFilePathByUri(final Uri uri) { + try { + return getFilePathByUri(DevUtils.getContext(), uri); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getFilePathByUri"); + return null; + } + } + + /** + * 通过 Uri 获取文件路径 + * @param context {@link Context} + * @param uri {@link Uri} + * @return 文件路径 + */ + private static String getFilePathByUri(final Context context, final Uri uri) { + if (context == null || uri == null) return null; + // 文件路径 + String path = null; + // 以 file:// 开头的 + if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { + path = uri.getPath(); + return path; + } + // 以 content:// 开头的, 比如 content://media/extenral/images/media/17766 + if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.Media.DATA}, null, null, null); + if (cursor != null) { + if (cursor.moveToFirst()) { + int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + if (columnIndex > -1) { + path = cursor.getString(columnIndex); + } + } + cursor.close(); + } + return path; + } + // 4.4 及之后的, 是以 content:// 开头的, 比如 content://com.android.providers.media.documents/document/image%3A235700 + if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (DocumentsContract.isDocumentUri(context, uri)) { + if (isExternalStorageDocument(uri)) { + // ExternalStorageProvider + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + if ("primary".equalsIgnoreCase(type)) { + path = Environment.getExternalStorageDirectory() + "/" + split[1]; + return path; + } + } 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)); + path = getDataColumn(context, contentUri, null, null); + return path; + } 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]}; + path = getDataColumn(context, contentUri, selection, selectionArgs); + return path; + } + } + } + return null; + } + + /** + * 获取 Uri Cursor 对应条件的数据行 data 字段 + * @param context {@link Context} + * @param uri {@link Uri} + * @param selection WHERE clause + * @param selectionArgs 对应的字段绑定 + * @return 对应条件的数据行 data 字段 + */ + private static String getDataColumn(final Context context, final Uri uri, final String selection, final 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 column_index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(column_index); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; + } + + /** + * 判读 Uri authority 是否为 ExternalStorage Provider + * @param uri {@link Uri} + * @return {@code true} yes, {@code false} no + */ + private static boolean isExternalStorageDocument(final Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + /** + * 判读 Uri authority 是否为 Downloads Provider + * @param uri {@link Uri} + * @return {@code true} yes, {@code false} no + */ + private static boolean isDownloadsDocument(final Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + /** + * 判读 Uri authority 是否为 Media Provider + * @param uri {@link Uri} + * @return {@code true} yes, {@code false} no + */ + private static boolean isMediaDocument(final Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/VibrationUtils.java b/lib/DevApp/src/main/java/dev/utils/app/VibrationUtils.java new file mode 100644 index 0000000000..5041082e60 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/VibrationUtils.java @@ -0,0 +1,68 @@ +package dev.utils.app; + +import android.content.Context; +import android.os.Vibrator; +import android.support.annotation.RequiresPermission; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 震动相关工具类 + * @author Ttt + *
+ *     所需权限
+ *     
+ * 
+ */ +public final class VibrationUtils { + + private VibrationUtils() { + } + + // 日志 TAG + private static final String TAG = VibrationUtils.class.getSimpleName(); + + /** + * 震动 + * @param milliseconds 震动时长 ( 毫秒 ) + */ + @RequiresPermission(android.Manifest.permission.VIBRATE) + public static void vibrate(final long milliseconds) { + try { + Vibrator vibrator = (Vibrator) DevUtils.getContext().getSystemService(Context.VIBRATOR_SERVICE); + vibrator.vibrate(milliseconds); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "vibrate"); + } + } + + /** + * pattern 模式震动 + * @param pattern new long[]{400, 800, 1200, 1600}, 就是指定在 400ms、800ms、1200ms、1600ms 这些时间点交替启动、关闭手机震动器 + * @param repeat 指定 pattern 数组的索引, 指定 pattern 数组中从 repeat 索引开始的震动进行循环, + * -1 表示只震动一次, 非 -1 表示从 pattern 数组指定下标开始重复震动 + */ + @RequiresPermission(android.Manifest.permission.VIBRATE) + public static void vibrate(final long[] pattern, final int repeat) { + if (pattern == null) return; + try { + Vibrator vibrator = (Vibrator) DevUtils.getContext().getSystemService(Context.VIBRATOR_SERVICE); + vibrator.vibrate(pattern, repeat); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "vibrate"); + } + } + + /** + * 取消震动 + */ + @RequiresPermission(android.Manifest.permission.VIBRATE) + public static void cancel() { + try { + ((Vibrator) DevUtils.getContext().getSystemService(Context.VIBRATOR_SERVICE)).cancel(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cancel"); + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/ViewUtils.java b/lib/DevApp/src/main/java/dev/utils/app/ViewUtils.java new file mode 100644 index 0000000000..6e01aa571b --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/ViewUtils.java @@ -0,0 +1,2276 @@ +package dev.utils.app; + +import android.app.Activity; +import android.content.Context; +import android.content.ContextWrapper; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.annotation.FloatRange; +import android.support.annotation.IdRes; +import android.support.annotation.LayoutRes; +import android.support.annotation.RequiresApi; +import android.support.design.widget.CoordinatorLayout; +import android.support.v4.view.ViewPager; +import android.support.v4.widget.DrawerLayout; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.Window; +import android.widget.AbsListView; +import android.widget.FrameLayout; +import android.widget.GridView; +import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: View 操作相关工具类 + * @author Ttt + *
+ *     组件设置 setCompoundDrawables 不生效解决办法
+ *     @see 
+ * 
+ */ +public final class ViewUtils { + + private ViewUtils() { + } + + // 日志 TAG + private static final String TAG = ViewUtils.class.getSimpleName(); + + /** + * 获取 Context + * @param view {@link View} + * @return {@link Context} + */ + public static Context getContext(final View view) { + if (view != null) { + return view.getContext(); + } + return null; + } + + /** + * 获取 View context 所属的 Activity + * @param view {@link View} + * @return {@link Activity} + */ + public static Activity getActivity(final View view) { + if (view != null) { + try { + Context context = view.getContext(); + while (context instanceof ContextWrapper) { + if (context instanceof Activity) { + return (Activity) context; + } + context = ((ContextWrapper) context).getBaseContext(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivity"); + } + } + return null; + } + + // = + + /** + * 获取 View + * @param resource R.layout.id + * @return {@link View} + */ + public static View inflate(@LayoutRes final int resource) { + return inflate(resource, null, false); + } + + /** + * 获取 View + * @param resource R.layout.id + * @param root {@link ViewGroup} + * @return {@link View} + */ + public static View inflate(@LayoutRes final int resource, final ViewGroup root) { + return inflate(resource, root, root != null); + } + + /** + * 获取 View + * @param resource R.layout.id + * @param root {@link ViewGroup} + * @param attachToRoot 是否添加到 root 上 + * @return {@link View} + */ + public static View inflate(@LayoutRes final int resource, final ViewGroup root, final boolean attachToRoot) { + try { + return LayoutInflater.from(DevUtils.getContext()).inflate(resource, root, attachToRoot); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "inflate"); + } + return null; + } + + // = + + /** + * 获取指定 View 父布局 + * @param view {@link View} + * @param 泛型 + * @return View + */ + public static T getParent(final View view) { + if (view != null) { + try { + return (T) view.getParent(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getParent"); + } + } + return null; + } + + // = + + /** + * 获取指定索引 View + * @param viewGroup {@link ViewGroup} + * @param 泛型 + * @return View + */ + public static T getChildAt(final ViewGroup viewGroup) { + return getChildAt(viewGroup, 0); + } + + /** + * 获取指定索引 View + * @param viewGroup {@link ViewGroup} + * @param index 索引 + * @param 泛型 + * @return View + */ + public static T getChildAt(final ViewGroup viewGroup, final int index) { + if (viewGroup != null && index >= 0) { + try { + return (T) viewGroup.getChildAt(index); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getChildAt"); + } + } + return null; + } + + /** + * 移除全部子 View + * @param viewGroup {@link ViewGroup} + * @param 泛型 + * @return 传入 View + */ + public static T removeAllViews(final T viewGroup) { + if (viewGroup != null) { + viewGroup.removeAllViews(); + } + return viewGroup; + } + + // = + + /** + * 获取 LayoutParams + * @param view {@link View} + * @param 泛型 + * @return LayoutParams + */ + public static T getLayoutParams(final View view) { + if (view != null) { + try { + return (T) view.getLayoutParams(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLayoutParams"); + } + } + return null; + } + + /** + * 设置 View LayoutParams + * @param view {@link View} + * @param params LayoutParams + * @return {@code true} success, {@code false} fail + */ + public static boolean setLayoutParams(final View view, final ViewGroup.LayoutParams params) { + if (view != null) { + try { + view.setLayoutParams(params); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setLayoutParams"); + } + } + return false; + } + + // ====================== + // = 初始化 View 操作等 = + // ====================== + + /** + * 初始化 View + * @param view {@link View} + * @param id R.id.viewId + * @param 泛型 + * @return {@link View} + */ + public static T findViewById(final View view, @IdRes final int id) { + if (view != null) { + try { + return view.findViewById(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "findViewById"); + } + } + return null; + } + + /** + * 初始化 View + * @param window {@link Window} + * @param id R.id.viewId + * @param 泛型 + * @return {@link View} + */ + public static T findViewById(final Window window, @IdRes final int id) { + if (window != null) { + try { + return window.findViewById(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "findViewById"); + } + } + return null; + } + + /** + * 初始化 View + * @param activity {@link Activity} + * @param id R.id.viewId + * @param 泛型 + * @return {@link View} + */ + public static T findViewById(final Activity activity, @IdRes final int id) { + if (activity != null) { + try { + return activity.findViewById(id); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "findViewById"); + } + } + return null; + } + + /** + * 转换 View + * @param view {@link View} + * @param 泛型 + * @return {@link View} + */ + public static T convertView(final View view) { + try { + return (T) view; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "convertView"); + } + return null; + } + + // ============= + // = View 判空 = + // ============= + + /** + * 判断 View 是否为 null + * @param view {@link View} + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final View view) { + return view == null; + } + + /** + * 判断 View 是否为 null + * @param views View[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final View... views) { + if (views != null && views.length != 0) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view == null) { + return true; + } + } + return false; + } + return true; + } + + /** + * 判断 View 是否不为 null + * @param view {@link View} + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final View view) { + return view != null; + } + + /** + * 判断 View 是否不为 null + * @param views View[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final View... views) { + if (views != null && views.length != 0) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view == null) { + return false; + } + } + return true; + } + return false; + } + + // = + + /** + * 获取 View 最小高度 + * @param view View + * @return View 最小高度 + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) + public static int getMinimumHeight(final View view) { + if (view != null) { + return view.getMinimumHeight(); + } + return -1; + } + + /** + * 设置 View 最小高度 + * @param view View + * @param minHeight 最小高度 + * @return View + */ + public static View setMinimumHeight(final View view, final int minHeight) { + if (view != null) { + view.setMinimumHeight(minHeight); + } + return view; + } + + /** + * 获取 View 最小宽度 + * @param view View + * @return View 最小宽度 + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) + public static int getMinimumWidth(final View view) { + if (view != null) { + return view.getMinimumWidth(); + } + return -1; + } + + /** + * 设置 View 最小宽度 + * @param view View + * @param minWidth 最小宽度 + * @return View + */ + public static View setMinimumWidth(final View view, final int minWidth) { + if (view != null) { + view.setMinimumWidth(minWidth); + } + return view; + } + + // = + + /** + * 获取 View 透明度 + * @param view View + * @return 透明度 + */ + public static float getAlpha(final View view) { + if (view != null) { + return view.getAlpha(); + } + return 1.0f; + } + + /** + * 设置 View 透明度 + * @param view View + * @param alpha 透明度 + * @return View + */ + public static View setAlpha(final View view, @FloatRange(from = 0.0, to = 1.0) final float alpha) { + if (view != null) { + view.setAlpha(alpha); + } + return view; + } + + /** + * 获取 View Tag + * @param view View + * @return Tag + */ + public static Object getTag(final View view) { + if (view != null) { + return view.getTag(); + } + return null; + } + + /** + * 设置 View Tag + * @param view View + * @param object Tag + * @return View + */ + public static View setTag(final View view, final Object object) { + if (view != null) { + view.setTag(object); + } + return view; + } + + // = + + /** + * 设置 View 滚动效应 + * @param view View + * @param isScrollContainer 是否需要滚动效应 + * @return View + */ + public static View setScrollContainer(final View view, final boolean isScrollContainer) { + if (view != null) { + view.setScrollContainer(isScrollContainer); + } + return view; + } + + /** + * 设置下一个获取焦点的 View id + * @param view View + * @param nextFocusForwardId 下一个获取焦点的 View id + * @return View + */ + public static View setNextFocusForwardId(final View view, @IdRes final int nextFocusForwardId) { + if (view != null) { + view.setNextFocusForwardId(nextFocusForwardId); + } + return view; + } + + /** + * 设置向下移动焦点时, 下一个获取焦点的 View id + * @param view View + * @param nextFocusDownId 下一个获取焦点的 View id + * @return View + */ + public static View setNextFocusDownId(final View view, @IdRes final int nextFocusDownId) { + if (view != null) { + view.setNextFocusDownId(nextFocusDownId); + } + return view; + } + + /** + * 设置向左移动焦点时, 下一个获取焦点的 View id + * @param view View + * @param nextFocusLeftId 下一个获取焦点的 View id + * @return View + */ + public static View setNextFocusLeftId(final View view, @IdRes final int nextFocusLeftId) { + if (view != null) { + view.setNextFocusLeftId(nextFocusLeftId); + } + return view; + } + + /** + * 设置向右移动焦点时, 下一个获取焦点的 View id + * @param view View + * @param nextFocusRightId 下一个获取焦点的 View id + * @return View + */ + public static View setNextFocusRightId(final View view, @IdRes final int nextFocusRightId) { + if (view != null) { + view.setNextFocusRightId(nextFocusRightId); + } + return view; + } + + /** + * 设置向上移动焦点时, 下一个获取焦点的 View id + * @param view View + * @param nextFocusUpId 下一个获取焦点的 View id + * @return View + */ + public static View setNextFocusUpId(final View view, @IdRes final int nextFocusUpId) { + if (view != null) { + view.setNextFocusUpId(nextFocusUpId); + } + return view; + } + + /** + * 设置 View 旋转度数 + * @param view View + * @param rotation 旋转度数 + * @return View + */ + public static View setRotation(final View view, final float rotation) { + if (view != null) { + view.setRotation(rotation); + } + return view; + } + + /** + * 设置 View 水平旋转度数 + * @param view View + * @param rotationX 水平旋转度数 + * @return View + */ + public static View setRotationX(final View view, final float rotationX) { + if (view != null) { + view.setRotationX(rotationX); + } + return view; + } + + /** + * 设置 View 竖直旋转度数 + * @param view View + * @param rotationY 竖直旋转度数 + * @return View + */ + public static View setRotationY(final View view, final float rotationY) { + if (view != null) { + view.setRotationY(rotationY); + } + return view; + } + + /** + * 设置 View 水平方向缩放比例 + * @param view View + * @param scaleX 水平方向缩放比例 + * @return View + */ + public static View setScaleX(final View view, final float scaleX) { + if (view != null) { + view.setScaleX(scaleX); + } + return view; + } + + /** + * 设置 View 竖直方向缩放比例 + * @param view View + * @param scaleY 竖直方向缩放比例 + * @return View + */ + public static View setScaleY(final View view, final float scaleY) { + if (view != null) { + view.setScaleY(scaleY); + } + return view; + } + + /** + * 设置文本的显示方式 + * @param view View + * @param textAlignment 文本的显示方式 + * @return View + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) + public static View setTextAlignment(final View view, final int textAlignment) { + if (view != null) { + view.setTextAlignment(textAlignment); + } + return view; + } + + /** + * 设置文本的显示方向 + * @param view View + * @param textDirection 文本的显示方向 + * @return View + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) + public static View setTextDirection(final View view, final int textDirection) { + if (view != null) { + view.setTextDirection(textDirection); + } + return view; + } + + /** + * 设置水平方向偏转量 + * @param view View + * @param pivotX 水平方向偏转量 + * @return View + */ + public static View setPivotX(final View view, final float pivotX) { + if (view != null) { + view.setPivotX(pivotX); + } + return view; + } + + /** + * 设置竖直方向偏转量 + * @param view View + * @param pivotY 竖直方向偏转量 + * @return View + */ + public static View setPivotY(final View view, final float pivotY) { + if (view != null) { + view.setPivotY(pivotY); + } + return view; + } + + /** + * 设置水平方向的移动距离 + * @param view View + * @param translationX 水平方向的移动距离 + * @return View + */ + public static View setTranslationX(final View view, final float translationX) { + if (view != null) { + view.setTranslationX(translationX); + } + return view; + } + + /** + * 设置竖直方向的移动距离 + * @param view View + * @param translationY 竖直方向的移动距离 + * @return View + */ + public static View setTranslationY(final View view, final float translationY) { + if (view != null) { + view.setTranslationY(translationY); + } + return view; + } + + /** + * 设置 View 硬件加速类型 + * @param view View + * @param layerType 硬件加速类型 + * @param paint {@link Paint} + * @return View + */ + public static View setLayerType(final View view, final int layerType, final Paint paint) { + if (view != null) { + view.setLayerType(layerType, paint); + } + return view; + } + + // = + + /** + * 设置 View 是否可以获取焦点 + * @param focusable {@code true} 可获取, {@code false} 不可获取 + * @param views View[] + * @return {@code true} 可获取, {@code false} 不可获取 + */ + public static boolean setFocusable(final boolean focusable, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view != null) { + view.setFocusable(focusable); + } + } + } + return focusable; + } + + /** + * 设置 View 是否选中 + * @param selected {@code true} 选中, {@code false} 非选中 + * @param views View[] + * @return {@code true} 选中, {@code false} 非选中 + */ + public static boolean setSelected(final boolean selected, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view != null) { + view.setSelected(selected); + } + } + } + return selected; + } + + /** + * 设置 View 是否启用 + * @param enabled {@code true} 启用, {@code false} 禁用 + * @param views View[] + * @return {@code true} 启用, {@code false} 禁用 + */ + public static boolean setEnabled(final boolean enabled, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view != null) { + view.setEnabled(enabled); + } + } + } + return enabled; + } + + /** + * 设置 View 是否可以点击 + * @param clickable {@code true} 可点击, {@code false} 不可点击 + * @param views View[] + * @return {@code true} 可点击, {@code false} 不可点击 + */ + public static boolean setClickable(final boolean clickable, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view != null) { + view.setClickable(clickable); + } + } + } + return clickable; + } + + /** + * 设置 View 是否可以长按 + * @param longClickable {@code true} 可长按, {@code false} 不可长按 + * @param views View[] + * @return {@code true} 可长按, {@code false} 不可长按 + */ + public static boolean setLongClickable(final boolean longClickable, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view != null) { + view.setLongClickable(longClickable); + } + } + } + return longClickable; + } + + // ================= + // = View 显示状态 = + // ================= + + /** + * 判断 View 是否显示 + * @param view {@link View} + * @return {@code true} yes, {@code false} no + */ + public static boolean isVisibility(final View view) { + return isVisibility(view, true); + } + + /** + * 判断 View 是否显示 + * @param view {@link View} + * @param defaultValue view 为 null 默认值 + * @return {@code true} yes, {@code false} no + */ + public static boolean isVisibility(final View view, final boolean defaultValue) { + if (view != null) { + return (view.getVisibility() == View.VISIBLE); + } + return defaultValue; + } + + /** + * 判断 View 是否都显示 + * @param views View[] + * @return {@code true} yes, {@code false} no + */ + public static boolean isVisibilitys(final View... views) { + if (views != null && views.length != 0) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view == null || view.getVisibility() != View.VISIBLE) { + return false; + } + } + return true; + } + return false; + } + + // = + + /** + * 判断 View 是否隐藏占位 + * @param view {@link View} + * @return {@code true} yes, {@code false} no + */ + public static boolean isVisibilityIN(final View view) { + return isVisibilityIN(view, false); + } + + /** + * 判断 View 是否隐藏占位 + * @param view {@link View} + * @param defaultValue view 为 null 默认值 + * @return {@code true} yes, {@code false} no + */ + public static boolean isVisibilityIN(final View view, final boolean defaultValue) { + if (view != null) { + return (view.getVisibility() == View.INVISIBLE); + } + return defaultValue; + } + + /** + * 判断 View 是否都隐藏占位 + * @param views View[] + * @return {@code true} yes, {@code false} no + */ + public static boolean isVisibilityINs(final View... views) { + if (views != null && views.length != 0) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view == null || view.getVisibility() != View.INVISIBLE) { + return false; + } + } + return true; + } + return false; + } + + // = + + /** + * 判断 View 是否隐藏 + * @param view {@link View} + * @return {@code true} yes, {@code false} no + */ + public static boolean isVisibilityGone(final View view) { + return isVisibilityGone(view, false); + } + + /** + * 判断 View 是否隐藏 + * @param view {@link View} + * @param defaultValue view 为 null 默认值 + * @return {@code true} yes, {@code false} no + */ + public static boolean isVisibilityGone(final View view, final boolean defaultValue) { + if (view != null) { + return (view.getVisibility() == View.GONE); + } + return defaultValue; + } + + /** + * 判断 View 是否都隐藏 + * @param views View[] + * @return {@code true} yes, {@code false} no + */ + public static boolean isVisibilityGones(final View... views) { + if (views != null && views.length != 0) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view == null || view.getVisibility() != View.GONE) { + return false; + } + } + return true; + } + return false; + } + + // = + + /** + * 获取显示的状态 (View.VISIBLE : View.GONE) + * @param isVisibility {@code true} View.VISIBLE, {@code false} View.GONE + * @return 显示的状态 {@link View#VISIBLE}、{@link View#GONE} + */ + public static int getVisibility(final boolean isVisibility) { + return isVisibility ? View.VISIBLE : View.GONE; + } + + /** + * 获取显示的状态 (View.VISIBLE : View.INVISIBLE) + * @param isVisibility {@code true} View.VISIBLE, {@code false} View.INVISIBLE + * @return 显示的状态 {@link View#VISIBLE}、{@link View#INVISIBLE} + */ + public static int getVisibilityIN(final boolean isVisibility) { + return isVisibility ? View.VISIBLE : View.INVISIBLE; + } + + // = + + /** + * 设置 View 显示的状态 + * @param isVisibility {@code true} View.VISIBLE, {@code false} View.GONE + * @param view {@link View} + * @return isVisibility + */ + public static boolean setVisibility(final boolean isVisibility, final View view) { + if (view != null) { + view.setVisibility(isVisibility ? View.VISIBLE : View.GONE); + } + return isVisibility; + } + + /** + * 设置 View 显示的状态 + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param view {@link View} + * @return isVisibility 是否传入 {@link View#VISIBLE} + */ + public static boolean setVisibility(final int isVisibility, final View view) { + if (view != null) { + view.setVisibility(isVisibility); + } + return (isVisibility == View.VISIBLE); + } + + // = + + /** + * 设置 View 显示的状态 + * @param isVisibility {@code true} View.VISIBLE, {@code false} View.GONE + * @param views View[] + * @return isVisibility 是否传入 {@link View#VISIBLE} + */ + public static boolean setVisibilitys(final boolean isVisibility, final View... views) { + return setVisibilitys(getVisibility(isVisibility), views); + } + + /** + * 设置 View 显示的状态 + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + * @return isVisibility 是否传入 {@link View#VISIBLE} + */ + public static boolean setVisibilitys(final int isVisibility, final View... views) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view != null) { + view.setVisibility(isVisibility); + } + } + } + return (isVisibility == View.VISIBLE); + } + + // = + + /** + * 切换 View 显示的状态 + * @param view {@link View} + * @param views View[] + */ + public static void toggleVisibilitys(final View view, final View... views) { + if (view != null) { + view.setVisibility(View.VISIBLE); + } + setVisibilitys(View.GONE, views); + } + + /** + * 切换 View 显示的状态 + * @param viewArys View[] + * @param views View[] + */ + public static void toggleVisibilitys(final View[] viewArys, final View... views) { + toggleVisibilitys(View.GONE, viewArys, views); + } + + /** + * 切换 View 显示的状态 + * @param status {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param viewArys View[] + * @param views View[] + */ + public static void toggleVisibilitys(final int status, final View[] viewArys, final View... views) { + // 默认显示 + setVisibilitys(View.VISIBLE, viewArys); + // 根据状态处理 + setVisibilitys(status, views); + } + + // = + + /** + * 反转 View 显示的状态 + * @param status {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param viewArys View[] + * @param views View[] + * @return isVisibility + */ + public static boolean reverseVisibilitys(final int status, final View[] viewArys, final View... views) { + return reverseVisibilitys(status == View.VISIBLE, viewArys, views); + } + + /** + * 反转 View 显示的状态 + * @param isVisibility {@code true} View.VISIBLE, {@code false} View.GONE + * @param viewArys View[] + * @param views View[] + * @return isVisibility + */ + public static boolean reverseVisibilitys(final boolean isVisibility, final View[] viewArys, final View... views) { + // 默认处理第一个数组 + setVisibilitys(isVisibility, viewArys); + // 根据状态处理 + setVisibilitys(!isVisibility, views); + return isVisibility; + } + + /** + * 反转 View 显示的状态 + * @param status {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param view {@link View} + * @param views View[] + * @return isVisibility + */ + public static boolean reverseVisibilitys(final int status, final View view, final View... views) { + return reverseVisibilitys(status == View.VISIBLE, view, views); + } + + /** + * 反转 View 显示的状态 + * @param isVisibility {@code true} View.VISIBLE, {@code false} View.GONE + * @param view {@link View} + * @param views View[] + * @return isVisibility + */ + public static boolean reverseVisibilitys(final boolean isVisibility, final View view, final View... views) { + // 默认处理第一个 View + setVisibilitys(isVisibility, view); + // 根据状态处理 + setVisibilitys(!isVisibility, views); + return isVisibility; + } + + // = + + /** + * 切换 View 状态 + * @param isChange 是否改变 + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param view {@link View} + * @return isChange + */ + public static boolean toggleView(final boolean isChange, final int isVisibility, final View view) { + if (isChange && view != null) { + view.setVisibility(isVisibility); + } + return isChange; + } + + /** + * 切换 View 状态 + * @param isChange 是否改变 + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + * @return isChange + */ + public static boolean toggleViews(final boolean isChange, final int isVisibility, final View... views) { + if (isChange && views != null) { + for (int i = 0, len = views.length; i < len; i++) { + View view = views[i]; + if (view != null) { + view.setVisibility(isVisibility); + } + } + } + return isChange; + } + + // = + + /** + * 把自身从父 View 中移除 + * @param view {@link View} + * @return View + */ + public static View removeSelfFromParent(final View view) { + if (view != null) { + try { + ViewParent parent = view.getParent(); + if (parent != null && parent instanceof ViewGroup) { + ViewGroup group = (ViewGroup) parent; + group.removeView(view); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "removeSelfFromParent"); + } + } + return view; + } + + /** + * 判断触点是否落在该 View 上 + * @param ev {@link MotionEvent} + * @param view 待判断 {@link View} + * @return {@code true} yes, {@code false} no + */ + public static boolean isTouchInView(final MotionEvent ev, final View view) { + if (ev != null && view != null) { + int[] locations = new int[2]; + view.getLocationOnScreen(locations); + float motionX = ev.getRawX(); + float motionY = ev.getRawY(); + return motionX >= locations[0] && motionX <= (locations[0] + view.getWidth()) + && motionY >= locations[1] && motionY <= (locations[1] + view.getHeight()); + } + return false; + } + + /** + * View 请求更新 + * @param view {@link View} + * @param allParent 是否全部父布局 View 都请求 + * @return View + */ + public static View requestLayoutParent(final View view, final boolean allParent) { + if (view != null) { + ViewParent parent = view.getParent(); + while (parent != null && parent instanceof View) { + if (!parent.isLayoutRequested()) { + parent.requestLayout(); + if (!allParent) { + break; + } + } + parent = parent.getParent(); + } + } + return view; + } + + /** + * 测量 View + * @param view {@link View} + * @return int[] 0 = 宽度, 1 = 高度 + */ + public static int[] measureView(final View view) { + if (view != null) { + try { + ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); + if (layoutParams == null) { + layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + int widthSpec = ViewGroup.getChildMeasureSpec(0, 0, layoutParams.width); + int height = layoutParams.height; + int heightSpec; + if (height > 0) { + heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + } else { + heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + } + view.measure(widthSpec, heightSpec); + return new int[]{view.getMeasuredWidth(), view.getMeasuredHeight()}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "measureView"); + } + } + return new int[]{0, 0}; + } + + /** + * 获取 View 的宽度 + * @param view {@link View} + * @return View 的宽度 + */ + public static int getMeasuredWidth(final View view) { + if (view != null) { + measureView(view); + return view.getMeasuredWidth(); + } + return 0; + } + + /** + * 获取 View 的高度 + * @param view {@link View} + * @return View 的高度 + */ + public static int getMeasuredHeight(final View view) { + if (view != null) { + measureView(view); + return view.getMeasuredHeight(); + } + return 0; + } + + // ================== + // = Layout Gravity = + // ================== + + /** + * 获取 View Layout Gravity + * @param view {@link View} + * @return Layout Gravity + */ + public static int getLayoutGravity(final View view) { + if (view != null) { + try { + if (view.getLayoutParams() instanceof LinearLayout.LayoutParams) { + return ((LinearLayout.LayoutParams) view.getLayoutParams()).gravity; + } else if (view.getLayoutParams() instanceof FrameLayout.LayoutParams) { + return ((FrameLayout.LayoutParams) view.getLayoutParams()).gravity; + } else if (view.getLayoutParams() instanceof ViewPager.LayoutParams) { + return ((ViewPager.LayoutParams) view.getLayoutParams()).gravity; + } else if (view.getLayoutParams() instanceof CoordinatorLayout.LayoutParams) { + return ((CoordinatorLayout.LayoutParams) view.getLayoutParams()).gravity; + } else if (view.getLayoutParams() instanceof DrawerLayout.LayoutParams) { + return ((DrawerLayout.LayoutParams) view.getLayoutParams()).gravity; + } else { + // 抛出不支持的类型 + throw new Exception("layoutParams:" + view.getLayoutParams().toString()); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLayoutGravity"); + } + } + return -1; + } + + /** + * 设置 View Layout Gravity + * @param view {@link View} + * @param gravity Gravity + * @return {@code true} success, {@code false} fail + */ + public static boolean setLayoutGravity(final View view, final int gravity) { + if (view != null) { + try { + if (view.getLayoutParams() instanceof LinearLayout.LayoutParams) { + ((LinearLayout.LayoutParams) view.getLayoutParams()).gravity = gravity; + } else if (view.getLayoutParams() instanceof FrameLayout.LayoutParams) { + ((FrameLayout.LayoutParams) view.getLayoutParams()).gravity = gravity; + } else if (view.getLayoutParams() instanceof ViewPager.LayoutParams) { + ((ViewPager.LayoutParams) view.getLayoutParams()).gravity = gravity; + } else if (view.getLayoutParams() instanceof CoordinatorLayout.LayoutParams) { + ((CoordinatorLayout.LayoutParams) view.getLayoutParams()).gravity = gravity; + } else if (view.getLayoutParams() instanceof DrawerLayout.LayoutParams) { + ((DrawerLayout.LayoutParams) view.getLayoutParams()).gravity = gravity; + } else { + // 抛出不支持的类型 + throw new Exception("layoutParams:" + view.getLayoutParams().toString()); + } + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setLayoutGravity"); + } + } + return false; + } + + // =============== + // = View Margin = + // =============== + + /** + * 获取 View Left Margin + * @param view {@link View} + * @return Left Margin + */ + public static int getMarginLeft(final View view) { + return getMargin(view)[0]; + } + + /** + * 获取 View Top Margin + * @param view {@link View} + * @return Top Margin + */ + public static int getMarginTop(final View view) { + return getMargin(view)[1]; + } + + /** + * 获取 View Right Margin + * @param view {@link View} + * @return Right Margin + */ + public static int getMarginRight(final View view) { + return getMargin(view)[2]; + } + + /** + * 获取 View Bottom Margin + * @param view {@link View} + * @return Bottom Margin + */ + public static int getMarginBottom(final View view) { + return getMargin(view)[3]; + } + + // = + + /** + * 获取 View Margin + * @param view {@link View} + * @return new int[] {left, top, right, bottom} + */ + public static int[] getMargin(final View view) { + int[] margins = new int[]{0, 0, 0, 0}; + if (view != null) { + // 判断是否属于 ViewGroup.MarginLayoutParams + if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + try { + ViewGroup.MarginLayoutParams layoutParams = ((ViewGroup.MarginLayoutParams) view.getLayoutParams()); + margins[0] = layoutParams.leftMargin; + margins[1] = layoutParams.topMargin; + margins[2] = layoutParams.rightMargin; + margins[3] = layoutParams.bottomMargin; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMargin"); + } + } + } + return margins; + } + + // = + + /** + * 设置 View Left Margin + * @param view {@link View} + * @param leftMargin Left Margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMarginLeft(final View view, final int leftMargin) { + return setMarginLeft(view, leftMargin, true); + } + + /** + * 设置 View Left Margin + * @param view {@link View} + * @param leftMargin Left Margin + * @param reset 是否重置清空其他 margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMarginLeft(final View view, final int leftMargin, final boolean reset) { + if (view != null) { + // 判断是否属于 ViewGroup.MarginLayoutParams + if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + if (reset) { + try { + ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).setMargins(leftMargin, 0, 0, 0); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMarginLeft"); + } + } else { + try { + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); + // 设置边距 + ((ViewGroup.MarginLayoutParams) view.getLayoutParams()) + .setMargins(leftMargin, layoutParams.topMargin, + layoutParams.rightMargin, layoutParams.bottomMargin); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMarginLeft"); + } + } + } + } + return false; + } + + /** + * 设置 View Top Margin + * @param view {@link View} + * @param topMargin Top Margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMarginTop(final View view, final int topMargin) { + return setMarginTop(view, topMargin, true); + } + + /** + * 设置 View Top Margin + * @param view {@link View} + * @param topMargin Top Margin + * @param reset 是否重置清空其他 margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMarginTop(final View view, final int topMargin, final boolean reset) { + if (view != null) { + // 判断是否属于 ViewGroup.MarginLayoutParams + if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + if (reset) { + try { + ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).setMargins(0, topMargin, 0, 0); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMarginTop"); + } + } else { + try { + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); + // 设置边距 + ((ViewGroup.MarginLayoutParams) view.getLayoutParams()) + .setMargins(layoutParams.leftMargin, topMargin, + layoutParams.rightMargin, layoutParams.bottomMargin); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMarginTop"); + } + } + } + } + return false; + } + + /** + * 设置 View Right Margin + * @param view {@link View} + * @param rightMargin Right Margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMarginRight(final View view, final int rightMargin) { + return setMarginRight(view, rightMargin, true); + } + + /** + * 设置 View Right Margin + * @param view {@link View} + * @param rightMargin Right Margin + * @param reset 是否重置清空其他 margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMarginRight(final View view, final int rightMargin, final boolean reset) { + if (view != null) { + // 判断是否属于 ViewGroup.MarginLayoutParams + if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + if (reset) { + try { + ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).setMargins(0, 0, rightMargin, 0); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMarginRight"); + } + } else { + try { + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); + // 设置边距 + ((ViewGroup.MarginLayoutParams) view.getLayoutParams()) + .setMargins(layoutParams.leftMargin, layoutParams.topMargin, + rightMargin, layoutParams.bottomMargin); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMarginRight"); + } + } + } + } + return false; + } + + /** + * 设置 View Bottom Margin + * @param view {@link View} + * @param bottomMargin Bottom Margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMarginBottom(final View view, final int bottomMargin) { + return setMarginBottom(view, bottomMargin, true); + } + + /** + * 设置 View Bottom Margin + * @param view {@link View} + * @param bottomMargin Bottom Margin + * @param reset 是否重置清空其他 margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMarginBottom(final View view, final int bottomMargin, final boolean reset) { + if (view != null) { + // 判断是否属于 ViewGroup.MarginLayoutParams + if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + if (reset) { + try { + ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).setMargins(0, 0, 0, bottomMargin); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMarginBottom"); + } + } else { + try { + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); + // 设置边距 + ((ViewGroup.MarginLayoutParams) view.getLayoutParams()) + .setMargins(layoutParams.leftMargin, layoutParams.topMargin, + layoutParams.rightMargin, bottomMargin); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMarginBottom"); + } + } + } + } + return false; + } + + /** + * 设置 Margin 边距 + * @param view {@link View} + * @param leftRight Left and Right Margin + * @param topBottom Top and bottom Margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMargin(final View view, final int leftRight, final int topBottom) { + return setMargin(view, leftRight, topBottom, leftRight, topBottom); + } + + /** + * 设置 Margin 边距 + * @param view {@link View} + * @param margin Margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMargin(final View view, final int margin) { + return setMargin(view, margin, margin, margin, margin); + } + + /** + * 设置 Margin 边距 + * @param view {@link View} + * @param left Left Margin + * @param top Top Margin + * @param right Right Margin + * @param bottom Bottom Margin + * @return {@code true} success, {@code false} fail + */ + public static boolean setMargin(final View view, final int left, final int top, final int right, final int bottom) { + if (view != null) { + // 判断是否属于 ViewGroup.MarginLayoutParams + if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + try { + // 设置边距 + ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).setMargins(left, top, right, bottom); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setMargin"); + } + } + } + return false; + } + + // = + + /** + * 设置 Margin 边距 + * @param views View[] + * @param leftRight Left and Right Margin + * @param topBottom Top and bottom Margin + */ + public static void setMargin(final View[] views, final int leftRight, final int topBottom) { + setMargin(views, leftRight, topBottom, leftRight, topBottom); + } + + /** + * 设置 Margin 边距 + * @param views View[] + * @param margin Margin + */ + public static void setMargin(final View[] views, final int margin) { + setMargin(views, margin, margin, margin, margin); + } + + /** + * 设置 Margin 边距 + * @param views View[] + * @param left Left Margin + * @param top Top Margin + * @param right Right Margin + * @param bottom Bottom Margin + */ + public static void setMargin(final View[] views, final int left, final int top, final int right, final int bottom) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + setMargin(views[i], left, top, right, bottom); + } + } + } + + // ================ + // = View Padding = + // ================ + + /** + * 获取 View Left Padding + * @param view {@link View} + * @return Left Padding + */ + public static int getPaddingLeft(final View view) { + return getPadding(view)[0]; + } + + /** + * 获取 View Top Padding + * @param view {@link View} + * @return Top Padding + */ + public static int getPaddingTop(final View view) { + return getPadding(view)[1]; + } + + /** + * 获取 View Right Padding + * @param view {@link View} + * @return Right Padding + */ + public static int getPaddingRight(final View view) { + return getPadding(view)[2]; + } + + /** + * 获取 View Bottom Padding + * @param view {@link View} + * @return Bottom Padding + */ + public static int getPaddingBottom(final View view) { + return getPadding(view)[3]; + } + + /** + * 获取 View Padding + * @param view {@link View} + * @return new int[] {left, top, right, bottom} + */ + public static int[] getPadding(final View view) { + int[] paddings = new int[]{0, 0, 0, 0}; + if (view != null) { + paddings[0] = view.getPaddingLeft(); + paddings[1] = view.getPaddingTop(); + paddings[2] = view.getPaddingRight(); + paddings[3] = view.getPaddingBottom(); + } + return paddings; + } + + // = + + /** + * 设置 View Left Padding + * @param view {@link View} + * @param leftPadding Left Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPaddingLeft(final View view, final int leftPadding) { + return setPaddingLeft(view, leftPadding, true); + } + + /** + * 设置 View Left Padding + * @param view {@link View} + * @param leftPadding Left Padding + * @param reset 是否重置清空其他 Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPaddingLeft(final View view, final int leftPadding, final boolean reset) { + if (view != null) { + if (reset) { + try { + view.setPadding(leftPadding, 0, 0, 0); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPaddingLeft"); + } + } else { + try { + view.setPadding(leftPadding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPaddingLeft"); + } + } + } + return false; + } + + /** + * 设置 View Top Padding + * @param view {@link View} + * @param topPadding Top Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPaddingTop(final View view, final int topPadding) { + return setPaddingTop(view, topPadding, true); + } + + /** + * 设置 View Top Padding + * @param view {@link View} + * @param topPadding Top Padding + * @param reset 是否重置清空其他 Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPaddingTop(final View view, final int topPadding, final boolean reset) { + if (view != null) { + if (reset) { + try { + view.setPadding(0, topPadding, 0, 0); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPaddingTop"); + } + } else { + try { + view.setPadding(view.getPaddingLeft(), topPadding, view.getPaddingRight(), view.getPaddingBottom()); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPaddingTop"); + } + } + } + return false; + } + + /** + * 设置 View Right Padding + * @param view {@link View} + * @param rightPadding Right Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPaddingRight(final View view, final int rightPadding) { + return setPaddingRight(view, rightPadding, true); + } + + /** + * 设置 View Right Padding + * @param view {@link View} + * @param rightPadding Right Padding + * @param reset 是否重置清空其他 Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPaddingRight(final View view, final int rightPadding, final boolean reset) { + if (view != null) { + if (reset) { + try { + view.setPadding(0, 0, rightPadding, 0); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPaddingRight"); + } + } else { + try { + view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), rightPadding, view.getPaddingBottom()); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPaddingRight"); + } + } + } + return false; + } + + /** + * 设置 View Bottom Padding + * @param view {@link View} + * @param bottomPadding Bottom Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPaddingBottom(final View view, final int bottomPadding) { + return setPaddingBottom(view, bottomPadding, true); + } + + /** + * 设置 View Bottom Padding + * @param view {@link View} + * @param bottomPadding Bottom Padding + * @param reset 是否重置清空其他 Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPaddingBottom(final View view, final int bottomPadding, final boolean reset) { + if (view != null) { + if (reset) { + try { + view.setPadding(0, 0, 0, bottomPadding); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPaddingBottom"); + } + } else { + try { + view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), bottomPadding); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPaddingBottom"); + } + } + } + return false; + } + + /** + * 设置 Padding 边距 + * @param view {@link View} + * @param leftRight Left and Right Padding + * @param topBottom Top and bottom Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPadding(final View view, final int leftRight, final int topBottom) { + return setPadding(view, leftRight, topBottom, leftRight, topBottom); + } + + /** + * 设置 Padding 边距 + * @param view {@link View} + * @param padding Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPadding(final View view, final int padding) { + return setPadding(view, padding, padding, padding, padding); + } + + /** + * 设置 Padding 边距 + * @param view {@link View} + * @param left Left Padding + * @param top Top Padding + * @param right Right Padding + * @param bottom Bottom Padding + * @return {@code true} success, {@code false} fail + */ + public static boolean setPadding(final View view, final int left, final int top, final int right, final int bottom) { + if (view != null) { + try { + view.setPadding(left, top, right, bottom); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPadding"); + } + } + return false; + } + + // = + + /** + * 设置 Padding 边距 + * @param views View[] + * @param leftRight Left and Right Padding + * @param topBottom Top and bottom Padding + */ + public static void setPadding(final View[] views, final int leftRight, final int topBottom) { + setPadding(views, leftRight, topBottom, leftRight, topBottom); + } + + /** + * 设置 Padding 边距 + * @param views View[] + * @param padding Padding + */ + public static void setPadding(final View[] views, final int padding) { + setPadding(views, padding, padding, padding, padding); + } + + /** + * 设置 Padding 边距 + * @param views View[] + * @param left Left Padding + * @param top Top Padding + * @param right Right Padding + * @param bottom Bottom Padding + */ + public static void setPadding(final View[] views, final int left, final int top, final int right, final int bottom) { + if (views != null) { + for (int i = 0, len = views.length; i < len; i++) { + setPadding(views[i], left, top, right, bottom); + } + } + } + + // ===================== + // = CompoundDrawables = + // ===================== + + /** + * 获取 CompoundDrawables + * @param textView {@link TextView} + * @param 泛型 + * @return Drawable[] { left, top, right, bottom } + */ + public static Drawable[] getCompoundDrawables(final T textView) { + if (textView != null) { + return textView.getCompoundDrawables(); + } + return new Drawable[]{null, null, null, null}; + } + + /** + * 获取 CompoundDrawables Padding + * @param textView {@link TextView} + * @param 泛型 + * @return CompoundDrawables Padding + */ + public static int getCompoundDrawablePadding(final T textView) { + if (textView != null) { + return textView.getCompoundDrawablePadding(); + } + return 0; + } + + // ======================== + // = setCompoundDrawables = + // ======================== + + /** + * 设置 Left CompoundDrawables + * @param textView {@link TextView} + * @param left left Drawable + * @param 泛型 + * @return {@link View} + */ + public static T setCompoundDrawablesByLeft(final T textView, final Drawable left) { + return setCompoundDrawables(textView, left, null, null, null); + } + + /** + * 设置 Top CompoundDrawables + * @param textView {@link TextView} + * @param top top Drawable + * @param 泛型 + * @return {@link View} + */ + public static T setCompoundDrawablesByTop(final T textView, final Drawable top) { + return setCompoundDrawables(textView, null, top, null, null); + } + + /** + * 设置 Right CompoundDrawables + * @param textView {@link TextView} + * @param right right Drawable + * @param 泛型 + * @return {@link View} + */ + public static T setCompoundDrawablesByRight(final T textView, final Drawable right) { + return setCompoundDrawables(textView, null, null, right, null); + } + + /** + * 设置 Bottom CompoundDrawables + * @param textView {@link TextView} + * @param bottom bottom Drawable + * @param 泛型 + * @return {@link View} + */ + public static T setCompoundDrawablesByBottom(final T textView, final Drawable bottom) { + return setCompoundDrawables(textView, null, null, null, bottom); + } + + /** + * 设置 CompoundDrawables + *
+     *     CompoundDrawable 的大小控制是通过 drawable.setBounds() 控制
+     *     需要先设置 Drawable 的 setBounds
+     * 
+ * @param textView {@link TextView} + * @param left left Drawable + * @param top top Drawable + * @param right right Drawable + * @param bottom bottom Drawable + * @param 泛型 + * @return {@link View} + */ + public static T setCompoundDrawables(final T textView, + final Drawable left, final Drawable top, + final Drawable right, final Drawable bottom) { + if (textView != null) { + try { + textView.setCompoundDrawables(left, top, right, bottom); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setCompoundDrawables"); + } + } + return textView; + } + + // =========================================== + // = setCompoundDrawablesWithIntrinsicBounds = + // =========================================== + + /** + * 设置 Left CompoundDrawables - 按照原有比例大小显示图片 + * @param textView {@link TextView} + * @param left left Drawable + * @param 泛型 + * @return {@link View} + */ + public static T setCompoundDrawablesWithIntrinsicBoundsByLeft(final T textView, final Drawable left) { + return setCompoundDrawablesWithIntrinsicBounds(textView, left, null, null, null); + } + + /** + * 设置 Top CompoundDrawables - 按照原有比例大小显示图片 + * @param textView {@link TextView} + * @param top top Drawable + * @param 泛型 + * @return {@link View} + */ + public static T setCompoundDrawablesWithIntrinsicBoundsByTop(final T textView, final Drawable top) { + return setCompoundDrawablesWithIntrinsicBounds(textView, null, top, null, null); + } + + /** + * 设置 Right CompoundDrawables - 按照原有比例大小显示图片 + * @param textView {@link TextView} + * @param right right Drawable + * @param 泛型 + * @return {@link View} + */ + public static T setCompoundDrawablesWithIntrinsicBoundsByRight(final T textView, final Drawable right) { + return setCompoundDrawablesWithIntrinsicBounds(textView, null, null, right, null); + } + + /** + * 设置 Bottom CompoundDrawables - 按照原有比例大小显示图片 + * @param textView {@link TextView} + * @param bottom bottom Drawable + * @param 泛型 + * @return {@link View} + */ + public static T setCompoundDrawablesWithIntrinsicBoundsByBottom(final T textView, final Drawable bottom) { + return setCompoundDrawablesWithIntrinsicBounds(textView, null, null, null, bottom); + } + + /** + * 设置 CompoundDrawables - 按照原有比例大小显示图片 + * @param textView {@link TextView} + * @param left left Drawable + * @param top top Drawable + * @param right right Drawable + * @param bottom bottom Drawable + * @param 泛型 + * @return {@link View} + */ + public static T setCompoundDrawablesWithIntrinsicBounds(final T textView, + final Drawable left, final Drawable top, + final Drawable right, final Drawable bottom) { + if (textView != null) { + try { + textView.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setCompoundDrawablesWithIntrinsicBounds"); + } + } + return textView; + } + + // ============ + // = ListView = + // ============ + + /** + * 计算 ListView Item 高度 + * @param listView {@link ListView} + * @return ListView Item 高度 + */ + public static int calcListViewItemHeight(final ListView listView) { + return calcListViewItemHeight(listView, false); + } + + /** + * 计算 ListView Item 高度 + * @param listView {@link ListView} + * @param setParams 是否 setLayoutParams + * @return ListView Item 高度 + */ + public static int calcListViewItemHeight(final ListView listView, final boolean setParams) { + // 获取 Adapter + ListAdapter listAdapter = listView.getAdapter(); + // 防止为 null + if (listAdapter == null) { + return 0; + } + // 获取总体高度 + int totalHeight = 0; + // 遍历累加 + for (int i = 0, len = listAdapter.getCount(); i < len; i++) { + // 累加 Item 高度 + totalHeight += getItemHeighet(listAdapter, listView, i); + } + // = + // 获取子项间分隔符占用的高度 + // listView.getDividerHeight(); + // 累加分割线高度 + totalHeight += (listView.getDividerHeight() * (listAdapter.getCount() - 1)); + // 判断是否需要设置高度 + if (setParams) { + ViewGroup.LayoutParams params = listView.getLayoutParams(); + params.height = totalHeight; + listView.setLayoutParams(params); + } + // 返回总体高度 + return totalHeight; + } + + // ============ + // = GridView = + // ============ + + /** + * 计算 GridView Item 高度 + * @param gridView {@link GridView} + * @param numColumns GridView 行数 + * @return GridView Item 高度 + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) + public static int calcGridViewItemHeight(final GridView gridView, final int numColumns) { + return calcGridViewItemHeight(gridView, numColumns, false); + } + + /** + * 计算 GridView Item 高度 + * @param gridView {@link GridView} + * @param numColumns GridView 行数 + * @param setParams 是否 setLayoutParams + * @return GridView Item 高度 + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) + public static int calcGridViewItemHeight(final GridView gridView, final int numColumns, final boolean setParams) { + // 获取 Adapter + ListAdapter listAdapter = gridView.getAdapter(); + // 防止为 null + if (listAdapter == null) { + return 0; + } + // 最高高度 + int singleMax = 0; + // 获取总体高度 + int totalHeight = 0; + // 获取总数 + int count = listAdapter.getCount(); + // 判断是否整数 + count = (count % numColumns == 0) ? (count / numColumns) : (count / numColumns + 1); + // 遍历累加 + for (int i = 0; i < count; i++) { + // 默认表示第一个的高度 + singleMax = getItemHeighet(listAdapter, gridView, i * numColumns); + // 遍历判断 + for (int eqI = 1; eqI < numColumns; eqI++) { + // 临时高度 + int tempHeight = 0; + // 进行判断处理 + if (i * numColumns + eqI <= count) { + // 获取对应的高度 + tempHeight = getItemHeighet(listAdapter, gridView, i * numColumns + eqI); + } + // 判断是否在最大高度 + if (tempHeight > singleMax) { + singleMax = tempHeight; + } + } + // 累加 Item 高度 + totalHeight += singleMax; + } + // 每列之间的间隔 | + // int hSpaec = gridView.getHorizontalSpacing(); + // 每行之间的间隔 - + int vSpace = gridView.getVerticalSpacing(); + // 最后获取整个 GridView 完整显示需要的高度 + totalHeight += (vSpace * (count - 1)); + // 判断是否需要设置高度 + if (setParams) { + ViewGroup.LayoutParams params = gridView.getLayoutParams(); + params.height = totalHeight; + gridView.setLayoutParams(params); + } + // 返回总体高度 + return totalHeight; + } + + // = + + /** + * 获取单独一个 Item 高度 + * @param absViews {@link AbsListView} + * @param pos 索引 + * @return Item 高度 + */ + public static int getItemHeighet(final AbsListView absViews, final int pos) { + return getItemHeighet(absViews, pos, 0); + } + + /** + * 获取单独一个 Item 高度 + * @param absViews {@link AbsListView} + * @param pos 索引 + * @param defaultHeight 默认高度 + * @return Item 高度 + */ + public static int getItemHeighet(final AbsListView absViews, final int pos, final int defaultHeight) { + if (absViews != null) { + return getItemHeighet(absViews.getAdapter(), absViews, pos, defaultHeight); + } + return defaultHeight; + } + + /** + * 获取单独一个 Item 高度 + * @param listAdapter {@link ListAdapter} + * @param absViews {@link AbsListView} + * @param pos 索引 + * @return Item 高度 + */ + public static int getItemHeighet(final ListAdapter listAdapter, final AbsListView absViews, final int pos) { + return getItemHeighet(listAdapter, absViews, pos, 0); + } + + /** + * 获取单独一个 Item 高度 + * @param listAdapter {@link ListAdapter} + * @param absViews {@link AbsListView} + * @param pos 索引 + * @param defaultHeight 默认高度 + * @return Item 高度 + */ + public static int getItemHeighet(final ListAdapter listAdapter, final AbsListView absViews, final int pos, final int defaultHeight) { + try { + // listAdapter.getCount() 返回数据项的数目 + View listItem = listAdapter.getView(pos, null, absViews); + // 计算子项 View 的宽高 + listItem.measure(0, 0); + // 统计所有子项的总高度 + return listItem.getMeasuredHeight(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getItemHeighet"); + return defaultHeight; + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/anim/AnimationUtils.java b/lib/DevApp/src/main/java/dev/utils/app/anim/AnimationUtils.java new file mode 100644 index 0000000000..2052702852 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/anim/AnimationUtils.java @@ -0,0 +1,636 @@ +package dev.utils.app.anim; + +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; +import android.view.animation.AnticipateInterpolator; +import android.view.animation.AnticipateOvershootInterpolator; +import android.view.animation.BounceInterpolator; +import android.view.animation.CycleInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.OvershootInterpolator; +import android.view.animation.RotateAnimation; +import android.view.animation.ScaleAnimation; +import android.view.animation.TranslateAnimation; + +/** + * detail: 动画工具类 + * @author Ttt + *
+ *     Android 动画机制详细解读
+ *     @see 
+ *     @see 
+ *     

+ * setInterpolator: + * {@link AccelerateInterpolator} 先减速后加速 + * {@link AnticipateInterpolator} 动画开始之前有回弹效果 + * {@link BounceInterpolator} 结束回弹效果 + * {@link CycleInterpolator} 跳一跳效果 + * {@link OvershootInterpolator} 动画结束时向前弹一定距离再回到原来位置 + * {@link AccelerateDecelerateInterpolator} 系统默认的动画效果, 先加速后减速 + * {@link AnticipateOvershootInterpolator} 开始之前向前甩, 结束的时候向后甩 + * {@link DecelerateInterpolator} 开始加速再减速 + *

+ * 设置无限重复次数 + * {@link Animation#setRepeatCount} param {@link Animation#INFINITE} + *
+ */ +public final class AnimationUtils { + + private AnimationUtils() { + } + + // 默认动画持续时间 + public static final long DEFAULT_ANIMATION_DURATION = 400; + + /** + * 设置动画重复处理 + * @param anim 动画 + * @param repeatCount 执行次数 + * @param repeatMode 重复模式 {@link Animation#RESTART} 重新从头开始执行、{@link Animation#REVERSE} 反方向执行 + * @param 泛型 + * @return 动画 + */ + public static T setAnimRepeat(final T anim, final int repeatCount, final int repeatMode) { + if (anim != null) { + anim.setRepeatCount(repeatCount); + anim.setRepeatMode(repeatMode); + } + return anim; + } + + // ================ + // = 视图旋转动画 = + // ================ + + /** + * 获取一个旋转动画 + * @param fromDegrees 开始角度 + * @param toDegrees 结束角度 + * @param pivotXType 旋转中心点 X 轴坐标相对类型 + * @param pivotXValue 旋转中心点 X 轴坐标 + * @param pivotYType 旋转中心点 Y 轴坐标相对类型 + * @param pivotYValue 旋转中心点 Y 轴坐标 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个旋转动画 + */ + public static RotateAnimation getRotateAnimation(final float fromDegrees, final float toDegrees, final int pivotXType, final float pivotXValue, + final int pivotYType, final float pivotYValue, final long durationMillis, + final AnimationListener animationListener) { + RotateAnimation rotateAnimation = new RotateAnimation(fromDegrees, toDegrees, pivotXType, pivotXValue, pivotYType, pivotYValue); + rotateAnimation.setDuration(durationMillis); + if (animationListener != null) { + rotateAnimation.setAnimationListener(animationListener); + } + return rotateAnimation; + } + + /** + * 获取一个旋转动画 + * @param fromDegrees 开始角度 + * @param toDegrees 结束角度 + * @param pivotX 旋转中心点 X 轴坐标 + * @param pivotY 旋转中心点 Y 轴坐标 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个旋转动画 + */ + public static RotateAnimation getRotateAnimation(final float fromDegrees, final float toDegrees, final float pivotX, final float pivotY, + final long durationMillis, final AnimationListener animationListener) { + RotateAnimation rotateAnimation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY); + rotateAnimation.setDuration(durationMillis); + if (animationListener != null) { + rotateAnimation.setAnimationListener(animationListener); + } + return rotateAnimation; + } + + /** + * 获取一个旋转动画 + * @param fromDegrees 开始角度 + * @param toDegrees 结束角度 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个旋转动画 + */ + public static RotateAnimation getRotateAnimation(final float fromDegrees, final float toDegrees, final long durationMillis, + final AnimationListener animationListener) { + RotateAnimation rotateAnimation = new RotateAnimation(fromDegrees, toDegrees); + rotateAnimation.setDuration(durationMillis); + if (animationListener != null) { + rotateAnimation.setAnimationListener(animationListener); + } + return rotateAnimation; + } + + // = + + /** + * 获取一个根据视图自身中心点旋转的动画 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个根据中心点旋转的动画 + */ + public static RotateAnimation getRotateAnimationByCenter(final long durationMillis, final AnimationListener animationListener) { + return getRotateAnimation(0f, 359f, Animation.RELATIVE_TO_SELF, + 0.5f, Animation.RELATIVE_TO_SELF, 0.5f, durationMillis, animationListener); + } + + /** + * 获取一个根据视图自身中心点旋转的动画 + * @param duration 动画持续时间 + * @return 一个根据中心点旋转的动画 + */ + public static RotateAnimation getRotateAnimationByCenter(final long duration) { + return getRotateAnimationByCenter(duration, null); + } + + /** + * 获取一个根据视图自身中心点旋转的动画 + * @param animationListener 动画监听器 + * @return 一个根据中心点旋转的动画 + */ + public static RotateAnimation getRotateAnimationByCenter(final AnimationListener animationListener) { + return getRotateAnimationByCenter(DEFAULT_ANIMATION_DURATION, animationListener); + } + + /** + * 获取一个根据视图自身中心点旋转的动画 + * @return 一个根据中心点旋转的动画 + */ + public static RotateAnimation getRotateAnimationByCenter() { + return getRotateAnimationByCenter(DEFAULT_ANIMATION_DURATION, null); + } + + // ================ + // = 视图渐变动画 = + // ================ + + /** + * 获取一个透明度渐变动画 + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个透明度渐变动画 + */ + public static AlphaAnimation getAlphaAnimation(final float fromAlpha, final float toAlpha, final long durationMillis, + final AnimationListener animationListener) { + AlphaAnimation alphaAnimation = new AlphaAnimation(fromAlpha, toAlpha); + alphaAnimation.setDuration(durationMillis); + if (animationListener != null) { + alphaAnimation.setAnimationListener(animationListener); + } + return alphaAnimation; + } + + /** + * 获取一个透明度渐变动画 + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度 + * @param durationMillis 动画持续时间 + * @return 一个透明度渐变动画 + */ + public static AlphaAnimation getAlphaAnimation(final float fromAlpha, final float toAlpha, final long durationMillis) { + return getAlphaAnimation(fromAlpha, toAlpha, durationMillis, null); + } + + /** + * 获取一个透明度渐变动画 + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度 + * @param animationListener 动画监听器 + * @return 一个透明度渐变动画 + */ + public static AlphaAnimation getAlphaAnimation(final float fromAlpha, final float toAlpha, final AnimationListener animationListener) { + return getAlphaAnimation(fromAlpha, toAlpha, DEFAULT_ANIMATION_DURATION, animationListener); + } + + /** + * 获取一个透明度渐变动画 + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度 + * @return 一个透明度渐变动画 + */ + public static AlphaAnimation getAlphaAnimation(final float fromAlpha, final float toAlpha) { + return getAlphaAnimation(fromAlpha, toAlpha, DEFAULT_ANIMATION_DURATION, null); + } + + // = + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个由完全显示变为不可见的透明度渐变动画 + */ + public static AlphaAnimation getHiddenAlphaAnimation(final long durationMillis, final AnimationListener animationListener) { + return getAlphaAnimation(1.0f, 0.0f, durationMillis, animationListener); + } + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * @param durationMillis 动画持续时间 + * @return 一个由完全显示变为不可见的透明度渐变动画 + */ + public static AlphaAnimation getHiddenAlphaAnimation(final long durationMillis) { + return getHiddenAlphaAnimation(durationMillis, null); + } + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * @param animationListener 动画监听器 + * @return 一个由完全显示变为不可见的透明度渐变动画 + */ + public static AlphaAnimation getHiddenAlphaAnimation(final AnimationListener animationListener) { + return getHiddenAlphaAnimation(DEFAULT_ANIMATION_DURATION, animationListener); + } + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * @return 一个由完全显示变为不可见的透明度渐变动画 + */ + public static AlphaAnimation getHiddenAlphaAnimation() { + return getHiddenAlphaAnimation(DEFAULT_ANIMATION_DURATION, null); + } + + // = + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个由不可见变为完全显示的透明度渐变动画 + */ + public static AlphaAnimation getShowAlphaAnimation(final long durationMillis, final AnimationListener animationListener) { + return getAlphaAnimation(0.0f, 1.0f, durationMillis, animationListener); + } + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * @param durationMillis 动画持续时间 + * @return 一个由不可见变为完全显示的透明度渐变动画 + */ + public static AlphaAnimation getShowAlphaAnimation(final long durationMillis) { + return getShowAlphaAnimation(durationMillis, null); + } + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * @param animationListener 动画监听器 + * @return 一个由不可见变为完全显示的透明度渐变动画 + */ + public static AlphaAnimation getShowAlphaAnimation(final AnimationListener animationListener) { + return getShowAlphaAnimation(DEFAULT_ANIMATION_DURATION, animationListener); + } + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * @return 一个由不可见变为完全显示的透明度渐变动画 + */ + public static AlphaAnimation getShowAlphaAnimation() { + return getShowAlphaAnimation(DEFAULT_ANIMATION_DURATION, null); + } + + // ================ + // = 视图缩放动画 = + // ================ + + /** + * 获取一个缩放动画 + * @param fromX 动画开始前在 X 坐标 + * @param toX 动画结束后在 X 坐标 + * @param fromY 动画开始前在 Y 坐标 + * @param toY 动画结束后在 Y 坐标 + * @param pivotXType 缩放中心点的 X 坐标类型, 取值范围为 {@link Animation#ABSOLUTE}、 + * {@link Animation#RELATIVE_TO_SELF}、{@link Animation#RELATIVE_TO_PARENT} + * @param pivotXValue 缩放中心点的 X 坐标, 当 pivotXType == ABSOLUTE 时, 表示绝对位置, 否则表示相对位置, 1.0 表示 100% + * @param pivotYType 缩放中心点的 Y 坐标类型 + * @param pivotYValue 缩放中心点的 Y 坐标 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个缩放动画 + */ + public static ScaleAnimation getScaleAnimation(final float fromX, final float toX, final float fromY, final float toY, + final int pivotXType, final float pivotXValue, final int pivotYType, final float pivotYValue, + final long durationMillis, final AnimationListener animationListener) { + ScaleAnimation scaleAnimation = new ScaleAnimation(fromX, toX, fromY, toY, pivotXType, pivotXValue, pivotYType, pivotYValue); + scaleAnimation.setDuration(durationMillis); + if (animationListener != null) { + scaleAnimation.setAnimationListener(animationListener); + } + return scaleAnimation; + } + + /** + * 获取一个缩放动画 + * @param fromX 动画开始前在 X 坐标 + * @param toX 动画结束后在 X 坐标 + * @param fromY 动画开始前在 Y 坐标 + * @param toY 动画结束后在 Y 坐标 + * @param pivotX 缩放中心点的 X 坐标 + * @param pivotY 缩放中心点的 Y 坐标 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个缩放动画 + */ + public static ScaleAnimation getScaleAnimation(final float fromX, final float toX, final float fromY, final float toY, + final float pivotX, final float pivotY, final long durationMillis, + final AnimationListener animationListener) { + ScaleAnimation scaleAnimation = new ScaleAnimation(fromX, toX, fromY, toY, pivotX, pivotY); + scaleAnimation.setDuration(durationMillis); + if (animationListener != null) { + scaleAnimation.setAnimationListener(animationListener); + } + return scaleAnimation; + } + + /** + * 获取一个缩放动画 + * @param fromX 动画开始前在 X 坐标 + * @param toX 动画结束后在 X 坐标 + * @param fromY 动画开始前在 Y 坐标 + * @param toY 动画结束后在 Y 坐标 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个缩放动画 + */ + public static ScaleAnimation getScaleAnimation(final float fromX, final float toX, final float fromY, final float toY, + final long durationMillis, final AnimationListener animationListener) { + ScaleAnimation scaleAnimation = new ScaleAnimation(fromX, toX, fromY, toY); + scaleAnimation.setDuration(durationMillis); + if (animationListener != null) { + scaleAnimation.setAnimationListener(animationListener); + } + return scaleAnimation; + } + + // = + + /** + * 获取一个中心点缩放动画 + * @param fromX 动画开始前在 X 坐标 + * @param toX 动画结束后在 X 坐标 + * @param fromY 动画开始前在 Y 坐标 + * @param toY 动画结束后在 Y 坐标 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个中心点缩放动画 + */ + public static ScaleAnimation getScaleAnimationCenter(final float fromX, final float toX, final float fromY, final float toY, + final long durationMillis, final AnimationListener animationListener) { + ScaleAnimation scaleAnimation = new ScaleAnimation(fromX, toX, fromY, toY, + ScaleAnimation.RELATIVE_TO_SELF, 0.5f, + ScaleAnimation.RELATIVE_TO_SELF, 0.5f); + scaleAnimation.setDuration(durationMillis); + if (animationListener != null) { + scaleAnimation.setAnimationListener(animationListener); + } + return scaleAnimation; + } + + /** + * 获取一个中心点缩放动画 + * @param fromX 动画开始前在 X 坐标 + * @param toX 动画结束后在 X 坐标 + * @param fromY 动画开始前在 Y 坐标 + * @param toY 动画结束后在 Y 坐标 + * @param durationMillis 动画持续时间 + * @return 一个中心点缩放动画 + */ + public static ScaleAnimation getScaleAnimationCenter(final float fromX, final float toX, final float fromY, final float toY, + final long durationMillis) { + return getScaleAnimationCenter(fromX, toX, fromY, toY, durationMillis, null); + } + + /** + * 获取一个中心点缩放动画 + * @param fromX 动画开始前在 X 坐标 + * @param toX 动画结束后在 X 坐标 + * @param fromY 动画开始前在 Y 坐标 + * @param toY 动画结束后在 Y 坐标 + * @param animationListener 动画监听器 + * @return 一个中心点缩放动画 + */ + public static ScaleAnimation getScaleAnimationCenter(final float fromX, final float toX, final float fromY, final float toY, + final AnimationListener animationListener) { + return getScaleAnimationCenter(fromX, toX, fromY, toY, DEFAULT_ANIMATION_DURATION, animationListener); + } + + // = + + /** + * 获取一个中心点缩放动画 + * @param fromX 动画开始前在 X 坐标 + * @param fromY 动画开始前在 Y 坐标 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个中心点缩放动画 + */ + public static ScaleAnimation getScaleAnimationCenter(final float fromX, final float fromY, final long durationMillis, + final AnimationListener animationListener) { + return getScaleAnimationCenter(fromX, 1.0f, fromY, 1.0f, durationMillis, animationListener); + } + + /** + * 获取一个中心点缩放动画 + * @param fromX 动画开始前在 X 坐标 + * @param fromY 动画开始前在 Y 坐标 + * @param durationMillis 动画持续时间 + * @return 一个中心点缩放动画 + */ + public static ScaleAnimation getScaleAnimationCenter(final float fromX, final float fromY, final long durationMillis) { + return getScaleAnimationCenter(fromX, fromY, durationMillis, null); + } + + /** + * 获取一个中心点缩放动画 + * @param fromX 动画开始前在 X 坐标 + * @param fromY 动画开始前在 Y 坐标 + * @param animationListener 动画监听器 + * @return 一个中心点缩放动画 + */ + public static ScaleAnimation getScaleAnimationCenter(final float fromX, final float fromY, final AnimationListener animationListener) { + return getScaleAnimationCenter(fromX, fromY, DEFAULT_ANIMATION_DURATION, animationListener); + } + + // = + + /** + * 获取一个缩小动画 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个缩小动画 + */ + public static ScaleAnimation getLessenScaleAnimation(final long durationMillis, final AnimationListener animationListener) { + ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f, + ScaleAnimation.RELATIVE_TO_SELF, ScaleAnimation.RELATIVE_TO_SELF); + scaleAnimation.setDuration(durationMillis); + if (animationListener != null) { + scaleAnimation.setAnimationListener(animationListener); + } + return scaleAnimation; + } + + /** + * 获取一个缩小动画 + * @param durationMillis 动画持续时间 + * @return 一个缩小动画 + */ + public static ScaleAnimation getLessenScaleAnimation(final long durationMillis) { + return getLessenScaleAnimation(durationMillis, null); + } + + /** + * 获取一个缩小动画 + * @param animationListener 动画监听器 + * @return 一个缩小动画 + */ + public static ScaleAnimation getLessenScaleAnimation(final AnimationListener animationListener) { + return getLessenScaleAnimation(DEFAULT_ANIMATION_DURATION, animationListener); + } + + // = + + /** + * 获取一个放大动画 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个放大动画 + */ + public static ScaleAnimation getAmplificationAnimation(final long durationMillis, final AnimationListener animationListener) { + ScaleAnimation scaleAnimation = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, + ScaleAnimation.RELATIVE_TO_SELF, ScaleAnimation.RELATIVE_TO_SELF); + scaleAnimation.setDuration(durationMillis); + if (animationListener != null) { + scaleAnimation.setAnimationListener(animationListener); + } + return scaleAnimation; + } + + /** + * 获取一个放大动画 + * @param durationMillis 动画持续时间 + * @return 一个放大动画 + */ + public static ScaleAnimation getAmplificationAnimation(final long durationMillis) { + return getAmplificationAnimation(durationMillis, null); + } + + /** + * 获取一个放大动画 + * @param animationListener 动画监听器 + * @return 一个放大动画 + */ + public static ScaleAnimation getAmplificationAnimation(final AnimationListener animationListener) { + return getAmplificationAnimation(DEFAULT_ANIMATION_DURATION, animationListener); + } + + // ================ + // = 视图移动动画 = + // ================ + + /** + * 获取一个视图移动动画 + * @param fromXType 动画开始前的 X 坐标类型, 取值范围为 {@link Animation#ABSOLUTE} 绝对位置、 + * {@link Animation#RELATIVE_TO_SELF} 以自身宽或高为参考、 + * {@link Animation#RELATIVE_TO_PARENT} 以父控件宽或高为参考 + * @param fromXValue 动画开始前的 X 坐标 + * @param toXType 动画结束后的 X 坐标类型 + * @param toXValue 动画结束后的 X 坐标 + * @param fromYType 动画开始前的 Y 坐标类型 + * @param fromYValue 动画开始前的 Y 坐标 + * @param toYType 动画结束后的 Y 坐标类型 + * @param toYValue 动画结束后的 Y 坐标 + * @param interpolator 动画周期 + * @param durationMillis 动画持续时间 + * @return 一个视图移动动画 + */ + public static TranslateAnimation getTranslateAnimation(final int fromXType, final float fromXValue, final int toXType, final float toXValue, + final int fromYType, final float fromYValue, final int toYType, final float toYValue, + final Interpolator interpolator, final long durationMillis) { + TranslateAnimation translateAnimation = new TranslateAnimation(fromXType, fromXValue, toXType, toXValue, fromYType, fromYValue, toYType, toYValue); + translateAnimation.setDuration(durationMillis); + if (interpolator != null) { + translateAnimation.setInterpolator(interpolator); + } + return translateAnimation; + } + + /** + * 获取一个视图移动动画 + * @param fromXDelta 动画开始的 X 轴坐标 + * @param toXDelta 动画结束的 X 轴坐标 + * @param fromYDelta 动画开始的 Y 轴坐标 + * @param toYDelta 动画结束的 Y 轴坐标 + * @param interpolator 动画周期 + * @param durationMillis 动画持续时间 + * @return 一个视图移动动画 + */ + public static TranslateAnimation getTranslateAnimation(final float fromXDelta, final float toXDelta, final float fromYDelta, final float toYDelta, + final Interpolator interpolator, final long durationMillis) { + TranslateAnimation translateAnimation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta); + translateAnimation.setDuration(durationMillis); + if (interpolator != null) { + translateAnimation.setInterpolator(interpolator); + } + return translateAnimation; + } + + // = + + /** + * 获取一个视图摇晃动画 + * @param fromXDelta 动画开始的 X 轴坐标 + * @param toXDelta 动画结束的 X 轴坐标 + * @param cycles 动画周期 {@link CycleInterpolator} + * @param durationMillis 动画持续时间 + * @return 一个视图摇晃动画 + */ + public static TranslateAnimation getShakeAnimation(final float fromXDelta, final float toXDelta, final float cycles, final long durationMillis) { + Interpolator interpolator = (cycles > 0.0f) ? new CycleInterpolator(cycles) : null; + return getTranslateAnimation(fromXDelta, toXDelta, 0.0f, 0.0f, interpolator, durationMillis); + } + + /** + * 获取一个视图摇晃动画 ( 摇晃幅度为 10) + * @param cycles 动画周期 {@link CycleInterpolator} + * @param durationMillis 动画持续时间 + * @return 一个视图摇晃动画 + */ + public static TranslateAnimation getShakeAnimation(final float cycles, final long durationMillis) { + Interpolator interpolator = (cycles > 0.0f) ? new CycleInterpolator(cycles) : null; + return getTranslateAnimation(0.0f, 10.0f, 0.0f, 0.0f, interpolator, durationMillis); + } + + /** + * 获取一个视图摇晃动画 ( 摇晃幅度为 10、持续 700 毫秒 ) + * @param cycles 动画周期 {@link CycleInterpolator} + * @return 一个视图摇晃动画 + */ + public static TranslateAnimation getShakeAnimation(final float cycles) { + Interpolator interpolator = (cycles > 0.0f) ? new CycleInterpolator(cycles) : null; + return getTranslateAnimation(0.0f, 10.0f, 0.0f, 0.0f, interpolator, 700); + } + + /** + * 获取一个视图摇晃动画 ( 摇晃幅度为 10、重复 7 次 ) + * @param durationMillis 动画持续时间 + * @return 一个视图摇晃动画 + */ + public static TranslateAnimation getShakeAnimation(final long durationMillis) { + return getTranslateAnimation(0.0f, 10.0f, 0.0f, 0.0f, new CycleInterpolator(7), durationMillis); + } + + /** + * 获取一个视图摇晃动画 ( 摇晃幅度为 10、重复 7 次、持续 700 毫秒 ) + * @return 一个视图摇晃动画 + */ + public static TranslateAnimation getShakeAnimation() { + return getTranslateAnimation(0.0f, 10.0f, 0.0f, 0.0f, new CycleInterpolator(7), 700); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/anim/ViewAnimationUtils.java b/lib/DevApp/src/main/java/dev/utils/app/anim/ViewAnimationUtils.java new file mode 100644 index 0000000000..2b4c0d68c7 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/anim/ViewAnimationUtils.java @@ -0,0 +1,541 @@ +package dev.utils.app.anim; + +import android.view.View; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; +import android.view.animation.CycleInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.TranslateAnimation; + +/** + * detail: 视图动画工具类 (AnimationUtils 基础上封装 ), 提供简单的控制视图的动画的工具方法 + * @author Ttt + */ +public final class ViewAnimationUtils { + + private ViewAnimationUtils() { + } + + // ====================== + // = 视图透明度渐变动画 = + // ====================== + + /** + * 将给定视图渐渐隐去 - view.setVisibility(View.INVISIBLE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + * @param animationListener 动画监听器 + */ + public static void invisibleViewByAlpha(final View view, final long durationMillis, final boolean isBanClick, + final AnimationListener animationListener) { + if (view != null && view.getVisibility() != View.INVISIBLE) { + view.setVisibility(View.INVISIBLE); + // 获取动画 + AlphaAnimation animation = AnimationUtils.getHiddenAlphaAnimation(durationMillis); + animation.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + if (isBanClick && view != null) { + view.setClickable(false); + } + if (animationListener != null) { + animationListener.onAnimationStart(animation); + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + if (animationListener != null) { + animationListener.onAnimationRepeat(animation); + } + } + + @Override + public void onAnimationEnd(Animation animation) { + if (isBanClick && view != null) { + view.setClickable(true); + } + if (animationListener != null) { + animationListener.onAnimationEnd(animation); + } + } + }); + view.startAnimation(animation); + } + } + + /** + * 将给定视图渐渐隐去 - view.setVisibility(View.INVISIBLE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + */ + public static void invisibleViewByAlpha(final View view, final long durationMillis, final AnimationListener animationListener) { + invisibleViewByAlpha(view, durationMillis, false, animationListener); + } + + /** + * 将给定视图渐渐隐去 - view.setVisibility(View.INVISIBLE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void invisibleViewByAlpha(final View view, final long durationMillis, final boolean isBanClick) { + invisibleViewByAlpha(view, durationMillis, isBanClick, null); + } + + /** + * 将给定视图渐渐隐去 - view.setVisibility(View.INVISIBLE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + */ + public static void invisibleViewByAlpha(final View view, final long durationMillis) { + invisibleViewByAlpha(view, durationMillis, false, null); + } + + /** + * 将给定视图渐渐隐去 - view.setVisibility(View.INVISIBLE) + * @param view 待处理的视图 + * @param isBanClick 在执行动画的过程中是否禁止点击 + * @param animationListener 动画监听器 + */ + public static void invisibleViewByAlpha(final View view, final boolean isBanClick, final AnimationListener animationListener) { + invisibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, animationListener); + } + + /** + * 将给定视图渐渐隐去 - view.setVisibility(View.INVISIBLE) + * @param view 待处理的视图 + * @param animationListener 动画监听器 + */ + public static void invisibleViewByAlpha(final View view, final AnimationListener animationListener) { + invisibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, false, animationListener); + } + + /** + * 将给定视图渐渐隐去 - view.setVisibility(View.INVISIBLE) + * @param view 待处理的视图 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void invisibleViewByAlpha(final View view, final boolean isBanClick) { + invisibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, null); + } + + /** + * 将给定视图渐渐隐去 - view.setVisibility(View.INVISIBLE) + * @param view 待处理的视图 + */ + public static void invisibleViewByAlpha(final View view) { + invisibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, false, null); + } + + // = + + /** + * 将给定视图渐渐隐去最后从界面中移除 - view.setVisibility(View.GONE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + * @param animationListener 动画监听器 + */ + public static void goneViewByAlpha(final View view, final long durationMillis, final boolean isBanClick, + final AnimationListener animationListener) { + if (view != null && view.getVisibility() != View.GONE) { + view.setVisibility(View.GONE); + // 获取动画 + AlphaAnimation animation = AnimationUtils.getHiddenAlphaAnimation(durationMillis); + animation.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + if (isBanClick && view != null) { + view.setClickable(false); + } + if (animationListener != null) { + animationListener.onAnimationStart(animation); + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + if (animationListener != null) { + animationListener.onAnimationRepeat(animation); + } + } + + @Override + public void onAnimationEnd(Animation animation) { + if (isBanClick && view != null) { + view.setClickable(true); + } + if (animationListener != null) { + animationListener.onAnimationEnd(animation); + } + } + }); + view.startAnimation(animation); + } + } + + /** + * 将给定视图渐渐隐去最后从界面中移除 - view.setVisibility(View.GONE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + */ + public static void goneViewByAlpha(final View view, final long durationMillis, final AnimationListener animationListener) { + goneViewByAlpha(view, durationMillis, false, animationListener); + } + + /** + * 将给定视图渐渐隐去最后从界面中移除 - view.setVisibility(View.GONE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void goneViewByAlpha(final View view, final long durationMillis, final boolean isBanClick) { + goneViewByAlpha(view, durationMillis, isBanClick, null); + } + + /** + * 将给定视图渐渐隐去最后从界面中移除 - view.setVisibility(View.GONE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + */ + public static void goneViewByAlpha(final View view, final long durationMillis) { + goneViewByAlpha(view, durationMillis, false, null); + } + + /** + * 将给定视图渐渐隐去最后从界面中移除 - view.setVisibility(View.GONE) + * @param view 待处理的视图 + * @param isBanClick 在执行动画的过程中是否禁止点击 + * @param animationListener 动画监听器 + */ + public static void goneViewByAlpha(final View view, final boolean isBanClick, final AnimationListener animationListener) { + goneViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, animationListener); + } + + /** + * 将给定视图渐渐隐去最后从界面中移除 - view.setVisibility(View.GONE) + * @param view 待处理的视图 + * @param animationListener 动画监听器 + */ + public static void goneViewByAlpha(final View view, final AnimationListener animationListener) { + goneViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, false, animationListener); + } + + /** + * 将给定视图渐渐隐去最后从界面中移除 - view.setVisibility(View.GONE) + * @param view 待处理的视图 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void goneViewByAlpha(final View view, final boolean isBanClick) { + goneViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, null); + } + + /** + * 将给定视图渐渐隐去最后从界面中移除 - view.setVisibility(View.GONE) + * @param view 待处理的视图 + */ + public static void goneViewByAlpha(final View view) { + goneViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, false, null); + } + + // = + + /** + * 将给定视图渐渐显示出来 - view.setVisibility(View.VISIBLE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + * @param animationListener 动画监听器 + */ + public static void visibleViewByAlpha(final View view, final long durationMillis, final boolean isBanClick, + final AnimationListener animationListener) { + if (view != null && view.getVisibility() != View.VISIBLE) { + view.setVisibility(View.VISIBLE); + // 获取动画 + AlphaAnimation animation = AnimationUtils.getShowAlphaAnimation(durationMillis); + animation.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + if (isBanClick && view != null) { + view.setClickable(false); + } + if (animationListener != null) { + animationListener.onAnimationStart(animation); + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + if (animationListener != null) { + animationListener.onAnimationRepeat(animation); + } + } + + @Override + public void onAnimationEnd(Animation animation) { + if (isBanClick && view != null) { + view.setClickable(true); + } + if (animationListener != null) { + animationListener.onAnimationEnd(animation); + } + } + }); + view.startAnimation(animation); + } + } + + /** + * 将给定视图渐渐显示出来 - view.setVisibility(View.VISIBLE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + */ + public static void visibleViewByAlpha(final View view, final long durationMillis, final AnimationListener animationListener) { + visibleViewByAlpha(view, durationMillis, false, animationListener); + } + + /** + * 将给定视图渐渐显示出来 - view.setVisibility(View.VISIBLE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void visibleViewByAlpha(final View view, final long durationMillis, final boolean isBanClick) { + visibleViewByAlpha(view, durationMillis, isBanClick, null); + } + + /** + * 将给定视图渐渐显示出来 - view.setVisibility(View.VISIBLE) + * @param view 待处理的视图 + * @param durationMillis 动画持续时间 + */ + public static void visibleViewByAlpha(final View view, final long durationMillis) { + visibleViewByAlpha(view, durationMillis, false, null); + } + + /** + * 将给定视图渐渐显示出来 - view.setVisibility(View.VISIBLE) + * @param view 待处理的视图 + * @param isBanClick 在执行动画的过程中是否禁止点击 + * @param animationListener 动画监听器 + */ + public static void visibleViewByAlpha(final View view, final boolean isBanClick, final AnimationListener animationListener) { + visibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, animationListener); + } + + /** + * 将给定视图渐渐显示出来 - view.setVisibility(View.VISIBLE) + * @param view 待处理的视图 + * @param animationListener 动画监听器 + */ + public static void visibleViewByAlpha(final View view, final AnimationListener animationListener) { + visibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, false, animationListener); + } + + /** + * 将给定视图渐渐显示出来 - view.setVisibility(View.VISIBLE) + * @param view 待处理的视图 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void visibleViewByAlpha(final View view, final boolean isBanClick) { + visibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, isBanClick, null); + } + + /** + * 将给定视图渐渐显示出来 - view.setVisibility(View.VISIBLE) + * @param view 待处理的视图 + */ + public static void visibleViewByAlpha(final View view) { + visibleViewByAlpha(view, AnimationUtils.DEFAULT_ANIMATION_DURATION, false, null); + } + + // ================ + // = 视图移动动画 = + // ================ + + /** + * 视图移动 + * @param view 待移动的视图 + * @param fromXDelta 动画开始的 X 轴坐标 + * @param toXDelta 动画结束的 X 轴坐标 + * @param fromYDelta 动画开始的 Y 轴坐标 + * @param toYDelta 动画结束的 Y 轴坐标 + * @param interpolator 动画周期 + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void translate(final View view, final float fromXDelta, final float toXDelta, + final float fromYDelta, final float toYDelta, final Interpolator interpolator, + final long durationMillis, final boolean isBanClick) { + if (view != null) { + TranslateAnimation animation = AnimationUtils.getTranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta, interpolator, durationMillis); + animation.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + if (isBanClick && view != null) { + view.setClickable(false); + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + + } + + @Override + public void onAnimationEnd(Animation animation) { + if (isBanClick && view != null) { + view.setClickable(true); + } + } + }); + view.startAnimation(animation); + } + } + + /** + * 视图移动 + * @param view 待移动的视图 + * @param fromXDelta 动画开始的 X 轴坐标 + * @param toXDelta 动画结束的 X 轴坐标 + * @param fromYDelta 动画开始的 Y 轴坐标 + * @param toYDelta 动画结束的 Y 轴坐标 + * @param cycles 动画周期 {@link CycleInterpolator} + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void translate(final View view, final float fromXDelta, final float toXDelta, + final float fromYDelta, final float toYDelta, final float cycles, final long durationMillis, final boolean isBanClick) { + if (view != null) { + Interpolator interpolator = (cycles > 0.0f) ? new CycleInterpolator(cycles) : null; + translate(view, fromXDelta, toXDelta, fromYDelta, toYDelta, interpolator, durationMillis, isBanClick); + } + } + + /** + * 视图移动 + * @param view 待移动的视图 + * @param fromXDelta 动画开始的 X 轴坐标 + * @param toXDelta 动画结束的 X 轴坐标 + * @param fromYDelta 动画开始的 Y 轴坐标 + * @param toYDelta 动画结束的 Y 轴坐标 + * @param cycles 动画周期 {@link CycleInterpolator} + * @param durationMillis 动画持续时间 + */ + public static void translate(final View view, final float fromXDelta, final float toXDelta, + final float fromYDelta, final float toYDelta, final float cycles, final long durationMillis) { + translate(view, fromXDelta, toXDelta, fromYDelta, toYDelta, cycles, durationMillis, false); + } + + // = + + /** + * 视图摇晃 + * @param view 待摇晃的视图 + * @param fromXDelta 动画开始的 X 轴坐标 + * @param toXDelta 动画结束的 X 轴坐标 + * @param cycles 动画周期 {@link CycleInterpolator} + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void shake(final View view, final float fromXDelta, final float toXDelta, + final float cycles, final long durationMillis, final boolean isBanClick) { + translate(view, fromXDelta, toXDelta, 0.0f, 0.0f, cycles, durationMillis, isBanClick); + } + + /** + * 视图摇晃 + * @param view 待摇晃的视图 + * @param fromXDelta 动画开始的 X 轴坐标 + * @param toXDelta 动画结束的 X 轴坐标 + * @param cycles 动画周期 {@link CycleInterpolator} + * @param durationMillis 动画持续时间 + */ + public static void shake(final View view, final float fromXDelta, final float toXDelta, final float cycles, final long durationMillis) { + translate(view, fromXDelta, toXDelta, 0.0f, 0.0f, cycles, durationMillis, false); + } + + /** + * 视图摇晃 ( 摇晃幅度为 10、重复 7 次 ) + * @param view 待摇晃的视图 + * @param cycles 动画周期 {@link CycleInterpolator} + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void shake(final View view, final float cycles, final long durationMillis, final boolean isBanClick) { + translate(view, 0.0f, 10.0f, 0.0f, 0.0f, cycles, durationMillis, isBanClick); + } + + /** + * 视图摇晃 ( 摇晃幅度为 10、持续 700 毫秒 ) + * @param view 待摇晃的视图 + * @param cycles 动画周期 {@link CycleInterpolator} + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void shake(final View view, final float cycles, final boolean isBanClick) { + translate(view, 0.0f, 10.0f, 0.0f, 0.0f, cycles, 700, isBanClick); + } + + /** + * 视图摇晃 ( 摇晃幅度为 10) + * @param view 待摇晃的视图 + * @param cycles 动画周期 {@link CycleInterpolator} + * @param durationMillis 动画持续时间 + */ + public static void shake(final View view, final float cycles, final long durationMillis) { + translate(view, 0.0f, 10.0f, 0.0f, 0.0f, cycles, durationMillis, false); + } + + /** + * 视图摇晃 ( 摇晃幅度为 10、重复 7 次 ) + * @param view 待摇晃的视图 + * @param durationMillis 动画持续时间 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void shake(final View view, final long durationMillis, final boolean isBanClick) { + translate(view, 0.0f, 10.0f, 0.0f, 0.0f, 7, durationMillis, isBanClick); + } + + /** + * 视图摇晃 ( 摇晃幅度为 10、持续 700 毫秒 ) + * @param view 待摇晃的视图 + * @param cycles 动画周期 {@link CycleInterpolator} + */ + public static void shake(final View view, final float cycles) { + translate(view, 0.0f, 10.0f, 0.0f, 0.0f, cycles, 700, false); + } + + /** + * 视图摇晃 ( 摇晃幅度为 10、重复 7 次 ) + * @param view 待摇晃的视图 + * @param durationMillis 动画持续时间 + */ + public static void shake(final View view, final long durationMillis) { + translate(view, 0.0f, 10.0f, 0.0f, 0.0f, 7, durationMillis, false); + } + + // = + + /** + * 视图摇晃 ( 摇晃幅度为 10、重复 7 次、持续 700 毫秒 ) + * @param view 待摇晃的视图 + * @param isBanClick 在执行动画的过程中是否禁止点击 + */ + public static void shake(final View view, final boolean isBanClick) { + translate(view, 0.0f, 10.0f, 0.0f, 0.0f, 7, 700, isBanClick); + } + + /** + * 视图摇晃 ( 摇晃幅度为 10、重复 7 次、持续 700 毫秒 ) + * @param view 待摇晃的视图 + */ + public static void shake(final View view) { + translate(view, 0.0f, 10.0f, 0.0f, 0.0f, 7, 700, false); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/assist/AsyncExecutor.java b/lib/DevApp/src/main/java/dev/utils/app/assist/AsyncExecutor.java new file mode 100644 index 0000000000..2230232347 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/assist/AsyncExecutor.java @@ -0,0 +1,179 @@ +package dev.utils.app.assist; + +import android.os.Handler; +import android.os.Looper; + +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; + +import dev.utils.LogPrintUtils; + +/** + * detail: 异步执行辅助类 + * @author MaTianyu + * @author Ttt ( 重写、规范注释、逻辑判断等 ) + */ +public class AsyncExecutor { + + // 日志 TAG + private final String TAG = AsyncExecutor.class.getSimpleName(); + // 主线程 Hander + private Handler mHandler = new Handler(Looper.getMainLooper()); + // 线程池 + private ExecutorService mThreadPool; + + public AsyncExecutor() { + this(null); + } + + /** + * 构造函数 + * @param pool {@link ExecutorService} + */ + public AsyncExecutor(final ExecutorService pool) { + if (mThreadPool != null) { + shutdownNow(); + } + // 进行赋值 + mThreadPool = pool; + // 防止为 null + if (mThreadPool == null) { + mThreadPool = Executors.newCachedThreadPool(); + } + } + + /** + * 立即关闭线程池任务 + */ + public synchronized void shutdownNow() { + if (mThreadPool != null && !mThreadPool.isShutdown()) { + mThreadPool.shutdownNow(); + mThreadPool = null; + } + } + + /** + * 将任务投入线程池执行 + * @param worker 任务 {@link Worker} + * @param 泛型 + * @return {@link FutureTask} + */ + public FutureTask execute(final Worker worker) { + if (worker != null) { + Callable call = new Callable() { + @Override + public T call() throws Exception { + return postResult(worker, worker.doInBackground()); + } + }; + FutureTask task = new FutureTask(call) { + @Override + protected void done() { + try { + get(); + } catch (InterruptedException e) { + LogPrintUtils.eTag(TAG, e, "execute"); + worker.abort(); + postCancel(worker); + } catch (ExecutionException e) { + LogPrintUtils.eTag(TAG, e, "execute"); + throw new RuntimeException("An error occured while executing doInBackground()", e.getCause()); + } catch (CancellationException e) { + worker.abort(); + postCancel(worker); + LogPrintUtils.eTag(TAG, e, "execute"); + } + } + }; + mThreadPool.execute(task); + return task; + } + return null; + } + + /** + * 将子线程结果传递到 UI 线程 + * @param worker 任务 {@link Worker} + * @param result 执行数据 + * @param 泛型 + * @return 执行数据 + */ + private T postResult(final Worker worker, final T result) { + if (worker == null) return result; + mHandler.post(new Runnable() { + @Override + public void run() { + if (worker != null) { + worker.onPostExecute(result); + } + } + }); + return result; + } + + /** + * 将子线程结果传递到 UI 线程 + * @param worker 任务 {@link Worker} + */ + private void postCancel(final Worker worker) { + if (worker == null) return; + mHandler.post(new Runnable() { + @Override + public void run() { + worker.onCanceled(); + } + }); + } + + /** + * 执行任务 + * @param call {@link Callable} + * @param 泛型 + * @return {@link FutureTask} + */ + public FutureTask execute(final Callable call) { + if (call != null) { + FutureTask task = new FutureTask<>(call); + mThreadPool.execute(task); + return task; + } + return null; + } + + /** + * detail: 任务 + * @param 泛型 + * @author Ttt + */ + public abstract class Worker { + + /** + * 后台运行 + * @return {@link T} 泛型类型 + */ + protected abstract T doInBackground(); + + /** + * 将子线程结果传递到 UI 线程 + * @param data 数据 + */ + protected void onPostExecute(T data) { + } + + /** + * 取消任务 + */ + protected void onCanceled() { + } + + /** + * 中止任务 + */ + protected void abort() { + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/assist/BeepVibrateAssist.java b/lib/DevApp/src/main/java/dev/utils/app/assist/BeepVibrateAssist.java new file mode 100644 index 0000000000..2f6f1c24be --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/assist/BeepVibrateAssist.java @@ -0,0 +1,292 @@ +package dev.utils.app.assist; + +import android.app.Activity; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.os.Vibrator; +import android.support.annotation.RawRes; +import android.support.annotation.RequiresPermission; + +import java.io.Closeable; + +import dev.utils.LogPrintUtils; + +/** + * detail: 播放「bee」的声音, 并且震动辅助类 + * @author Ttt + *
+ *     所需权限
+ *     
+ * 
+ */ +public final class BeepVibrateAssist implements Closeable { + + // 日志 TAG + private static final String TAG = BeepVibrateAssist.class.getSimpleName(); + // Activity + private final Activity mActivity; + // 播放资源对象 + private MediaPlayer mMediaPlayer = null; + // 是否需要震动 + private boolean mIsVibrate = true; + // 震动时间 + private long mVibrateDuration = 200L; + + /** + * 构造函数 + * @param activity {@link Activity} + */ + public BeepVibrateAssist(final Activity activity) { + this.mActivity = activity; + } + + /** + * 构造函数 + * @param activity {@link Activity} + * @param rawId R.raw.id + */ + public BeepVibrateAssist(final Activity activity, @RawRes final int rawId) { + this.mActivity = activity; + this.mMediaPlayer = buildMediaPlayer(activity, rawId); + } + + /** + * 构造函数 + * @param activity {@link Activity} + * @param filePath 文件路径 + */ + public BeepVibrateAssist(final Activity activity, final String filePath) { + this.mActivity = activity; + this.mMediaPlayer = buildMediaPlayer(filePath); + } + + // ================ + // = 内部判断方法 = + // ================ + + /** + * 判断是否允许播放声音 + * @return {@code true} 允许, {@code false} 不允许 + */ + private boolean shouldBeep() { + try { + // RINGER_MODE_NORMAL( 普通 )、RINGER_MODE_SILENT( 静音 )、RINGER_MODE_VIBRATE( 震动 ) + AudioManager audioService = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE); + if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) { + return false; // 只有属于, 静音、震动, 才不播放 + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "shouldBeep"); + } + return true; + } + + /** + * 更新播放流处理 + */ + private synchronized void streamUpdate() { + if (shouldBeep() && mMediaPlayer != null) { + try { + // The volume on STREAM_SYSTEM is not adjustable, and users found it too loud, + // so we now play on the music stream. + mActivity.setVolumeControlStream(AudioManager.STREAM_MUSIC); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "update"); + } + } + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 判断是否允许播放声音 + * @return {@code true} 允许, {@code false} 不允许 + */ + public boolean isPlayBeep() { + return shouldBeep(); + } + + /** + * 判断是否允许震动 + * @return {@code true} 允许, {@code false} 不允许 + */ + public boolean isVibrate() { + return mIsVibrate; + } + + /** + * 设置是否允许震动 + * @param vibrate 是否允许震动 + * @return {@link BeepVibrateAssist} + */ + public BeepVibrateAssist setVibrate(final boolean vibrate) { + setVibrate(vibrate, 200L); + return this; + } + + /** + * 设置是否允许震动 + * @param vibrate 是否允许震动 + * @param vibrateDuration 震动时间 ( 毫秒 ) + * @return {@link BeepVibrateAssist} + */ + public BeepVibrateAssist setVibrate(final boolean vibrate, final long vibrateDuration) { + this.mIsVibrate = vibrate; + this.mVibrateDuration = vibrateDuration; + return this; + } + + /** + * 设置播放资源对象 + * @param mediaPlayer {@link MediaPlayer} + * @return {@link BeepVibrateAssist} + */ + public BeepVibrateAssist setMediaPlayer(final MediaPlayer mediaPlayer) { + this.mMediaPlayer = mediaPlayer; + // 更新播放流处理 + streamUpdate(); + return this; + } + + /** + * 进行播放声音, 并且震动 + */ + @RequiresPermission(android.Manifest.permission.VIBRATE) + public synchronized void playBeepSoundAndVibrate() { + // 判断是否允许播放 + if (shouldBeep() && mMediaPlayer != null) { + try { + // 播放 + mMediaPlayer.start(); + } catch (Exception e) { + } + } + // 判断是否允许震动 + if (mIsVibrate) { + try { + Vibrator vibrator = (Vibrator) mActivity.getSystemService(Context.VIBRATOR_SERVICE); + vibrator.vibrate(mVibrateDuration); + } catch (Exception e) { + } + } + } + + /** + * 关闭震动、提示声, 并释放资源 + */ + @Override + public synchronized void close() { + if (mMediaPlayer != null) { + mMediaPlayer.release(); + mMediaPlayer = null; + } + } + + // ========================= + // = 创建 MediaPlayer 处理 = + // ========================= + + /** + * 创建 MediaPlayer 对象 + * @param context {@link Context} + * @param rawId R.raw.id + * @return {@link MediaPlayer} + */ + public static MediaPlayer buildMediaPlayer(final Context context, @RawRes final int rawId) { + return buildMediaPlayer(context, rawId, 0.1f); + } + + /** + * 创建 MediaPlayer 对象 + * @param context {@link Context} + * @param rawId R.raw.id + * @param beepVolume 音量 + * @return {@link MediaPlayer} + */ + public static MediaPlayer buildMediaPlayer(final Context context, @RawRes final int rawId, final float beepVolume) { + final MediaPlayer mediaPlayer = new MediaPlayer(); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + @Override + public void onCompletion(MediaPlayer mp) { + LogPrintUtils.dTag(TAG, "buildMediaPlayer - onCompletion"); + } + }); + mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { + @Override + public synchronized boolean onError(MediaPlayer mp, int what, int extra) { + LogPrintUtils.dTag(TAG, "buildMediaPlayer - onError what: " + what + ", extra: " + extra); + // 播放异常, 直接不处理 + return true; + } + }); + try { + AssetFileDescriptor file = context.getResources().openRawResourceFd(rawId); + try { + mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); + } finally { + try { + file.close(); + } catch (Exception e) { + } + } + mediaPlayer.setVolume(beepVolume, beepVolume); + mediaPlayer.prepare(); + return mediaPlayer; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "buildMediaPlayer"); + mediaPlayer.release(); + return null; + } + } + + // = + + /** + * 创建 MediaPlayer 对象 + * @param filePath 文件路径 + * @return {@link MediaPlayer} + */ + public static MediaPlayer buildMediaPlayer(final String filePath) { + return buildMediaPlayer(filePath, 0.1f); + } + + /** + * 创建 MediaPlayer 对象 + * @param filePath 文件路径 + * @param beepVolume 音量 + * @return {@link MediaPlayer} + */ + public static MediaPlayer buildMediaPlayer(final String filePath, final float beepVolume) { + final MediaPlayer mediaPlayer = new MediaPlayer(); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + @Override + public void onCompletion(MediaPlayer mp) { + LogPrintUtils.dTag(TAG, "buildMediaPlayer - onCompletion"); + } + }); + mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { + @Override + public synchronized boolean onError(MediaPlayer mp, int what, int extra) { + LogPrintUtils.dTag(TAG, "buildMediaPlayer - onError what: " + what + ", extra: " + extra); + // 播放异常, 直接不处理 + return true; + } + }); + try { + mediaPlayer.setDataSource(filePath); + mediaPlayer.setVolume(beepVolume, beepVolume); + mediaPlayer.prepare(); + return mediaPlayer; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "buildMediaPlayer"); + mediaPlayer.release(); + return null; + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java b/lib/DevApp/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java new file mode 100644 index 0000000000..3d7fe6ff03 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java @@ -0,0 +1,173 @@ +package dev.utils.app.assist; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.AsyncTask; +import android.os.BatteryManager; +import android.os.Build; + +/** + * detail: Activity 无操作定时辅助类 + * @author Ttt + *
+ *     需要注意的是, 需要在对应的生命周期内, 调用对应的 onPause/onResume/onDestroy 方法
+ * 
+ */ +public final class InactivityTimerAssist { + + // 无操作时间 ( 到时间自动关闭 ) - 默认五分钟 + private long mInactivityTime; + // 对应的页面 + private Activity mActivity; + // 电池广播 ( 充电中, 则不处理, 主要是为了省电 ) + private BroadcastReceiver mPowerStatusReceiver; + // 检查任务 + private AsyncTask mInactivityTask; + + // ============ + // = 构造函数 = + // ============ + + /** + * 构造函数 + * @param activity {@link Activity} + */ + public InactivityTimerAssist(final Activity activity) { + this(activity, 300000L); // 5 * 60 * 1000L + } + + /** + * 构造函数 + * @param activity {@link Activity} + * @param inactivityTime 无操作时间间隔 ( 毫秒 ) + */ + public InactivityTimerAssist(final Activity activity, final long inactivityTime) { + this.mActivity = activity; + this.mInactivityTime = inactivityTime; + // 电池广播监听 + mPowerStatusReceiver = new PowerStatusReceiver(); + // 关闭任务 + cancel(); + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 暂停检测 + *
+     *     Activity 生命周期 onPause 调用
+     * 
+ */ + public synchronized void onPause() { + // 取消任务 + cancel(); + try { + // 取消注册广播 + mActivity.unregisterReceiver(mPowerStatusReceiver); + } catch (Exception e) { + } + } + + /** + * 回到 Activity 处理 + *
+     *     Activity 生命周期 onResume 调用
+     * 
+ */ + public synchronized void onResume() { + try { + // 注册广播 + mActivity.registerReceiver(mPowerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + } catch (Exception e) { + } + // 开始检测 + start(); + } + + /** + * Activity 销毁处理 + *
+     *     Activity 生命周期 onDestroy 调用
+     * 
+ */ + public synchronized void onDestroy() { + cancel(); + } + + // ============ + // = 内部方法 = + // ============ + + /** + * 开始计时任务 + */ + private synchronized void start() { + // 取消任务 + cancel(); + // 注册任务 + mInactivityTask = new InactivityAsyncTask(); + // 开启任务 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + mInactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } else { + mInactivityTask.execute(); + } + } + + /** + * 取消计时任务 + */ + private synchronized void cancel() { + AsyncTask task = mInactivityTask; + if (task != null) { + task.cancel(true); + // 重置为 null + mInactivityTask = null; + } + } + + // = + + /** + * detail: 电池监听广播 + * @author Ttt + */ + private class PowerStatusReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent != null && Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { + // 0 indicates that we're on battery + boolean isBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0; + if (isBatteryNow) { // 属于非充电才进行记时 + InactivityTimerAssist.this.start(); + } else { // 充电中, 则不处理 + InactivityTimerAssist.this.cancel(); + } + } + } + } + + /** + * detail: 定时检测任务 ( 无操作检测 ) + * @author Ttt + */ + private class InactivityAsyncTask extends AsyncTask { + @Override + protected Object doInBackground(Object... objects) { + try { + Thread.sleep(mInactivityTime); + // 关闭页面 + if (mActivity != null) { + mActivity.finish(); + } + } catch (Exception e) { + } + return null; + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java b/lib/DevApp/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java new file mode 100644 index 0000000000..6c9a2203ab --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java @@ -0,0 +1,289 @@ +package dev.utils.app.assist; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Handler; +import android.os.Message; + +import dev.utils.LogPrintUtils; + +/** + * detail: 屏幕传感器辅助类 ( 监听是否横竖屏 ) + * @author Ttt + */ +public final class ScreenSensorAssist { + + // 日志 TAG + private final String TAG = ScreenSensorAssist.class.getSimpleName(); + + // ====================== + // = 重力传感器监听对象 = + // ====================== + + // 传感器管理对象 + private SensorManager mSensorManager; + // 重力传感器 + private Sensor mSensor; + // 重力传感器监听事件 + private OrientationSensorListener mListener; + + // =================================================== + // = 重力传感器监听对象 ( 改变方向后, 判断参数不同 ) = + // =================================================== + + // 传感器管理对象 ( 切屏后 ) + private SensorManager mSensorManagerChange; + // 重力传感器监听事件 ( 切屏后 ) + private OrientationSensorChangeListener mListenerChange; + + // ======== + // = 常量 = + // ======== + + // 坐标索引常量 + private final int DATA_X = 0; + private final int DATA_Y = 1; + private final int DATA_Z = 2; + // 方向未知常量 + private final int ORIENTATION_UNKNOWN = -1; + // 触发屏幕方向改变回调 + public static final int CHANGE_ORIENTATION_WHAT = 9919; + + // ======== + // = 变量 = + // ======== + + // 是否允许切屏 + private boolean mAllowChange = false; + // 是否是竖屏 + private boolean mPortrait = true; + // 回调操作 + private Handler mHandler; + + /** + * 角度处理 Handler + */ + private Handler mRotateHandler = new Handler() { + public void handleMessage(Message msg) { + switch (msg.what) { + case CHANGE_ORIENTATION_WHAT: + // 获取角度 + int rotation = msg.arg1; + // = + LogPrintUtils.dTag(TAG, "当前角度: " + rotation); + // 判断角度 + if (rotation > 45 && rotation < 135) { // 横屏 - 屏幕对着别人 + LogPrintUtils.dTag(TAG, "切换成横屏 - 屏幕对着自己"); + // = + if (mPortrait) { + mPortrait = false; + if (mHandler != null) { + Message vMsg = new Message(); + vMsg.what = CHANGE_ORIENTATION_WHAT; + vMsg.arg1 = 1; + mHandler.sendMessage(vMsg); + } + } + } else if (rotation > 135 && rotation < 225) { // 竖屏 - 屏幕对着别人 + LogPrintUtils.dTag(TAG, "切换成竖屏 - 屏幕对着别人"); + // = + if (!mPortrait) { + mPortrait = true; + if (mHandler != null) { + Message vMsg = new Message(); + vMsg.what = CHANGE_ORIENTATION_WHAT; + vMsg.arg1 = 2; + mHandler.sendMessage(vMsg); + } + } + } else if (rotation > 225 && rotation < 315) { // 横屏 - 屏幕对着自己 + LogPrintUtils.dTag(TAG, "切换成横屏 - 屏幕对着自己"); + // = + if (mPortrait) { + mPortrait = false; + if (mHandler != null) { + Message vMsg = new Message(); + vMsg.what = CHANGE_ORIENTATION_WHAT; + vMsg.arg1 = 1; + mHandler.sendMessage(vMsg); + } + } + } else if ((rotation > 315 && rotation < 360) || (rotation > 0 && rotation < 45)) { // 竖屏 - 屏幕对着自己 + LogPrintUtils.dTag(TAG, "切换成竖屏 - 屏幕对着自己"); + // = + if (!mPortrait) { + mPortrait = true; + if (mHandler != null) { + Message vMsg = new Message(); + vMsg.what = CHANGE_ORIENTATION_WHAT; + vMsg.arg1 = 2; + mHandler.sendMessage(vMsg); + } + } + } else { + LogPrintUtils.dTag(TAG, "其他角度: " + rotation); + } + break; + } + } + }; + + // = + + /** + * 初始化操作 + * @param context {@link Context} + * @param handler 回调 {@link Handler} + */ + private void init(final Context context, final Handler handler) { + this.mHandler = handler; + + // 设置传感器 + mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + // 注册重力感应器, 监听屏幕旋转 + mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + mListener = new OrientationSensorListener(); + + // 根据 旋转之后、点击全屏之后 两者方向一致, 激活 SensorManager + mSensorManagerChange = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + mListenerChange = new OrientationSensorChangeListener(); + } + + /** + * 开始监听 + * @param context {@link Context} + * @param handler 回调 {@link Handler} + */ + public void start(final Context context, final Handler handler) { + mAllowChange = true; + try { + LogPrintUtils.dTag(TAG, "start orientation listener."); + // 初始化操作 + init(context, handler); + // 监听重力传感器 + mSensorManager.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_UI); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "start"); + } + } + + /** + * 停止监听 + */ + public void stop() { + mAllowChange = false; + LogPrintUtils.dTag(TAG, "stop orientation listener."); + try { + mSensorManager.unregisterListener(mListener); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stop"); + } + try { + mSensorManagerChange.unregisterListener(mListenerChange); + } catch (Exception e) { + } + } + + /** + * 是否竖屏 + * @return {@code true} 竖屏, {@code false} 非竖屏 + */ + public boolean isPortrait() { + return this.mPortrait; + } + + /** + * 是否允许切屏 + * @return {@code true} 允许, {@code false} 不允许 + */ + public boolean isAllowChange() { + return this.mAllowChange; + } + + // = + + /** + * detail: 重力传感器监听事件 + * @author Ttt + */ + class OrientationSensorListener implements SensorEventListener { + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + + @Override + public void onSensorChanged(SensorEvent event) { + float[] values = event.values; + int orientation = ORIENTATION_UNKNOWN; + float X = -values[DATA_X]; + float Y = -values[DATA_Y]; + float Z = -values[DATA_Z]; + float magnitude = X * X + Y * Y; + // Don't trust the angle if the magnitude is small compared to the y value + if (magnitude * 4 >= Z * Z) { + // 屏幕旋转时 + float OneEightyOverPi = 57.29577957855f; + float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi; + orientation = 90 - Math.round(angle); + // normalize to 0 - 359 range + while (orientation >= 360) { + orientation -= 360; + } + while (orientation < 0) { + orientation += 360; + } + } + if (mRotateHandler != null) { + mRotateHandler.obtainMessage(CHANGE_ORIENTATION_WHAT, orientation, 0).sendToTarget(); + } + } + } + + /** + * detail: 重力传感器监听事件 ( 切屏后 ) + * @author Ttt + */ + class OrientationSensorChangeListener implements SensorEventListener { + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + + @Override + public void onSensorChanged(SensorEvent event) { + float[] values = event.values; + int orientation = ORIENTATION_UNKNOWN; + float X = -values[DATA_X]; + float Y = -values[DATA_Y]; + float Z = -values[DATA_Z]; + float magnitude = X * X + Y * Y; + // Don't trust the angle if the magnitude is small compared to the y value + if (magnitude * 4 >= Z * Z) { + // 屏幕旋转时 + float OneEightyOverPi = 57.29577957855f; + float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi; + orientation = 90 - Math.round(angle); + // normalize to 0 - 359 range + while (orientation >= 360) { + orientation -= 360; + } + while (orientation < 0) { + orientation += 360; + } + } + if (orientation > 225 && orientation < 315) { // 检测到当前实际是横屏 + if (!mPortrait) { + mSensorManager.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_UI); + mSensorManagerChange.unregisterListener(mListenerChange); + } + } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) { // 检测到当前实际是竖屏 + if (mPortrait) { + mSensorManager.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_UI); + mSensorManagerChange.unregisterListener(mListenerChange); + } + } + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/assist/manager/TimerManager.java b/lib/DevApp/src/main/java/dev/utils/app/assist/manager/TimerManager.java new file mode 100644 index 0000000000..d5ee7859db --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/assist/manager/TimerManager.java @@ -0,0 +1,727 @@ +package dev.utils.app.assist.manager; + +import android.os.Handler; +import android.os.Message; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Timer; + +import dev.utils.LogPrintUtils; + +/** + * detail: 定时器工具类 + * @author Ttt + *
+ *     主要是为了控制整个项目的定时器, 防止定时器混乱, 或者忘记关闭等情况, 以及减少初始化等操作代码
+ *     主要实现是 AbsTimer、TimerTask 这两个类
+ *     AbsTimer 定时器抽象类, 对外提供该类对象以及内部方法便于内部实现方法的隐藏, 以达到对定时器任务的控制处理
+ *     TimerTask 内部私有类, 实现了具体的定时器操作以及代码控制等, 防止外部直接 new 导致定时器混乱
+ *     

+ * 如果外部想要实现定时器, 但是通过内部 ArrayList 控制, 也可以通过实现 AbsTimer 接口, 内部的 startTimer()、closeTimer() 进行了对 AbsTimer 的保存、标记等操作 + * 需要注意的是, 实现 start(close)Timer() 方法, 必须保留 super.start(close)Timer() 内部 ArrayList 进行了操作, 而不对外开放 ( 不需要主动调用 ) + *

+ * startTimer() 主要进行添加到 ArrayList, 并且标记不需要回收 + * closeTimer() 不直接操作 remove, 防止出现 ConcurrentModificationException 异常, 而是做一个标记, 便于后续回收 + *
+ */ +public final class TimerManager { + + private TimerManager() { + } + + // 日志 TAG + private static final String TAG = TimerManager.class.getSimpleName(); + // 内部保存定时器对象, 防止忘记关闭等其他情况便于控制处理 + private static final List mTimerLists = new ArrayList<>(); + + // ============================ + // = ArrayList 对外公开的方法 = + // ============================ + + /** + * 回收定时器资源 + */ + public static void gc() { + synchronized (mTimerLists) { + // 临时数据源 + List lists = new ArrayList<>(mTimerLists); + // 清空旧的数据 + mTimerLists.clear(); + // 开始删除无用资源 + Iterator iterator = lists.iterator(); + while (iterator.hasNext()) { + AbsTimer absTimer = iterator.next(); + if (absTimer == null || absTimer.markSweep) { // 进行回收 + iterator.remove(); + } + } + // 把不需要回收的保存回去 + mTimerLists.addAll(lists); + // 移除旧的 + lists.clear(); + lists = null; + } + } + + /** + * 获取全部任务总数 + * @return 全部任务总数 + */ + public static int getTimerSize() { + return mTimerLists.size(); + } + + // = + + /** + * 获取属于对应字符串标记的定时器任务 ( 优先获取符合的 ) + * @param markStr 判断 {@link AbsTimer#getMarkStr()} + * @return 属于对应字符串标记的定时器任务 ( 优先获取符合的 ) {@link AbsTimer} + */ + public static AbsTimer getTimer(final String markStr) { + if (markStr != null) { + try { + for (int i = 0, len = mTimerLists.size(); i < len; i++) { + AbsTimer absTimer = mTimerLists.get(i); + // 判断是否符合标记, 原本标记不为 null, 并且符合条件的 + if (absTimer != null && absTimer.getMarkStr() != null && absTimer.getMarkStr().equals(markStr)) { + return absTimer; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTimer"); + } + } + return null; + } + + /** + * 获取属于标记 id 的定时器任务 ( 优先获取符合的 ) + * @param markId 判断 {@link AbsTimer#getMarkId()} + * @return 属于标记 id 的定时器任务 ( 优先获取符合的 ) {@link AbsTimer} + */ + public static AbsTimer getTimer(final int markId) { + try { + for (int i = 0, len = mTimerLists.size(); i < len; i++) { + AbsTimer absTimer = mTimerLists.get(i); + // 判断是否符合标记 + if (absTimer != null && absTimer.getMarkId() == markId) { + return absTimer; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTimer"); + } + return null; + } + + // = + + /** + * 获取属于对应字符串标记的定时器任务集合 + * @param markStr 判断 {@link AbsTimer#getMarkStr()} + * @return 属于对应字符串标记的定时器任务集合 {@link List} + */ + public static List getTimers(final String markStr) { + List lists = new ArrayList<>(); + // 防止为 null + if (markStr != null) { + try { + for (int i = 0, len = mTimerLists.size(); i < len; i++) { + AbsTimer absTimer = mTimerLists.get(i); + // 判断是否符合标记, 原本标记不为 null, 并且符合条件的 + if (absTimer != null && absTimer.getMarkStr() != null && absTimer.getMarkStr().equals(markStr)) { + lists.add(absTimer); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTimers"); + } + } + return lists; + } + + /** + * 获取属于标记 id 的定时器任务集合 + * @param markId 判断 {@link AbsTimer#getMarkId()} + * @return 属于标记 id 的定时器任务集合 {@link List} + */ + public static List getTimers(final int markId) { + List lists = new ArrayList<>(); + try { + for (int i = 0, len = mTimerLists.size(); i < len; i++) { + AbsTimer absTimer = mTimerLists.get(i); + // 判断是否符合标记 + if (absTimer != null && absTimer.getMarkId() == markId) { + lists.add(absTimer); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getTimers"); + } + return lists; + } + + // = + + /** + * 关闭全部任务 + */ + public static void closeAll() { + try { + for (int i = 0, len = mTimerLists.size(); i < len; i++) { + AbsTimer absTimer = mTimerLists.get(i); + if (absTimer != null) { // 关闭定时器 + absTimer.closeTimer(); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeAll"); + } + } + + /** + * 关闭所有未运行的任务 + */ + public static void closeNotRunTask() { + try { + for (int i = 0, len = mTimerLists.size(); i < len; i++) { + AbsTimer absTimer = mTimerLists.get(i); + // 判断是否运行中 + if (absTimer != null && !absTimer.isRunTimer()) { + absTimer.closeTimer(); // 关闭定时器 + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeNotRunTask"); + } + } + + /** + * 关闭所有无限循环的任务 + */ + public static void closeInfiniteTask() { + try { + for (int i = 0, len = mTimerLists.size(); i < len; i++) { + AbsTimer absTimer = mTimerLists.get(i); + // 判断是否无限运行 + if (absTimer != null && absTimer.isInfinite()) { + absTimer.closeTimer(); // 关闭定时器 + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeInfiniteTask"); + } + } + + /** + * 关闭所有符合对应的字符串标记的定时器任务 + * @param markStr 判断 {@link AbsTimer#getMarkStr()} + */ + public static void closeMark(final String markStr) { + if (markStr != null) { + try { + for (int i = 0, len = mTimerLists.size(); i < len; i++) { + AbsTimer absTimer = mTimerLists.get(i); + // 判断是否符合标记, 原本标记不为 null, 并且符合条件的 + if (absTimer != null && absTimer.getMarkStr() != null && absTimer.getMarkStr().equals(markStr)) { + absTimer.closeTimer(); // 关闭定时器 + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeMark"); + } + } + } + + /** + * 关闭所有符合对应的标记 id 的定时器任务 + * @param markId 判断 {@link AbsTimer#getMarkId()} + */ + public static void closeMark(final int markId) { + try { + for (int i = 0, len = mTimerLists.size(); i < len; i++) { + AbsTimer absTimer = mTimerLists.get(i); + if (absTimer != null && absTimer.getMarkId() == markId) { + absTimer.closeTimer(); // 关闭定时器 + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeMark"); + } + } + + // ================================================================ + // = 对外公开初始化 AbsTimer 方法 ( 内部控制对 TimerTask 的创建 ) = + // ================================================================ + + /** + * 创建定时器 ( 立即执行、无限循环、通知默认 what ) + * @param handler 通知的 Handler + * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 {@link AbsTimer} + */ + public static AbsTimer createTimer(final Handler handler, final long period) { + return createTimer(handler, AbsTimer.TIMER_NOTIFY_WHAT, 0L, period, -1); + } + + /** + * 创建定时器 ( 无限循环、通知默认 what ) + * @param handler 通知的 Handler + * @param delay 延迟时间 - 多少毫秒后开始执行 + * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 {@link AbsTimer} + */ + public static AbsTimer createTimer(final Handler handler, final long delay, final long period) { + return createTimer(handler, AbsTimer.TIMER_NOTIFY_WHAT, delay, period, -1); + } + + /** + * 创建定时器 ( 立即执行、通知默认 what ) + * @param handler 通知的 Handler + * @param period 循环时间 - 每隔多少秒执行一次 + * @param triggerLimit 触发次数上限 (-1 表示无限循环 ) + * @return 定时器抽象对象 {@link AbsTimer} + */ + public static AbsTimer createTimer(final Handler handler, final long period, final int triggerLimit) { + return createTimer(handler, AbsTimer.TIMER_NOTIFY_WHAT, 0L, period, triggerLimit); + } + + /** + * 创建定时器 ( 立即执行、无限循环 ) + * @param handler 通知的 Handler + * @param what 通知的 what + * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 {@link AbsTimer} + */ + public static AbsTimer createTimer(final Handler handler, final int what, final long period) { + return createTimer(handler, what, 0L, period, -1); + } + + /** + * 创建定时器 ( 无限循环 ) + * @param handler 通知的 Handler + * @param what 通知的 what + * @param delay 延迟时间 - 多少毫秒后开始执行 + * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 {@link AbsTimer} + */ + public static AbsTimer createTimer(final Handler handler, final int what, final long delay, final long period) { + return createTimer(handler, what, delay, period, -1); + } + + /** + * 创建定时器 ( 立即执行 ) + * @param handler 通知的 Handler + * @param what 通知的 what + * @param period 循环时间 - 每隔多少秒执行一次 + * @param triggerLimit 触发次数上限 (-1 表示无限循环 ) + * @return 定时器抽象对象 {@link AbsTimer} + */ + public static AbsTimer createTimer(final Handler handler, final int what, final long period, final int triggerLimit) { + return createTimer(handler, what, 0L, period, triggerLimit); + } + + /** + * 创建定时器 + * @param handler 通知的 Handler + * @param what 通知的 what + * @param delay 延迟时间 - 多少毫秒后开始执行 + * @param period 循环时间 - 每隔多少秒执行一次 + * @param triggerLimit 触发次数上限 (-1 表示无限循环 ) + * @return 定时器抽象对象 {@link AbsTimer} + */ + public static AbsTimer createTimer(final Handler handler, final int what, final long delay, final long period, final int triggerLimit) { + return new TimerTask(handler, what, delay, period, triggerLimit); + } + + // ====================================== + // = 定时器抽象类, 开放对定时器操作方法 = + // ====================================== + + /** + * detail: 定时器抽象类, 主要对内部 Timer 参数进行控制, 以及防止外部直接 new TimerTask, 照成不必要的失误 + * @author Ttt + *
+     *     TODO 推荐使用 {@link TimerManager#createTimer} 创建定时任务, 如果需要自己实现 AbsTimer, 则参考 {@link TimerManager.TimerTask} 实现
+     *     TODO {@link TimerManager.AbsTimer} 只是提供了常见的方法, 以及变量等, 便于定时任务控制, 具体实现在 {@link TimerManager.TimerTask}
+     * 
+ */ + public static abstract class AbsTimer { + + // 默认通知的 what + public static final int TIMER_NOTIFY_WHAT = 50000; + // 状态标识 - 是否标记清除 + private boolean markSweep = true; + // int 标记 + private int markId = -1; + // String 标记 + private String markStr = null; + + /** + * 获取标记 id + * @return 标记 id + */ + public final int getMarkId() { + return markId; + } + + /** + * 获取标记字符串 + * @return 标记字符串 + */ + public final String getMarkStr() { + return markStr; + } + + /** + * 设置标记 id + * @param markId 标记 id + * @return 定时器抽象对象 {@link AbsTimer} + */ + public final AbsTimer setMarkId(final int markId) { + this.markId = markId; + return this; + } + + /** + * 设置标记字符串 + * @param markStr 标记字符串 + * @return 定时器抽象对象 {@link AbsTimer} + */ + public final AbsTimer setMarkStr(final String markStr) { + this.markStr = markStr; + return this; + } + + // ============================ + // = 定时器任务, 功能实现方法 = + // ============================ + + /** + * 运行定时器 + *
+         *     如果外部通过了 createTimer 或者直接 new AbsTimer 初始化了对象, 没有调用 startTimer, 都不会保存到 mTimerLists 并不影响对定时器的控制
+         * 
+ */ + public void startTimer() { + // 标记状态 - 不需要回收 + this.markSweep = false; + synchronized (mTimerLists) { + // 不存在才进行添加 + if (!mTimerLists.contains(this)) { + mTimerLists.add(this); + } + } + } + + /** + * 关闭定时器 + */ + public void closeTimer() { + // 标记状态 - 需要回收 + this.markSweep = true; + } + + /** + * 判断是否运行中 + * @return {@code true} yes, {@code false} no + */ + public abstract boolean isRunTimer(); + + /** + * 获取已经触发的次数 + * @return 已经触发的次数 + */ + public abstract int getTriggerNumber(); + + /** + * 获取允许触发的上限次数 + * @return 允许触发的上限次数 + */ + public abstract int getTriggerLimit(); + + /** + * 是否触发结束 ( 到达最大次数 ) + * @return {@code true} yes, {@code false} no + */ + public abstract boolean isTriggerEnd(); + + /** + * 是否无限循环 + * @return {@code true} yes, {@code false} no + */ + public abstract boolean isInfinite(); + + /** + * 设置通知的 Handler + * @param handler {@link Handler} + * @return 定时器抽象对象 {@link AbsTimer} + */ + public abstract AbsTimer setHandler(Handler handler); + + /** + * 设置通知的 what + * @param notifyWhat 通知 what + * @return 定时器抽象对象 {@link AbsTimer} + */ + public abstract AbsTimer setNotifyWhat(int notifyWhat); + + /** + * 设置通知的 Object + * @param notifyObj 通知对象 + * @return 定时器抽象对象 {@link AbsTimer} + */ + public abstract AbsTimer setNotifyObject(Object notifyObj); + + /** + * 设置时间 + * @param delay 延迟时间 - 多少毫秒后开始执行 + * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 {@link AbsTimer} + */ + public abstract AbsTimer setTime(long delay, long period); + + /** + * 设置触发次数上限 + * @param triggerLimit 触发次数上限 + * @return 定时器抽象对象 {@link AbsTimer} + */ + public abstract AbsTimer setTriggerLimit(int triggerLimit); + } + + // ==================================== + // = 定时器 具体实现类 ( 不对外开放 ) = + // ==================================== + + /** + * detail: 定时器内部封装类 ( 定时器任务类 ) + * @author Ttt + *
+     *     便于快捷使用, 并且防止外部 new 从而达到对整个项目定时器的控制
+     * 
+ */ + private static final class TimerTask extends AbsTimer { + + // 定时器 + private Timer timer; + // 定时器任务栈 + private java.util.TimerTask timerTask; + // 通知 Handler + private Handler handler; + // 通知的数据 + private Object notifyObj = null; + // 通知类型 + private int notifyWhat; + // 延迟时间 - 多少毫秒后开始执行 + private long delay; + // 循环时间 - 每隔多少秒执行一次 + private long period; + // 触发次数上限 + private int triggerLimit; + // 触发次数 + private int triggerNumber = 0; + // 定时器是否运行中 + private boolean running = false; + + public TimerTask(final Handler handler, final int what, final long delay, final long period, final int triggerLimit) { + this.handler = handler; + this.notifyWhat = what; + this.delay = delay; + this.period = period; + this.triggerLimit = triggerLimit; + } + + /** + * 开始执行定时器任务 + */ + private void start() { + // 先关闭旧的定时器 + close(); + // 表示运行定时器中 + running = true; + // 每次重置触发次数 + triggerNumber = 0; + // 开启定时器 + timer = new Timer(); // 每次重新 new 防止被取消 + // 重新生成定时器, 防止出现 TimerTask is scheduled already 同一个定时器任务只能被放置一次 + timerTask = new java.util.TimerTask() { + @Override + public void run() { + // 表示运行定时器中 + running = true; + // 累计触发次数 + triggerNumber++; + // 进行通知 + if (handler != null) { + // 从 Message 池中返回一个新的 Message 实例 - 通知 what, arg1 = 触发次数, arg2 = 触发上限, obj = notifyObj + Message msg = handler.obtainMessage(notifyWhat, triggerNumber, triggerLimit, notifyObj); + handler.sendMessage(msg); + } + // 如果大于触发次数, 则关闭 + if (triggerLimit >= 0 && triggerNumber >= triggerLimit) { + // 关闭任务, 进行标记需要回收 + closeTimer(); + } + } + }; + try { + // xx 毫秒后执行, 每隔 xx 毫秒再执行一次 + timer.schedule(timerTask, delay, period); + } catch (Exception e) { + // 表示非运行定时器中 + running = false; + // 关闭任务, 进行标记需要回收 + closeTimer(); // 启动失败, 则进行标记需要回收 + } + } + + /** + * 关闭定时器任务 + */ + private void close() { + // 表示非运行定时器中 + running = false; + // 取消定时器任务 + try { + if (timer != null) { + timer.cancel(); + timer = null; + } + if (timerTask != null) { + timerTask.cancel(); + timerTask = null; + } + } catch (Exception e) { + } + } + + // ================== + // = 实现抽象类方法 = + // ================== + + /** + * 运行定时器 + */ + @Override + public void startTimer() { + super.startTimer(); // 必须保留这句话 + // 开始定时器任务 + start(); + } + + /** + * 关闭定时器 + */ + @Override + public void closeTimer() { + super.closeTimer(); // 必须保留这句话 + // 关闭定时器任务 + close(); + } + + /** + * 判断是否运行中 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isRunTimer() { + return running; + } + + /** + * 获取已经触发的次数 + * @return 已经触发的次数 + */ + @Override + public int getTriggerNumber() { + return triggerNumber; + } + + /** + * 获取允许触发的上限次数 + * @return 允许触发的上限次数 + */ + @Override + public int getTriggerLimit() { + return triggerLimit; + } + + /** + * 是否触发结束 ( 到达最大次数 ) + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isTriggerEnd() { + return (triggerLimit >= 0 && triggerNumber >= triggerLimit); + } + + /** + * 是否无限循环 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isInfinite() { + return (triggerLimit <= -1); + } + + /** + * 设置通知的 Handler + * @param handler {@link Handler} + * @return 定时器抽象对象 {@link AbsTimer} + */ + @Override + public AbsTimer setHandler(final Handler handler) { + this.handler = handler; + return this; + } + + /** + * 设置通知的 what + * @param notifyWhat 通知 what + * @return 定时器抽象对象 {@link AbsTimer} + */ + @Override + public AbsTimer setNotifyWhat(final int notifyWhat) { + this.notifyWhat = notifyWhat; + return this; + } + + /** + * 设置通知的 Object + * @param notifyObj 通知对象 + * @return 定时器抽象对象 {@link AbsTimer} + */ + @Override + public AbsTimer setNotifyObject(final Object notifyObj) { + this.notifyObj = notifyObj; + return this; + } + + /** + * 设置时间 + * @param delay 延迟时间 - 多少毫秒后开始执行 + * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 {@link AbsTimer} + */ + @Override + public AbsTimer setTime(final long delay, final long period) { + this.delay = delay; + this.period = period; + return this; + } + + /** + * 设置触发次数上限 + * @param triggerLimit 触发次数上限 + * @return 定时器抽象对象 {@link AbsTimer} + */ + @Override + public AbsTimer setTriggerLimit(final int triggerLimit) { + this.triggerLimit = triggerLimit; + return this; + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/cache/DevCache.java b/lib/DevApp/src/main/java/dev/utils/app/cache/DevCache.java new file mode 100644 index 0000000000..986966f977 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/cache/DevCache.java @@ -0,0 +1,671 @@ +package dev.utils.app.cache; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 缓存工具类 + * @author 杨福海 (michael) www.yangfuhai.com + * @author Ttt ( 重写、规范注释、逻辑判断等 ) + */ +public final class DevCache { + + private DevCache() { + } + + // 日志 TAG + private static final String TAG = DevCache.class.getSimpleName(); + // 缓存文件名 + private static final String DEF_FILE_NAME = DevCache.class.getSimpleName(); + // 过期小时 ( 单位秒 ) = 1 小时 + public static final int TIME_HOUR = 60 * 60; + // 一天 24 小时 + public static final int TIME_DAY = TIME_HOUR * 24; + // 缓存最大值 50 MB + private static final int MAX_SIZE = 1000 * 1000 * 50; + // 不限制存放数据的数量 + private static final int MAX_COUNT = Integer.MAX_VALUE; + // 不同地址配置缓存对象 + private static Map sInstanceMaps = new HashMap<>(); + // 缓存管理类 + private DevCacheManager mCache; + // 缓存地址 + private static File sContextCacheDir = null; + + /** + * 内部处理防止 Context 为 null 崩溃问题 + * @param context {@link Context} + * @return {@link Context} + */ + private static Context getContext(final Context context) { + if (context != null) { + return context; + } else { + // 设置全局 Context + return DevUtils.getContext(); + } + } + + /** + * 获取缓存地址 + * @param context {@link Context} + * @return 应用缓存地址 + */ + public static File getCacheDir(final Context context) { + if (sContextCacheDir == null) { + sContextCacheDir = getContext(context).getCacheDir(); + } + return sContextCacheDir; + } + + /** + * 默认缓存地址 + * @param context {@link Context} + * @return {@link DevCache} + */ + public static DevCache get(final Context context) { + return get(context, DEF_FILE_NAME); + } + + /** + * 获取缓存地址 + * @param context {@link Context} + * @param cacheName 缓存文件名 + * @return {@link DevCache} + */ + public static DevCache get(final Context context, final String cacheName) { + if (cacheName == null) return null; + // 进行处理 + File file = new File(getCacheDir(context), cacheName); + // 获取默认地址 + return get(file, MAX_SIZE, MAX_COUNT); + } + + /** + * 设置自定义缓存地址 + * @param cacheDir 缓存文件地址 + * @return {@link DevCache} + */ + public static DevCache get(final File cacheDir) { + return get(cacheDir, MAX_SIZE, MAX_COUNT); + } + + /** + * 自定义缓存大小 + * @param context {@link Context} + * @param maxSize 文件最大大小 + * @param maxCount 最大存储数量 + * @return {@link DevCache} + */ + public static DevCache get(final Context context, final long maxSize, final int maxCount) { + File file = new File(getCacheDir(context), DEF_FILE_NAME); + return get(file, maxSize, maxCount); + } + + /** + * 自定义缓存地址、大小等 + * @param cacheDir 缓存文件地址 + * @param maxSize 文件最大大小 + * @param maxCount 最大存储数量 + * @return {@link DevCache} + */ + public static DevCache get(final File cacheDir, final long maxSize, final int maxCount) { + if (cacheDir == null) return null; + // 判断是否存在缓存信息 + DevCache manager = sInstanceMaps.get(cacheDir.getAbsoluteFile() + myPid()); + if (manager == null) { + // 初始化新的缓存信息, 并且保存 + manager = new DevCache(cacheDir, maxSize, maxCount); + sInstanceMaps.put(cacheDir.getAbsolutePath() + myPid(), manager); + } + return manager; + } + + /** + * 获取进程 id - android.os.Process.myPid() + * @return 进程 id + */ + private static String myPid() { + return "_" + android.os.Process.myPid(); + } + + /** + * 最终初始化方法 + * @param cacheDir 缓存文件地址 + * @param maxSize 文件最大大小 + * @param maxCount 最大存储数量 + * @return {@link DevCache} 缓存工具类对象 + */ + private DevCache(final File cacheDir, final long maxSize, final int maxCount) { + if (cacheDir == null) { + new Exception("cacheDir is null"); + } else if (!cacheDir.exists() && !cacheDir.mkdirs()) { + new Exception("can't make dirs in " + cacheDir.getAbsolutePath()); + } + mCache = new DevCacheManager(cacheDir, maxSize, maxCount); + } + + /** + * Provides a means to save a cached file before the data are available. + * Since writing about the file is complete, and its close method is called, + * its contents will be registered in the cache + */ + class xFileOutputStream extends FileOutputStream { + + private File file; + + public xFileOutputStream(File file) throws FileNotFoundException { + super(file); + this.file = file; + } + + public void close() throws IOException { + super.close(); + mCache.put(file); + } + } + + // ==================== + // = string 数据 读写 = + // ==================== + + /** + * 保存 String 数据到缓存中 + * @param key 保存的 key + * @param value 保存的 String 数据 + */ + public void put(final String key, final String value) { + File file = mCache.newFile(key); + if (file == null || value == null) { + return; + } + BufferedWriter bw = null; + try { + bw = new BufferedWriter(new FileWriter(file), 1024); + bw.write(value); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "put"); + } finally { + if (bw != null) { + try { + bw.flush(); + } catch (Exception e) { + } + } + if (bw != null) { + try { + bw.close(); + } catch (Exception e) { + } + } + mCache.put(file); + } + } + + /** + * 保存 String 数据到缓存中 + * @param key 保存的 key + * @param value 保存的 String 数据 + * @param saveTime 保存的时间, 单位: 秒 + */ + public void put(final String key, final String value, final int saveTime) { + if (key != null && value != null) { + put(key, DevCacheUtils.newStringWithDateInfo(saveTime, value)); + } + } + + /** + * 读取 String 数据 + * @param key 保存的 key + * @return 字符串数据 + */ + public String getAsString(final String key) { + File file = mCache.get(key); + if (file == null) { + return null; + } + if (!file.exists()) + return null; + boolean removeFile = false; + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(file)); + StringBuilder builder = new StringBuilder(); + String currentLine; + while ((currentLine = br.readLine()) != null) { + builder.append(currentLine); + } + // 读取内容 + String readString = builder.toString(); + if (!DevCacheUtils.isDue(readString)) { + return DevCacheUtils.clearDateInfo(readString); + } else { + LogPrintUtils.dTag(TAG, "getAsString key: " + key + " file has expired"); + removeFile = true; + return null; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAsString"); + return null; + } finally { + if (br != null) { + try { + br.close(); + } catch (Exception e) { + } + } + if (removeFile) + remove(key); + } + } + + // ======================== + // = JSONObject 数据 读写 = + // ======================== + + /** + * 保存 JSONObject 数据到缓存中 + * @param key 保存的 key + * @param value 保存的 JSONObject 数据 + */ + public void put(final String key, final JSONObject value) { + if (value != null) { + try { + put(key, value.toString()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "put JSONObject"); + } + } + } + + /** + * 保存 JSONObject 数据到缓存中 + * @param key 保存的 key + * @param value 保存的 JSONObject 数据 + * @param saveTime 保存的时间, 单位: 秒 + */ + public void put(final String key, final JSONObject value, final int saveTime) { + if (value != null) { + try { + put(key, value.toString(), saveTime); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "put JSONObject"); + } + } + } + + /** + * 读取 JSONObject 数据 + * @param key 保存的 key + * @return {@link JSONObject} + */ + public JSONObject getAsJSONObject(final String key) { + String json = getAsString(key); + if (json != null) { + try { + return new JSONObject(json); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAsJSONObject"); + } + } + return null; + } + + // ======================= + // = JSONArray 数据 读写 = + // ======================= + + /** + * 保存 JSONArray 数据到缓存中 + * @param key 保存的 key + * @param value 保存的 JSONArray 数据 + */ + public void put(final String key, final JSONArray value) { + if (value != null) { + try { + put(key, value.toString()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "put JSONArray"); + } + } + } + + /** + * 保存 JSONArray 数据到缓存中 + * @param key 保存的 key + * @param value 保存的 JSONArray 数据 + * @param saveTime 保存的时间, 单位: 秒 + */ + public void put(final String key, final JSONArray value, final int saveTime) { + if (value != null) { + try { + put(key, value.toString(), saveTime); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "put JSONArray"); + } + } + } + + /** + * 读取 JSONArray 数据 + * @param key 保存的 key + * @return {@link JSONArray} + */ + public JSONArray getAsJSONArray(final String key) { + String json = getAsString(key); + if (json != null) { + try { + return new JSONArray(json); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAsJSONArray"); + } + } + return null; + } + + // ================== + // = byte 数据 读写 = + // ================== + + /** + * 保存 byte 数据到缓存中 + * @param key 保存的 key + * @param data 保存的数据 + */ + public void put(final String key, final byte[] data) { + if (key == null || data == null) { + return; + } + File file = mCache.newFile(key); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(file); + fos.write(data); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "put byte[]"); + } finally { + if (fos != null) { + try { + fos.flush(); + } catch (Exception e) { + } + } + if (fos != null) { + try { + fos.close(); + } catch (Exception e) { + } + } + mCache.put(file); + } + } + + /** + * 返回缓存流写入数据对象 + * @param key 保存的 key + * @return {@link OutputStream} + * @throws FileNotFoundException 文件不存在 + */ + public OutputStream put(final String key) throws FileNotFoundException { + File file = mCache.newFile(key); + if (file != null) { + return new xFileOutputStream(file); + } + return null; + } + + /** + * 获取对应 key 的 File 输入流 + * @param key 保存的 key + * @return {@link InputStream} + * @throws FileNotFoundException 文件不存在 + */ + public InputStream get(final String key) throws FileNotFoundException { + File file = mCache.get(key); + if (file != null && file.exists()) { + return new FileInputStream(file); + } + return null; + } + + /** + * 保存 byte 数据到缓存中 + * @param key 保存的 key + * @param data 保存的数据 + * @param saveTime 保存的时间, 单位: 秒 + */ + public void put(final String key, final byte[] data, final int saveTime) { + put(key, DevCacheUtils.newByteArrayWithDateInfo(saveTime, data)); + } + + /** + * 获取 byte[] 数据 + * @param key 保存的 key + * @return byte[] + */ + public byte[] getAsBinary(final String key) { + RandomAccessFile raFile = null; + boolean removeFile = false; + try { + File file = mCache.get(key); + if (!file.exists()) + return null; + raFile = new RandomAccessFile(file, "r"); + byte[] byteArray = new byte[(int) raFile.length()]; + raFile.read(byteArray); + if (!DevCacheUtils.isDue(byteArray)) { + return DevCacheUtils.clearDateInfo(byteArray); + } else { + LogPrintUtils.dTag(TAG, "getAsBinary - key: " + key + " file has expired"); + removeFile = true; + return null; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAsBinary"); + return null; + } finally { + if (raFile != null) { + try { + raFile.close(); + } catch (Exception e) { + } + } + if (removeFile) + remove(key); + } + } + + // ==================== + // = 序列化 数据 读写 = + // ==================== + + /** + * 保存 Serializable 数据到缓存中 + * @param key 保存的 key + * @param value 保存的 value + */ + public void put(final String key, final Serializable value) { + put(key, value, -1); + } + + /** + * 保存 Serializable 数据到缓存中 + * @param key 保存的 key + * @param value 保存的 value + * @param saveTime 保存的时间, 单位: 秒 + */ + public void put(final String key, final Serializable value, final int saveTime) { + ObjectOutputStream oos = null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(baos); + oos.writeObject(value); + byte[] data = baos.toByteArray(); + if (saveTime != -1) { + put(key, data, saveTime); + } else { + put(key, data); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "put"); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (Exception e) { + } + } + } + } + + /** + * 读取 Serializable 数据 + * @param key 保存的 key + * @return {@link Serializable} Object + */ + public Object getAsObject(final String key) { + byte[] data = getAsBinary(key); + if (data != null) { + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(new ByteArrayInputStream(data)); + return ois.readObject(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAsObject"); + return null; + } finally { + if (ois != null) { + try { + ois.close(); + } catch (Exception e) { + } + } + } + } + return null; + } + + // ==================== + // = bitmap 数据 读写 = + // ==================== + + /** + * 保存 Bitmap 到缓存中 + * @param key 保存的 key + * @param value 保存的 bitmap 数据 + */ + public void put(final String key, final Bitmap value) { + put(key, DevCacheUtils.bitmapToBytes(value)); + } + + /** + * 保存 Bitmap 到缓存中 + * @param key 保存的 key + * @param value 保存的 bitmap 数据 + * @param saveTime 保存的时间, 单位: 秒 + */ + public void put(final String key, final Bitmap value, final int saveTime) { + put(key, DevCacheUtils.bitmapToBytes(value), saveTime); + } + + /** + * 读取 Bitmap 数据 + * @param key 保存的 key + * @return {@link Bitmap} + */ + public Bitmap getAsBitmap(final String key) { + byte[] data = getAsBinary(key); + if (data == null) return null; + return DevCacheUtils.bytesToBitmap(data); + } + + // ====================== + // = drawable 数据 读写 = + // ====================== + + /** + * 保存 Drawable 到缓存中 + * @param key 保存的 key + * @param value 保存的 drawable 数据 + */ + public void put(final String key, final Drawable value) { + put(key, DevCacheUtils.drawableToBitmap(value)); + } + + /** + * 保存 Drawable 到缓存中 + * @param key 保存的 key + * @param value 保存的 drawable 数据 + * @param saveTime 保存的时间, 单位: 秒 + */ + public void put(final String key, final Drawable value, final int saveTime) { + put(key, DevCacheUtils.drawableToBitmap(value), saveTime); + } + + /** + * 读取 Drawable 数据 + * @param key 保存的 key + * @return {@link Drawable} + */ + public Drawable getAsDrawable(final String key) { + byte[] data = getAsBinary(key); + if (data == null) return null; + return DevCacheUtils.bitmapToDrawable(DevCacheUtils.bytesToBitmap(data)); + } + + /** + * 获取缓存文件 + * @param key 保存的 key + * @return 缓存的文件 + */ + public File file(final String key) { + File file = mCache.newFile(key); + if (file != null && file.exists()) { + return file; + } + return null; + } + + /** + * 移除某个 key 的数据 + * @param key 保存的 key + * @return {@code true} yes, {@code false} no + */ + public boolean remove(final String key) { + return mCache.remove(key); + } + + /** + * 清除所有数据 + */ + public void clear() { + mCache.clear(); + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheManager.java b/lib/DevApp/src/main/java/dev/utils/app/cache/DevCacheManager.java similarity index 53% rename from DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheManager.java rename to lib/DevApp/src/main/java/dev/utils/app/cache/DevCacheManager.java index 0fbd5e5c05..93d98ff388 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheManager.java +++ b/lib/DevApp/src/main/java/dev/utils/app/cache/DevCacheManager.java @@ -10,51 +10,58 @@ /** * detail: 缓存管理类 - * Created by 杨福海(michael) www.yangfuhai.com - * https://github.com/yangfuhai/ASimpleCache - * Update to Ttt + * @author 杨福海 (michael) www.yangfuhai.com + * @author Ttt ( 重写、规范注释、逻辑判断等 ) */ final class DevCacheManager { // 总缓存大小 - private final AtomicLong cacheSize; + private final AtomicLong mCacheSize; // 总缓存的文件总数 - private final AtomicInteger cacheCount; + private final AtomicInteger mCacheCount; // 大小限制 - private final long sizeLimit; + private final long mSizeLimit; // 文件总数限制 - private final int countLimit; + private final int mCountLimit; // 保存文件时间信息 - 文件地址, 文件最后使用时间 - private final Map lastUsageDates = Collections.synchronizedMap(new HashMap()); + private final Map mLastUsageDateMaps = Collections.synchronizedMap(new HashMap()); // 文件目录 - protected File cacheDir; + private File mCacheDir; - protected DevCacheManager(File cacheDir, long sizeLimit, int countLimit) { - this.cacheDir = cacheDir; - this.sizeLimit = sizeLimit; - this.countLimit = countLimit; - cacheSize = new AtomicLong(); - cacheCount = new AtomicInteger(); + /** + * 构造函数 + * @param cacheDir 存储地址 + * @param sizeLimit 文件大小限制 + * @param countLimit 文件总数限制 + */ + protected DevCacheManager(final File cacheDir, final long sizeLimit, final int countLimit) { + this.mCacheDir = cacheDir; + this.mSizeLimit = sizeLimit; + this.mCountLimit = countLimit; + mCacheSize = new AtomicLong(); + mCacheCount = new AtomicInteger(); // 计算文件信息等 calculateCacheSizeAndCacheCount(); } - /** 计算 cacheSize 和 cacheCount */ + /** + * 计算 cacheSize 和 cacheCount + */ private void calculateCacheSizeAndCacheCount() { new Thread(new Runnable() { @Override public void run() { int size = 0, count = 0; - if (cacheDir != null) { - File[] cachedFiles = cacheDir.listFiles(); + if (mCacheDir != null) { + File[] cachedFiles = mCacheDir.listFiles(); if (cachedFiles != null) { for (File cachedFile : cachedFiles) { size += calculateSize(cachedFile); count += 1; - lastUsageDates.put(cachedFile, cachedFile.lastModified()); + mLastUsageDateMaps.put(cachedFile, cachedFile.lastModified()); } - cacheSize.set(size); - cacheCount.set(count); + mCacheSize.set(size); + mCacheCount.set(count); } } } @@ -63,49 +70,50 @@ public void run() { /** * 添加文件 - * @param file + * @param file 文件 */ - protected void put(File file) { + protected void put(final File file) { + if (file == null) return; // 获取文件总数 - int curCacheCount = cacheCount.get(); + int curCacheCount = mCacheCount.get(); // 判断是否超过数量限制 - while (curCacheCount + 1 > countLimit) { + while (curCacheCount + 1 > mCountLimit) { // 删除文件, 并获取删除的文件大小 long freedSize = removeNext(); // 删除文件大小 - cacheSize.addAndGet(-freedSize); + mCacheSize.addAndGet(-freedSize); // 递减文件 - curCacheCount = cacheCount.addAndGet(-1); + curCacheCount = mCacheCount.addAndGet(-1); } // 累加文件数量 - cacheCount.addAndGet(1); + mCacheCount.addAndGet(1); // 计算文件总大小 long valueSize = calculateSize(file); // 判断当前缓存大小 - long curCacheSize = cacheSize.get(); + long curCacheSize = mCacheSize.get(); // 判断是否超过大小限制 - while (curCacheSize + valueSize > sizeLimit) { + while (curCacheSize + valueSize > mSizeLimit) { long freedSize = removeNext(); - curCacheSize = cacheSize.addAndGet(-freedSize); + curCacheSize = mCacheSize.addAndGet(-freedSize); } - cacheSize.addAndGet(valueSize); + mCacheSize.addAndGet(valueSize); Long currentTime = System.currentTimeMillis(); file.setLastModified(currentTime); - lastUsageDates.put(file, currentTime); + mLastUsageDateMaps.put(file, currentTime); } /** * 获取文件 - * @param key - * @return + * @param key 保存的 key + * @return {@link File} */ - protected File get(String key) { + protected File get(final String key) { File file = newFile(key); - if (file != null){ + if (file != null) { Long currentTime = System.currentTimeMillis(); file.setLastModified(currentTime); - lastUsageDates.put(file, currentTime); + mLastUsageDateMaps.put(file, currentTime); return file; } return file; @@ -113,24 +121,24 @@ protected File get(String key) { /** * 创建文件对象 - * @param key - * @return + * @param key 保存的 key + * @return {@link File} */ - protected File newFile(String key) { - if (key != null){ - return new File(cacheDir, key.hashCode() + ""); + protected File newFile(final String key) { + if (key != null) { + return new File(mCacheDir, key.hashCode() + ""); } return null; } /** * 删除文件 - * @param key - * @return + * @param key 保存的 key + * @return {@code true} 删除成功, {@code false} 删除失败 */ - protected boolean remove(String key) { + protected boolean remove(final String key) { File file = get(key); - if (file != null){ + if (file != null) { return file.delete(); } return false; @@ -140,9 +148,9 @@ protected boolean remove(String key) { * 清空全部缓存数据 */ protected void clear() { - lastUsageDates.clear(); - cacheSize.set(0); - File[] files = cacheDir.listFiles(); + mLastUsageDateMaps.clear(); + mCacheSize.set(0); + File[] files = mCacheDir.listFiles(); if (files != null) { for (File f : files) { f.delete(); @@ -152,16 +160,16 @@ protected void clear() { /** * 移除旧的文件 - * @return + * @return 移除的文件大小 */ private long removeNext() { - if (lastUsageDates.isEmpty()) { + if (mLastUsageDateMaps.isEmpty()) { return 0; } Long oldestUsage = null; File mostLongUsedFile = null; - Set> entries = lastUsageDates.entrySet(); - synchronized (lastUsageDates) { + Set> entries = mLastUsageDateMaps.entrySet(); + synchronized (mLastUsageDateMaps) { for (Map.Entry entry : entries) { if (mostLongUsedFile == null) { mostLongUsedFile = entry.getKey(); @@ -178,20 +186,20 @@ private long removeNext() { long fileSize = calculateSize(mostLongUsedFile); if (mostLongUsedFile.delete()) { - lastUsageDates.remove(mostLongUsedFile); + mLastUsageDateMaps.remove(mostLongUsedFile); } return fileSize; } /** * 计算文件大小 - * @param file - * @return + * @param file 文件 + * @return 文件大小 */ - private long calculateSize(File file) { - if (file != null){ + private long calculateSize(final File file) { + if (file != null) { return file.length(); } - return 0l; + return 0L; } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/cache/DevCacheUtils.java b/lib/DevApp/src/main/java/dev/utils/app/cache/DevCacheUtils.java new file mode 100644 index 0000000000..91b5675d6d --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/cache/DevCacheUtils.java @@ -0,0 +1,319 @@ +package dev.utils.app.cache; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.PixelFormat; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; + +import java.io.ByteArrayOutputStream; + +import dev.utils.LogPrintUtils; + +/** + * detail: 缓存内部工具类 + * @author 杨福海 (michael) www.yangfuhai.com + * @author Ttt ( 重写、规范注释、逻辑判断等 ) + */ +final class DevCacheUtils { + + private DevCacheUtils() { + } + + // 日志 TAG + private static final String TAG = DevCacheUtils.class.getSimpleName(); + // 空格 + private static final char mSeparator = ' '; + + /** + * 判断缓存的 String 数据是否到期 + * @param data 待存储数据 + * @return {@code true} 到期了, {@code false} 还没有到期 + */ + public static boolean isDue(final String data) { + if (data == null) return true; + return isDue(data.getBytes()); + } + + /** + * 判断缓存的 byte 数据是否到期 + * @param data 待存储数据 + * @return {@code true} 到期了, {@code false} 还没有到期 + */ + public static boolean isDue(final byte[] data) { + // 获取时间数据信息 + String[] strs = getDateInfoFromDate(data); + // 判断是否过期 + if (strs != null && strs.length == 2) { + // 保存的时间 + String saveTimeStr = strs[0]; + // 判断是否 0 开头, 是的话裁剪 + while (saveTimeStr.startsWith("0")) { + saveTimeStr = saveTimeStr.substring(1); + } + // 转换时间 + long saveTime = Long.valueOf(saveTimeStr); // 保存时间 + long deleteAfter = Long.valueOf(strs[1]); // 过期时间 + // 判断当前时间是否大于 保存时间 + 过期时间 + return System.currentTimeMillis() > saveTime + deleteAfter * 1000; + } + return false; + } + + // = + + /** + * 保存数据, 创建时间信息 + * @param second 时间 ( 秒 ) + * @param strInfo 字符串信息 + * @return 字符串, 包含创建时间、存储数据等 + */ + public static String newStringWithDateInfo(final int second, final String strInfo) { + return createDateInfo(second) + strInfo; + } + + /** + * 保存数据, 创建时间信息 + * @param second 时间 ( 秒 ) + * @param data 待存储数据 + * @return byte[], 包含创建时间、存储数据等 + */ + public static byte[] newByteArrayWithDateInfo(final int second, final byte[] data) { + if (data != null) { + try { + byte[] dateArys = createDateInfo(second).getBytes(); + return arraycopy(dateArys, data); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newByteArrayWithDateInfo"); + } + } + return null; + } + + // = + + /** + * 创建时间信息 + * @param second 时间 ( 秒 ) + * @return 时间信息字符串 + */ + private static String createDateInfo(final int second) { + String currentTime = System.currentTimeMillis() + ""; + while (currentTime.length() < 13) { + currentTime = "0" + currentTime; + } + return currentTime + "-" + second + mSeparator; + } + + /** + * 清空时间信息 + * @param strInfo 存储数据 + * @return 存储数据 + */ + public static String clearDateInfo(final String strInfo) { + if (strInfo != null && hasDateInfo(strInfo.getBytes())) { + return strInfo.substring(strInfo.indexOf(mSeparator) + 1); + } + return strInfo; + } + + /** + * 清空时间信息 + * @param data 存储数据 + * @return 存储数据 + */ + public static byte[] clearDateInfo(final byte[] data) { + if (hasDateInfo(data)) { + try { + return copyOfRange(data, indexOf(data, mSeparator) + 1, data.length); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "clearDateInfo"); + } + } + return data; + } + + /** + * 判断是否时间信息 + * @param data 时间数据 + * @return {@code true} yes, {@code false} no + */ + private static boolean hasDateInfo(final byte[] data) { + return data != null && data.length > 15 && data[13] == '-' && indexOf(data, mSeparator) > 14; + } + + /** + * 获取时间信息 - 保存时间、过期时间 + * @param data 时间数据 + * @return String[] + */ + private static String[] getDateInfoFromDate(final byte[] data) { + if (hasDateInfo(data)) { + try { + // 保存时间 + String saveDate = new String(copyOfRange(data, 0, 13)); + // 过期时间 + String deleteAfter = new String(copyOfRange(data, 14, indexOf(data, mSeparator))); + // 返回数据 + return new String[]{saveDate, deleteAfter}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDateInfoFromDate"); + } + } + return null; + } + + /** + * 获取字符所在的索引 + * @param data 数据 + * @param ch 字符 + * @return 对应的索引 + */ + private static int indexOf(final byte[] data, final char ch) { + if (data != null) { + for (int i = 0; i < data.length; i++) { + if (data[i] == ch) { + return i; + } + } + } + return -1; + } + + /** + * 复制 byte[] 范围 + * @param original 原始数据 + * @param from 开始位置 + * @param to 所需位置 + * @return byte[] + * @throws Exception 索引异常 + */ + private static byte[] copyOfRange(final byte[] original, final int from, final int to) throws Exception { + int newLength = to - from; + if (newLength < 0) + throw new IllegalArgumentException(from + " > " + to); + + byte[] copy = new byte[newLength]; + System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); + return copy; + } + + /** + * Bitmap 转 byte[] + * @param bitmap {@link Bitmap} + * @return byte[] + */ + public static byte[] bitmapToBytes(final Bitmap bitmap) { + if (bitmap == null) return null; + ByteArrayOutputStream baos = null; + try { + baos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); + return baos.toByteArray(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "bitmapToBytes"); + } finally { + if (baos != null) { + try { + baos.close(); + } catch (Exception e) { + } + } + } + return null; + } + + /** + * byte[] 转 Bitmap + * @param bytes byte[] + * @return {@link Bitmap} + */ + public static Bitmap bytesToBitmap(final byte[] bytes) { + if (bytes != null && bytes.length != 0) { + try { + return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "bytesToBitmap"); + } + } + return null; + } + + /** + * Drawable 转 Bitmap + * @param drawable {@link Drawable} + * @return {@link Bitmap} + */ + public static Bitmap drawableToBitmap(final Drawable drawable) { + if (drawable == null) return null; + try { + // 取 drawable 的长宽 + int width = drawable.getIntrinsicWidth(); + int height = drawable.getIntrinsicHeight(); + // 取 drawable 的颜色格式 + Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + // 建立对应 bitmap + Bitmap bitmap = Bitmap.createBitmap(width, height, config); + // 建立对应 bitmap 的画布 + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, width, height); + // 把 drawable 内容画到画布中 + drawable.draw(canvas); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "drawableToBitmap"); + } + return null; + } + + /** + * Bitmap 转 Drawable + * @param bitmap {@link Bitmap} + * @return {@link Drawable} + */ + @SuppressWarnings("deprecation") + public static Drawable bitmapToDrawable(final Bitmap bitmap) { + if (bitmap == null) return null; + try { + BitmapDrawable drawable = new BitmapDrawable(bitmap); + drawable.setTargetDensity(bitmap.getDensity()); + return new BitmapDrawable(bitmap); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "bitmapToDrawable"); + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============== + // = ArrayUtils = + // ============== + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + private static byte[] arraycopy(final byte[] prefix, final byte[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + byte[] arrays = new byte[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/AutoFocusAssist.java b/lib/DevApp/src/main/java/dev/utils/app/camera1/AutoFocusAssist.java similarity index 50% rename from DevLibUtils/src/main/java/dev/utils/app/assist/camera/AutoFocusAssist.java rename to lib/DevApp/src/main/java/dev/utils/app/camera1/AutoFocusAssist.java index 3b0aebbf53..c6b0fdd07e 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/AutoFocusAssist.java +++ b/lib/DevApp/src/main/java/dev/utils/app/camera1/AutoFocusAssist.java @@ -1,6 +1,5 @@ -package dev.utils.app.assist.camera; +package dev.utils.app.camera1; -import android.annotation.SuppressLint; import android.hardware.Camera; import android.os.AsyncTask; import android.os.Build; @@ -12,79 +11,108 @@ import dev.utils.LogPrintUtils; /** - * detail: 自动获取焦点 辅助类 - * Created by Ttt + * detail: 摄像头自动获取焦点辅助类 + * @author Ttt + *
+ *     对焦模式
+ *     @see 
+ * 
*/ public final class AutoFocusAssist implements Camera.AutoFocusCallback { // 日志 TAG private final String TAG = AutoFocusAssist.class.getSimpleName(); // 设置对焦模式 - private static final Collection FOCUS_MODES_CALLING_AF; + public static final Collection FOCUS_MODES; static { - // 对焦模式 - // https://blog.csdn.net/fulinwsuafcie/article/details/49558001 - FOCUS_MODES_CALLING_AF = new ArrayList<>(2); - FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO); // 自动对焦 - FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO); // 微距 + FOCUS_MODES = new ArrayList<>(); + FOCUS_MODES.add(Camera.Parameters.FOCUS_MODE_AUTO); // 自动对焦 + FOCUS_MODES.add(Camera.Parameters.FOCUS_MODE_MACRO); // 微距 } - // == 变量 == - // 间隔获取焦点时间 - private long interval = 2000L; + // ======== + // = 变量 = + // ======== + + // 自动对焦时间间隔 + private long mInterval; // 摄像头对象 - private final Camera camera; + private final Camera mCamera; // 判断摄像头是否使用对焦 - private final boolean useAutoFocus; + private final boolean mUseAutoFocus; // 判断是否停止对焦 - private boolean stopped; + private boolean mStopped; // 判断是否对焦中 - private boolean focusing; + private boolean mFocusing; // 对焦任务 - private AsyncTask outstandingTask; + private AsyncTask mOutstandingTask; // 判断是否需要自动对焦 - private boolean isAutoFocus = true; + private boolean mAutoFocus = true; - // == 构造函数 == + // ============ + // = 构造函数 = + // ============ - public AutoFocusAssist(Camera camera){ + /** + * 构造函数 + * @param camera {@link android.hardware.Camera} + */ + public AutoFocusAssist(final Camera camera) { this(camera, 2000L); } - public AutoFocusAssist(Camera camera, long interval) { - this.camera = camera; - this.interval = interval; - // 防止为null - if (camera != null){ + /** + * 构造函数 + * @param camera {@link android.hardware.Camera} + * @param interval 自动对焦时间间隔 + */ + public AutoFocusAssist(final Camera camera, final long interval) { + this.mCamera = camera; + this.mInterval = interval; + // 防止为 null + if (camera != null) { // 获取对象对焦模式 String currentFocusMode = camera.getParameters().getFocusMode(); - // 判断是否(使用/支持)对焦 - useAutoFocus = FOCUS_MODES_CALLING_AF.contains(currentFocusMode); + // 判断是否支持对焦 + mUseAutoFocus = FOCUS_MODES.contains(currentFocusMode); } else { // 不支持对焦 - useAutoFocus = false; + mUseAutoFocus = false; } // 开始任务 start(); } + /** + * 设置对焦模式 + * @param collection 对焦模式集合 + */ + public static void setFocusModes(final Collection collection) { + // 清空旧的 + FOCUS_MODES.clear(); + // 防止为 null + if (collection != null) { + FOCUS_MODES.addAll(collection); + } + } + /** * 是否允许自动对焦 - * @return + * @return {@code true} 自动对焦, {@code false} 非自动对焦 */ public boolean isAutoFocus() { - return isAutoFocus; + return mAutoFocus; } /** * 设置是否开启自动对焦 - * @param autoFocus + * @param autoFocus 是否自动对焦 */ - public void setAutoFocus(boolean autoFocus) { - isAutoFocus = autoFocus; + public void setAutoFocus(final boolean autoFocus) { + this.mAutoFocus = autoFocus; // 判断是否开启自动对焦 - if (isAutoFocus){ + if (autoFocus) { start(); } else { stop(); @@ -92,37 +120,38 @@ public void setAutoFocus(boolean autoFocus) { } /** - * Camera.AutoFocusCallback 重写方法 - * @param success 是否对焦成功 - * @param theCamera 对焦的摄像头 + * 对焦回调 {@link Camera.AutoFocusCallback} 重写方法 + * @param success 是否对焦成功 + * @param theCamera 对焦的 {@link android.hardware.Camera} */ @Override public synchronized void onAutoFocus(boolean success, Camera theCamera) { // 对焦结束, 设置非对焦中 - focusing = false; + mFocusing = false; // 再次自动对焦 autoFocusAgainLater(); } + // ============ // = 内部方法 = + // ============ /** * 再次自动对焦 */ - @SuppressLint("NewApi") private synchronized void autoFocusAgainLater() { - // 不属于停止, 并且任务等于null, 才处理 - if (!stopped && outstandingTask == null) { + // 不属于停止, 并且任务等于 null 才处理 + if (!mStopped && mOutstandingTask == null) { // 初始化任务 AutoFocusTask newTask = new AutoFocusTask(); try { - if (Build.VERSION.SDK_INT >= 11) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // 默认使用异步任务 newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } else { newTask.execute(); } - outstandingTask = newTask; + mOutstandingTask = newTask; } catch (RejectedExecutionException ree) { LogPrintUtils.eTag(TAG, ree, "autoFocusAgainLater"); } @@ -134,22 +163,22 @@ private synchronized void autoFocusAgainLater() { */ public synchronized void start() { // 如果不使用自动对焦, 则不处理 - if (!isAutoFocus){ + if (!mAutoFocus) { return; } // 支持对焦才处理 - if (useAutoFocus) { - // 重置任务为null - outstandingTask = null; + if (mUseAutoFocus) { + // 重置任务为 null + mOutstandingTask = null; // 不属于停止 并且 非对焦中 - if (!stopped && !focusing) { + if (!mStopped && !mFocusing) { try { // 设置自动对焦回调 - camera.autoFocus(this); + mCamera.autoFocus(this); // 表示对焦中 - focusing = true; + mFocusing = true; } catch (RuntimeException re) { - LogPrintUtils.eTag(TAG, re,"start"); + LogPrintUtils.eTag(TAG, re, "start"); // Try again later to keep cycle going autoFocusAgainLater(); } @@ -162,16 +191,16 @@ public synchronized void start() { */ public synchronized void stop() { // 表示属于停止 - stopped = true; + mStopped = true; // 判断是否支持对焦 - if (useAutoFocus) { + if (mUseAutoFocus) { // 关闭任务 cancelOutstandingTask(); try { // 取消对焦 - camera.cancelAutoFocus(); + mCamera.cancelAutoFocus(); } catch (RuntimeException re) { - LogPrintUtils.eTag(TAG, re,"stop"); + LogPrintUtils.eTag(TAG, re, "stop"); } } } @@ -180,24 +209,24 @@ public synchronized void stop() { * 取消对焦任务 */ private synchronized void cancelOutstandingTask() { - if (outstandingTask != null) { - if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) { - outstandingTask.cancel(true); + if (mOutstandingTask != null) { + if (mOutstandingTask.getStatus() != AsyncTask.Status.FINISHED) { + mOutstandingTask.cancel(true); } - outstandingTask = null; + mOutstandingTask = null; } } /** * detail: 自动对焦任务 - * Created by Ttt + * @author Ttt */ private final class AutoFocusTask extends AsyncTask { @Override protected Object doInBackground(Object... voids) { try { // 堵塞时间 - Thread.sleep(interval); + Thread.sleep(mInterval); } catch (InterruptedException e) { } // 开启定时 @@ -205,4 +234,4 @@ protected Object doInBackground(Object... voids) { return null; } } -} +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraAssist.java b/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraAssist.java similarity index 50% rename from DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraAssist.java rename to lib/DevApp/src/main/java/dev/utils/app/camera1/CameraAssist.java index 83dfa70e41..b9aa98f251 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraAssist.java +++ b/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraAssist.java @@ -1,124 +1,123 @@ -package dev.utils.app.assist.camera; +package dev.utils.app.camera1; import android.content.pm.PackageManager; import android.hardware.Camera; import android.view.SurfaceHolder; -import java.io.IOException; - import dev.DevUtils; import dev.utils.LogPrintUtils; -import static android.hardware.Camera.Parameters.FLASH_MODE_OFF; -import static android.hardware.Camera.Parameters.FLASH_MODE_TORCH; - /** * detail: 摄像头辅助类 - * Created by Ttt + * @author Ttt */ public final class CameraAssist { - // 日志TAG + // 日志 TAG private final String TAG = CameraAssist.class.getSimpleName(); - // 摄像头 Camera + // 摄像头对象 private Camera mCamera; // 是否预览中 - private boolean previewing; - // 自动对焦时间 - private long autoInterval = 2000l; + private boolean mPreviewing; + // 自动对焦时间间隔 + private long mAutoInterval = 2000L; // 预览通知 - private PreviewNotify previewNotify; + private PreviewNotify mPreviewNotify; + + // ============== // = 内部工具类 = + // ============== + // 摄像头大小计算 - private CameraSizeAssist cameraSizeAssist; + private CameraSizeAssist mCameraSizeAssist; // 自动获取焦点辅助类 - private AutoFocusAssist autoFocusAssist; + private AutoFocusAssist mAutoFocusAssist; - public CameraAssist(){ + public CameraAssist() { } - public CameraAssist(Camera camera) { + /** + * 构造函数 + * @param camera {@link android.hardware.Camera} + */ + public CameraAssist(final Camera camera) { setCamera(camera); } - public CameraAssist(Camera camera, long interval) { - this.autoInterval = interval; + /** + * 构造函数 + * @param camera {@link android.hardware.Camera} + * @param interval 自动对焦时间间隔 + */ + public CameraAssist(final Camera camera, final long interval) { + this.mAutoInterval = interval; setCamera(camera); } - // == 操作方法 == + // ============ + // = 操作方法 = + // ============ /** * 打开摄像头程序 - * @param holder + * @param holder {@link SurfaceHolder} + * @return {@link CameraAssist} + * @throws Exception 设置预览画面, 异常时抛出 */ - public synchronized CameraAssist openDriver(SurfaceHolder holder) throws IOException { + public synchronized CameraAssist openDriver(final SurfaceHolder holder) throws Exception { Camera theCamera = mCamera; // 设置预览 Holder theCamera.setPreviewDisplay(holder); -// // 获取之前的摄像头配置信息 -// Camera.Parameters parameters = theCamera.getParameters(); -// String parametersFlattened = parameters == null ? null : parameters.flatten(); -// try { -// // xxxx -// } catch (RuntimeException re) { -// if (parametersFlattened != null) { -// parameters = theCamera.getParameters(); -// parameters.unflatten(parametersFlattened); -// try { -// theCamera.setParameters(parameters); -// } catch (RuntimeException re2) { -// } -// } -// } return this; } /** - * 关闭相机驱动程 + * 关闭摄像头程序 */ public synchronized void closeDriver() { // 释放摄像头资源 freeCameraResource(); } - // == 预览相关 == + // ============ + // = 预览相关 = + // ============ /** - * 开始将Camera画面预览到手机上 + * 开始将 Camera 画面预览到手机上 */ public synchronized void startPreview() { Camera theCamera = mCamera; - if (theCamera != null && !previewing) { + if (theCamera != null && !mPreviewing) { // 开始预览 theCamera.startPreview(); // 表示预览中 - previewing = true; + mPreviewing = true; // 初始化自动获取焦点 - autoFocusAssist = new AutoFocusAssist(mCamera, autoInterval); + mAutoFocusAssist = new AutoFocusAssist(mCamera, mAutoInterval); // 开始预览通知 - if (previewNotify != null){ - previewNotify.startPreviewNotify(); + if (mPreviewNotify != null) { + mPreviewNotify.startPreviewNotify(); } } } /** - * 停止 Cmaera 画面预览 + * 停止 Camera 画面预览 */ public synchronized void stopPreview() { - if (autoFocusAssist != null) { - autoFocusAssist.stop(); - autoFocusAssist = null; + if (mAutoFocusAssist != null) { + mAutoFocusAssist.stop(); + mAutoFocusAssist = null; } - if (mCamera != null && previewing) { + if (mCamera != null && mPreviewing) { // 停止预览 mCamera.stopPreview(); // 表示非预览中 - previewing = false; + mPreviewing = false; // 停止预览通知 - if (previewNotify != null){ - previewNotify.stopPreviewNotify(); + if (mPreviewNotify != null) { + mPreviewNotify.stopPreviewNotify(); } } } @@ -141,30 +140,32 @@ private void freeCameraResource() { } } - // == 摄像头相关 == + // ============== + // = 摄像头相关 = + // ============== // 预览尺寸大小 private Camera.Size mPreviewSize = null; /** * 获取相机分辨率 - * @return + * @return {@link Camera.Size} 相机分辨率 */ public Camera.Size getCameraResolution() { - if (mPreviewSize == null){ + if (mPreviewSize == null) { // 获取预览大小 - mPreviewSize = cameraSizeAssist.getPreviewSize(); + mPreviewSize = mCameraSizeAssist.getPreviewSize(); return mPreviewSize; } return mPreviewSize; } /** - * 获取预览大小 - * @return + * 获取预览分辨率 + * @return {@link Camera.Size} 预览分辨率 */ public Camera.Size getPreviewSize() { - if (null != mCamera) { + if (mCamera != null && mCamera.getParameters() != null) { return mCamera.getParameters().getPreviewSize(); } return null; @@ -172,15 +173,15 @@ public Camera.Size getPreviewSize() { /** * 获取 Camera.Size 计算辅助类 - * @return + * @return {@link CameraSizeAssist} */ public CameraSizeAssist getCameraSizeAssist() { - return cameraSizeAssist; + return mCameraSizeAssist; } /** * 获取摄像头 - * @return + * @return {@link android.hardware.Camera} */ public Camera getCamera() { return mCamera; @@ -188,53 +189,63 @@ public Camera getCamera() { /** * 设置摄像头 - * @param camera + * @param camera {@link android.hardware.Camera} + * @return {@link CameraAssist} */ - public void setCamera(Camera camera) { + public CameraAssist setCamera(final Camera camera) { this.mCamera = camera; - // 初始化 Camera大小 - this.cameraSizeAssist = new CameraSizeAssist(mCamera); + // 初始化 Camera 大小 + this.mCameraSizeAssist = new CameraSizeAssist(mCamera); + return this; } /** * 设置预览回调 - * @param previewNotify + * @param previewNotify 预览通知接口 + * @return {@link CameraAssist} */ - public CameraAssist setPreviewNotify(PreviewNotify previewNotify) { - this.previewNotify = previewNotify; + public CameraAssist setPreviewNotify(final PreviewNotify previewNotify) { + this.mPreviewNotify = previewNotify; return this; } /** * 设置是否开启自动对焦 - * @param autoFocus + * @param autoFocus 是否自动对焦 + * @return {@link CameraAssist} */ - public CameraAssist setAutoFocus(boolean autoFocus) { - if (autoFocusAssist != null){ - autoFocusAssist.setAutoFocus(autoFocus); + public CameraAssist setAutoFocus(final boolean autoFocus) { + if (mAutoFocusAssist != null) { + mAutoFocusAssist.setAutoFocus(autoFocus); } return this; } /** * 是否预览中 - * @return + * @return {@code true} 预览中, {@code false} 非预览 */ public boolean isPreviewing() { - return previewing; + return mPreviewing; } /** * 设置自动对焦时间间隔 - * @param autoInterval + * @param autoInterval 自动对焦时间间隔 + * @return {@link CameraAssist} */ - public void setAutoInterval(long autoInterval) { - this.autoInterval = autoInterval; + public CameraAssist setAutoInterval(final long autoInterval) { + this.mAutoInterval = autoInterval; + return this; } - // == 闪光灯相关 == + // ====================== + // = 其他工具类实现代码 = + // ====================== - // FlashlightUtils + // =================== + // = FlashlightUtils = + // =================== /** * 打开闪光灯 @@ -244,8 +255,8 @@ public void setFlashlightOn() { return; } Camera.Parameters parameters = mCamera.getParameters(); - if (!FLASH_MODE_TORCH.equals(parameters.getFlashMode())) { - parameters.setFlashMode(FLASH_MODE_TORCH); + if (!Camera.Parameters.FLASH_MODE_TORCH.equals(parameters.getFlashMode())) { + parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); mCamera.setParameters(parameters); } } @@ -258,37 +269,39 @@ public void setFlashlightOff() { return; } Camera.Parameters parameters = mCamera.getParameters(); - if (FLASH_MODE_TORCH.equals(parameters.getFlashMode())) { - parameters.setFlashMode(FLASH_MODE_OFF); + if (Camera.Parameters.FLASH_MODE_TORCH.equals(parameters.getFlashMode())) { + parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); mCamera.setParameters(parameters); } } /** * 是否打开闪光灯 - * @return + * @return {@code true} 打开, {@code false} 关闭 */ public boolean isFlashlightOn() { if (mCamera == null) { return false; } Camera.Parameters parameters = mCamera.getParameters(); - return FLASH_MODE_TORCH.equals(parameters.getFlashMode()); + return Camera.Parameters.FLASH_MODE_TORCH.equals(parameters.getFlashMode()); } /** * 是否支持手机闪光灯 - * @return + * @return {@code true} 支持, {@code false} 不支持 */ public static boolean isFlashlightEnable() { return DevUtils.getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH); } + // ======== // = 接口 = + // ======== /** - * detail: 预览通知 - * Created by Ttt + * detail: 预览通知接口 + * @author Ttt */ public interface PreviewNotify { @@ -302,4 +315,4 @@ public interface PreviewNotify { */ void startPreviewNotify(); } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraSizeAssist.java b/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraSizeAssist.java new file mode 100644 index 0000000000..48d9cbb07f --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraSizeAssist.java @@ -0,0 +1,753 @@ +package dev.utils.app.camera1; + +import android.graphics.Point; +import android.hardware.Camera; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import dev.utils.LogPrintUtils; +import dev.utils.app.ScreenUtils; + +/** + * detail: 摄像头 ( 预览、输出大小 ) 辅助类 + * @author Ttt + *
+ *     正常只需要摄像头权限
+ *     所需权限
+ *      摄像头
+ *      震动
+ *      手电筒
+ * 
+ */ +public final class CameraSizeAssist { + + // 日志 TAG + private final String TAG = CameraSizeAssist.class.getSimpleName(); + // 摄像头对象 + private Camera mCamera; + // 默认最大的偏差 + private final double MAX_ASPECT_DISTORTION = 0.15d; + // 最小尺寸, 小于该尺寸则不处理 + private final int MIN_PREVIEW_PIXELS = 480 * 320; + + // ============ + // = 构造函数 = + // ============ + + /** + * 构造函数 + * @param camera {@link android.hardware.Camera} + */ + public CameraSizeAssist(final Camera camera) { + this.mCamera = camera; + } + + /** + * 获取摄像头 + * @return {@link android.hardware.Camera} + */ + public Camera getCamera() { + return mCamera; + } + + // ================ + // = 预览大小相关 = + // ================ + + /** + * 设置预览大小 + * @param previewSize 预览 {@link Camera.Size} + * @return {@link Camera.Parameters} + */ + public Camera.Parameters setPreviewSize(final Camera.Size previewSize) { + return setPreviewSize(null, previewSize); + } + + /** + * 设置预览大小 + * @param parameters {@link Camera.Parameters} + * @param previewSize 预览 {@link Camera.Size} + * @return {@link Camera.Parameters} + */ + public Camera.Parameters setPreviewSize(Camera.Parameters parameters, final Camera.Size previewSize) { + if (mCamera != null && previewSize != null) { + try { + if (parameters == null) { + parameters = mCamera.getParameters(); + } + // 设置预览大小 + parameters.setPreviewSize(previewSize.width, previewSize.height); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPreviewSize"); + } + } + return parameters; + } + + // = + + /** + * 根据手机支持的预览分辨率计算, 设置预览尺寸 + * @return {@link Camera.Size} 预览分辨率 + */ + public Camera.Size getPreviewSize() { + return getPreviewSize(null, -1d); + } + + /** + * 根据手机支持的预览分辨率计算, 设置预览尺寸 + * @param point {@link Point} point.x = 宽, point.y = 高 + * @return {@link Camera.Size} 预览分辨率 + */ + public Camera.Size getPreviewSize(final Point point) { + return getPreviewSize(point, -1d); + } + + /** + * 根据手机支持的预览分辨率计算, 设置预览尺寸 + * @param distortion 偏差比例值 + * @return {@link Camera.Size} 预览分辨率 + */ + public Camera.Size getPreviewSize(final double distortion) { + return getPreviewSize(null, distortion); + } + + /** + * 根据手机支持的预览分辨率计算, 设置预览尺寸 ( 无任何操作, 单独把 Camera 显示到 SurfaceView 预览尺寸 ) + * @param point 指定的尺寸 ( 为 null, 则使用屏幕尺寸 ) + * ( 从指定的宽高, 开始往下 ( 超过的不处理 ) 选择最接近尺寸 ) point.x = 宽, point.y = 高 + * @param distortion 偏差比例值 + * @return {@link Camera.Size} 预览分辨率 + */ + public Camera.Size getPreviewSize(final Point point, final double distortion) { + if (mCamera == null) { + LogPrintUtils.dTag(TAG, "camera is null"); + return null; + } + try { + // 计算大小并返回 + return calcPreviewSize(point, distortion); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPreviewSize"); + } + return null; + } + + // ================ + // = 拍照大小相关 = + // ================ + + /** + * 设置拍照图片大小 + * @param pictureSize 拍照图片 {@link Camera.Size} + * @return {@link Camera.Parameters} + */ + public Camera.Parameters setPictureSize(final Camera.Size pictureSize) { + return setPictureSize(null, pictureSize); + } + + /** + * 设置拍照图片大小 + * @param parameters {@link Camera.Parameters} + * @param pictureSize 拍照图片 {@link Camera.Size} + * @return {@link Camera.Parameters} + */ + public Camera.Parameters setPictureSize(Camera.Parameters parameters, final Camera.Size pictureSize) { + if (mCamera != null && pictureSize != null) { + try { + if (parameters == null) { + parameters = mCamera.getParameters(); + } + // 设置预览大小 + parameters.setPictureSize(pictureSize.width, pictureSize.height); +// // 设置拍照输出格式 +// parameters.setPictureFormat(PixelFormat.JPEG); +// // 照片质量 +// parameters.set("jpeg-quality", 70); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setPictureSize"); + } + } + return parameters; + } + + // = + + /** + * 根据手机支持的拍照分辨率计算 + * @return {@link Camera.Size} 拍照分辨率 + */ + public Camera.Size getPictureSize() { + return getPictureSize(false, null, -1d); + } + + /** + * 根据手机支持的拍照分辨率计算 + * @param max 是否使用最大的尺寸 + * @return {@link Camera.Size} 拍照分辨率 + */ + public Camera.Size getPictureSize(final boolean max) { + return getPictureSize(max, null, -1d); + } + + /** + * 根据手机支持的拍照分辨率计算 + * @param point 指定的尺寸 ( 为 null, 则使用屏幕尺寸 ) + * ( 从指定的宽高, 开始往下 ( 超过的不处理 ) 选择最接近尺寸 ) point.x = 宽, point.y = 高 + * @return {@link Camera.Size} 拍照分辨率 + */ + public Camera.Size getPictureSize(final Point point) { + return getPictureSize(false, point, -1d); + } + + /** + * 根据手机支持的拍照分辨率计算 + * @param distortion 偏差比例值 + * @return {@link Camera.Size} 拍照分辨率 + */ + public Camera.Size getPictureSize(final double distortion) { + return getPictureSize(false, null, distortion); + } + + /** + * 根据手机支持的拍照分辨率计算 + * @param point {@link Point} point.x = 宽, point.y = 高 + * @param distortion 偏差比例值 + * @return {@link Camera.Size} 拍照分辨率 + */ + public Camera.Size getPictureSize(final Point point, final double distortion) { + return getPictureSize(false, point, distortion); + } + + /** + * 根据手机支持的拍照分辨率计算, 设置预览尺寸 + * @param max 是否使用最大的尺寸 + * @param point 指定的尺寸 ( 为 null, 则使用屏幕尺寸 ) + * ( 从指定的宽高, 开始往下 ( 超过的不处理 ) 选择最接近尺寸 ) point.x = 宽, point.y = 高 + * @param distortion 偏差比例值 + * @return {@link Camera.Size} 拍照分辨率 + */ + public Camera.Size getPictureSize(final boolean max, final Point point, final double distortion) { + if (mCamera == null) { + LogPrintUtils.dTag(TAG, "camera is null"); + return null; + } + try { + // 计算大小并返回 + return calcPictureSize(max, point, distortion); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPictureSize"); + } + return null; + } + + // ==================== + // = 视频录制大小相关 = + // ==================== + + /** + * 根据手机支持的视频录制分辨率计算 + * @return {@link Camera.Size} 视频分辨率 + */ + public Camera.Size getVideoSize() { + return getVideoSize(false, null, -1d, false); + } + + /** + * 根据手机支持的视频录制分辨率计算 + * @param max 是否使用最大的尺寸 + * @return {@link Camera.Size} 视频分辨率 + */ + public Camera.Size getVideoSize(final boolean max) { + return getVideoSize(max, null, -1d, false); + } + + /** + * 根据手机支持的视频录制分辨率计算 + * @param point {@link Point} point.x = 宽, point.y = 高 + * @return {@link Camera.Size} 视频分辨率 + */ + public Camera.Size getVideoSize(final Point point) { + return getVideoSize(false, point, -1d, false); + } + + /** + * 根据手机支持的视频录制分辨率计算 + * @param distortion 偏差比例值 + * @return {@link Camera.Size} 视频分辨率 + */ + public Camera.Size getVideoSize(final double distortion) { + return getVideoSize(false, null, distortion, false); + } + + /** + * 根据手机支持的视频录制分辨率计算 + * @param point {@link Point} point.x = 宽, point.y = 高 + * @param distortion 偏差比例值 + * @return {@link Camera.Size} 视频分辨率 + */ + public Camera.Size getVideoSize(final Point point, final double distortion) { + return getVideoSize(false, point, distortion, false); + } + + /** + * 根据手机支持的视频录制分辨率计算, 设置预览尺寸 + * @param max 是否使用最大的尺寸 + * @param point 指定的尺寸 ( 为 null, 则使用屏幕尺寸 ) + * ( 从指定的宽高, 开始往下 ( 超过的不处理 ) 选择最接近尺寸 ) point.x = 宽, point.y = 高 + * @param distortion 偏差比例值 + * @param minAccord 是否存在最小使用最小 + * @return {@link Camera.Size} 视频分辨率 + */ + public Camera.Size getVideoSize(final boolean max, final Point point, final double distortion, final boolean minAccord) { + if (mCamera == null) { + LogPrintUtils.dTag(TAG, "camera is null"); + return null; + } + try { + // 计算大小并返回 + return calcVideoSize(max, point, distortion, minAccord); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getVideoSize"); + } + return null; + } + + // ============ + // = 预览大小 = + // ============ + + /** + * 根据对应的尺寸, 计算相应最符合的大小 + * @param point 指定的尺寸 ( 为 null, 则使用屏幕尺寸 ) + * ( 从指定的宽高, 开始往下 ( 超过的不处理 ) 选择最接近尺寸 ) point.x = 宽, point.y = 高 + * @param distortion 偏差比例值 + * @return {@link Camera.Size} 预览分辨率 + */ + private Camera.Size calcPreviewSize(Point point, double distortion) { + // 判断是否为 null + if (point == null) { + point = ScreenUtils.getScreenWidthHeightToPoint(); + } + // 如果误差为负数, 则使用默认值 + if (distortion < 0) { + distortion = MAX_ASPECT_DISTORTION; + } + // 获取 Camera 参数 + Camera.Parameters params = mCamera.getParameters(); + // 获取手机支持的分辨率集合, 并以宽度为基准降序排序 + List listPreviewSizes = params.getSupportedPreviewSizes(); + // 防止数据为 null + if (listPreviewSizes == null) { + // 获取默认预览大小 + Camera.Size defaultSize = params.getPreviewSize(); + return defaultSize; + } + + // 进行排序处理, 并以宽度 * 高度 为基准降序排序 + Collections.sort(listPreviewSizes, new Comparator() { + @Override + public int compare(Camera.Size lhs, Camera.Size rhs) { + int leftPixels = lhs.height * lhs.width; + int rightPixels = rhs.height * rhs.width; + + if (leftPixels > rightPixels) { + return -1; + } else if (leftPixels == rightPixels) { + return 0; + } else { + return 1; + } + } + }); + + // = 打印信息 = + if (LogPrintUtils.isPrintLog()) { + StringBuilder builder = new StringBuilder(); + builder.append("预览支持尺寸: \r\n"); + // 打印信息 + for (Camera.Size previewSize : listPreviewSizes) { + // 例: 1080 x 1920 + builder.append(previewSize.width).append("x").append(previewSize.height).append("\r\n"); + } + // 打印尺寸信息 + LogPrintUtils.dTag(TAG, builder.toString()); + } + + // 判断是否竖屏 point.x = 宽, point.y = 高 + boolean isPortrait = point.y > point.x; + // 如果是竖屏, 则修改 + if (isPortrait) { + int tempY = point.y; + int tempX = point.x; + // 进行转换 + point.x = tempY; + point.y = tempX; + } + + // 计算比例值 宽 / 高 + double screenAspectRatio = (double) point.x / (double) point.y; + + // 循环遍历判断 + Iterator iterator = listPreviewSizes.iterator(); + while (iterator.hasNext()) { + // 获取预览大小 + Camera.Size previewSize = iterator.next(); + // 获取宽、高 + int realWidth = previewSize.width; + int realHeight = previewSize.height; + // 小于最小尺寸, 则不处理 + if (realWidth * realHeight < MIN_PREVIEW_PIXELS) { + iterator.remove(); + continue; + } + + // 判断预选的尺寸是否竖屏 + boolean isCandidatePortrait = realWidth < realHeight; + // 翻转宽、高 + int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; + int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; + + // 计算比例 + double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight; + double calcDistortion = Math.abs(aspectRatio - screenAspectRatio); + + // 如果大于指定的尺寸比例差, 则跳过 + if (calcDistortion > distortion) { + iterator.remove(); + continue; + } + + // 如果相符, 则直接跳过 + if (maybeFlippedWidth == point.x && maybeFlippedHeight == point.y) { + return previewSize; + } + } + + // 如果没有精确匹配, 则使用最大预览大小 + if (!listPreviewSizes.isEmpty()) { + // 获取最大的尺寸 + Camera.Size largestPreview = listPreviewSizes.get(0); + return largestPreview; + } + + // = 都不匹配, 则用默认分辨率 = + // 获取默认预览大小 + Camera.Size defaultSize = params.getPreviewSize(); + return defaultSize; + } + + // ============ + // = 拍照大小 = + // ============ + + /** + * 根据对应的尺寸, 计算相应最符合的大小 + * @param max 是否使用最大的尺寸 + * @param point 指定的尺寸 ( 为 null, 则使用屏幕尺寸 ) + * ( 从指定的宽高, 开始往下 ( 超过的不处理 ) 选择最接近尺寸 ) point.x = 宽, point.y = 高 + * @param distortion 偏差比例值 + * @return {@link Camera.Size} 拍照分辨率 + */ + private Camera.Size calcPictureSize(final boolean max, Point point, double distortion) { + // 判断是否为 null + if (point == null) { + point = ScreenUtils.getScreenWidthHeightToPoint(); + } + // 如果误差为负数, 则使用默认值 + if (distortion < 0) { + distortion = MAX_ASPECT_DISTORTION; + } + // 获取 Camera 参数 + Camera.Parameters params = mCamera.getParameters(); + // 获取手机支持的分辨率集合, 并以宽度为基准降序排序 + List listPictureSizes = params.getSupportedPictureSizes(); + // 防止数据为 null + if (listPictureSizes == null) { + // 获取默认拍照大小 + Camera.Size defaultSize = params.getPictureSize(); + return defaultSize; + } + + // 进行排序处理, 并以宽度 * 高度 为基准降序排序 + Collections.sort(listPictureSizes, new Comparator() { + @Override + public int compare(Camera.Size lhs, Camera.Size rhs) { + int leftPixels = lhs.height * lhs.width; + int rightPixels = rhs.height * rhs.width; + + if (leftPixels > rightPixels) { + return -1; + } else if (leftPixels == rightPixels) { + return 0; + } else { + return 1; + } + } + }); + + // = 打印信息 = + if (LogPrintUtils.isPrintLog()) { + StringBuilder builder = new StringBuilder(); + builder.append("拍照支持尺寸: \r\n"); + // 打印信息 + for (Camera.Size pictureSize : listPictureSizes) { + // 例: 1080 x 1920 + builder.append(pictureSize.width).append("x").append(pictureSize.height).append("\r\n"); + } + // 打印尺寸信息 + LogPrintUtils.dTag(TAG, builder.toString()); + } + + // 判断是否拿最大支持的尺寸 + if (max) { + if (!listPictureSizes.isEmpty()) { + // 获取最大的尺寸 + Camera.Size largestPicture = listPictureSizes.get(0); + return largestPicture; + } else { + // 获取默认拍照大小 + Camera.Size defaultSize = params.getPictureSize(); + return defaultSize; + } + } + + // 判断是否竖屏 point.x = 宽, point.y = 高 + boolean isPortrait = point.y > point.x; + // 如果是竖屏, 则修改 + if (isPortrait) { + int tempY = point.y; + int tempX = point.x; + // 进行转换 + point.x = tempY; + point.y = tempX; + } + + // 计算比例值 宽 / 高 + double pictureAspectRatio = (double) point.x / (double) point.y; + + // 判断最大符合 + Camera.Size maxAccordSize = null; + + // 循环遍历判断 + Iterator iterator = listPictureSizes.iterator(); + while (iterator.hasNext()) { + // 获取拍照大小 + Camera.Size pictureSize = iterator.next(); + // 获取宽、高 + int realWidth = pictureSize.width; + int realHeight = pictureSize.height; + // 小于最小尺寸, 则不处理 + if (realWidth * realHeight < MIN_PREVIEW_PIXELS) { + iterator.remove(); + continue; + } + + // 判断预选的尺寸是否竖屏 + boolean isCandidatePortrait = realWidth < realHeight; + // 翻转宽、高 + int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; + int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; + + // 计算比例 + double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight; + double calcDistortion = Math.abs(aspectRatio - pictureAspectRatio); + + // 如果大于指定的尺寸比例差, 则跳过 + if (calcDistortion > distortion) { + iterator.remove(); + continue; + } + + // 如果相符, 则直接跳过 + if (maybeFlippedWidth == point.x && maybeFlippedHeight == point.y) { + return pictureSize; + } + + // 保存最大相符的尺寸 + if (maxAccordSize == null) { + maxAccordSize = pictureSize; + } + } + + // 如果存在最相符的则返回 + if (maxAccordSize != null) { + return maxAccordSize; + } + + // 如果没有精确匹配, 则使用最大尺寸大小 + if (!listPictureSizes.isEmpty()) { + // 获取最大的尺寸 + Camera.Size largestPicture = listPictureSizes.get(0); + return largestPicture; + } + + // = 都不匹配, 则用默认分辨率 = + // 获取默认拍照大小 + Camera.Size defaultSize = params.getPictureSize(); + return defaultSize; + } + + // ================ + // = 视频录制尺寸 = + // ================ + + /** + * 根据对应的尺寸, 计算相应最符合的大小 + * @param max 是否使用最大的尺寸 + * @param point 指定的尺寸 ( 为 null, 则使用屏幕尺寸 ) + * ( 从指定的宽高, 开始往下 ( 超过的不处理 ) 选择最接近尺寸 ) point.x = 宽, point.y = 高 + * @param distortion 偏差比例值 + * @param minAccord 是否判断存在最小使用最小 + * @return {@link Camera.Size} 视频分辨率 + */ + private Camera.Size calcVideoSize(final boolean max, Point point, double distortion, final boolean minAccord) { + // 判断是否为 null + if (point == null) { + point = ScreenUtils.getScreenWidthHeightToPoint(); + } + // 如果误差为负数, 则使用默认值 + if (distortion < 0) { + distortion = MAX_ASPECT_DISTORTION; + } + // 获取 Camera 参数 + Camera.Parameters params = mCamera.getParameters(); + // 获取手机支持的分辨率集合, 并以宽度为基准降序排序 + List listVideoSizes = params.getSupportedVideoSizes(); + // 防止数据为 null + if (listVideoSizes == null) { + // 获取默认拍照大小 + Camera.Size defaultSize = params.getPreferredPreviewSizeForVideo(); + return defaultSize; + } + + // 进行排序处理, 并以宽度 * 高度 为基准降序排序 + Collections.sort(listVideoSizes, new Comparator() { + @Override + public int compare(Camera.Size lhs, Camera.Size rhs) { + int leftPixels = lhs.height * lhs.width; + int rightPixels = rhs.height * rhs.width; + + if (leftPixels > rightPixels) { + return -1; + } else if (leftPixels == rightPixels) { + return 0; + } else { + return 1; + } + } + }); + + // = 打印信息 = + if (LogPrintUtils.isPrintLog()) { + StringBuilder builder = new StringBuilder(); + builder.append("视频录制支持尺寸: \r\n"); + // 打印信息 + for (Camera.Size videoSize : listVideoSizes) { + // 例: 1080 x 1920 + builder.append(videoSize.width).append("x").append(videoSize.height).append("\r\n"); + } + // 打印尺寸信息 + LogPrintUtils.dTag(TAG, builder.toString()); + } + + // 判断是否拿最大支持的尺寸 + if (max) { + if (!listVideoSizes.isEmpty()) { + // 获取最大的尺寸 + Camera.Size largestVideo = listVideoSizes.get(0); + return largestVideo; + } else { + // 获取默认视频大小 + Camera.Size defaultSize = params.getPreferredPreviewSizeForVideo(); + return defaultSize; + } + } + + // 判断是否竖屏 point.x = 宽, point.y = 高 + boolean isPortrait = point.y > point.x; + // 如果是竖屏, 则修改 + if (isPortrait) { + int tempY = point.y; + int tempX = point.x; + // 进行转换 + point.x = tempY; + point.y = tempX; + } + + // 计算比例值 宽 / 高 + double videoAspectRatio = (double) point.x / (double) point.y; + + // 判断最大符合 + Camera.Size maxAccordSize = null; + // 判断最小符合 + Camera.Size minAccordSize = null; + + // 循环遍历判断 + Iterator iterator = listVideoSizes.iterator(); + while (iterator.hasNext()) { + // 获取视频大小 + Camera.Size videoSize = iterator.next(); + // 获取宽、高 + int realWidth = videoSize.width; + int realHeight = videoSize.height; + // 小于最小尺寸, 则不处理 + if (realWidth * realHeight < MIN_PREVIEW_PIXELS) { + iterator.remove(); + continue; + } + + // 判断预选的尺寸是否竖屏 + boolean isCandidatePortrait = realWidth < realHeight; + // 翻转宽、高 + int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; + int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; + + // 计算比例 + double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight; + double calcDistortion = Math.abs(aspectRatio - videoAspectRatio); + + // 如果大于指定的尺寸比例差, 则跳过 + if (calcDistortion > distortion) { + iterator.remove(); + continue; + } + + // 如果相符, 则直接跳过 + if (maybeFlippedWidth == point.x && maybeFlippedHeight == point.y) { + return videoSize; + } + + // 保存最大相符的尺寸 + if (maxAccordSize == null) { + maxAccordSize = videoSize; + } + // 保存最小符合的 + minAccordSize = videoSize; + } + + if (minAccord && minAccordSize != null) { + return minAccordSize; + } + + // 如果存在最相符的则返回 + if (maxAccordSize != null) { + return maxAccordSize; + } + + // 如果没有精确匹配, 则使用最大尺寸大小 + if (!listVideoSizes.isEmpty()) { + // 获取最大的尺寸 + Camera.Size largestVideo = listVideoSizes.get(0); + return largestVideo; + } + + // = 都不匹配, 则用默认分辨率 = + // 获取默认视频大小 + Camera.Size defaultSize = params.getPreferredPreviewSizeForVideo(); + return defaultSize; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraUtils.java b/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraUtils.java new file mode 100644 index 0000000000..7e44ff640e --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/camera1/CameraUtils.java @@ -0,0 +1,220 @@ +package dev.utils.app.camera1; + +import android.hardware.Camera; + +import dev.utils.LogPrintUtils; + +/** + * detail: 摄像头相关工具类 + * @author Ttt + */ +public final class CameraUtils { + + private CameraUtils() { + } + + // 日志 TAG + private static final String TAG = CameraUtils.class.getSimpleName(); + + // ================== + // = 摄像头快速处理 = + // ================== + + /** + * 判断是否支持反转摄像头 ( 是否存在前置摄像头 ) + * @return {@code true} yes, {@code false} no + */ + public static boolean isSupportReverse() { + try { + // 默认是不支持 + int isSupportReverse = 0; + // 判断是否支持前置, 支持则使用前置 + if (checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_FRONT)) { + isSupportReverse += 1; + // = + LogPrintUtils.dTag(TAG, "支持前置摄像头 ( 手机屏幕 )"); + } + // 判断是否支持后置, 是则使用后置 + if (checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_BACK)) { + isSupportReverse += 1; + // = + LogPrintUtils.dTag(TAG, "支持后置摄像头 ( 手机背面 )"); + } + // 如果都支持才表示支持反转 + return isSupportReverse == 2; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isSupportReverse"); + } + // 默认不支持反转摄像头 + return false; + } + + /** + * 检查是否有指定的摄像头 + * @param facing 摄像头标识 id + * @return {@code true} yes, {@code false} no + */ + public static boolean checkCameraFacing(final int facing) { + try { + int cameraCount = Camera.getNumberOfCameras(); + Camera.CameraInfo info = new Camera.CameraInfo(); + for (int i = 0; i < cameraCount; i++) { + Camera.getCameraInfo(i, info); + if (facing == info.facing) { + return true; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "checkCameraFacing"); + } + return false; + } + + /** + * 判断是否使用前置摄像头 + * @param facing 摄像头标识 id + * @return {@code true} yes, {@code false} no + */ + public static boolean isFrontCamera(final int facing) { + return facing == Camera.CameraInfo.CAMERA_FACING_FRONT; + } + + /** + * 判断是否使用后置摄像头 + * @param facing 摄像头标识 id + * @return {@code true} yes, {@code false} no + */ + public static boolean isBackCamera(final int facing) { + return facing == Camera.CameraInfo.CAMERA_FACING_BACK; + } + + /** + * 判断使用的摄像头 + * @param isFrontCamera 是否前置摄像头 + * @return 摄像头标识 id + */ + public static int isUseCameraFacing(final boolean isFrontCamera) { + // 默认使用后置摄像头 + int cameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK; + try { + // 支持的摄像头 - 前置, 后置 + boolean[] cFacingArys = new boolean[]{false, false}; + // 判断是否支持前置 + cFacingArys[0] = checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_FRONT); + // 判断是否支持后置 + cFacingArys[1] = checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_BACK); + // 进行判断想要使用的是前置, 还是后置 + if (isFrontCamera && cFacingArys[0]) { // 使用前置, 必须也支持前置 + // 表示使用前置摄像头 + cameraFacing = Camera.CameraInfo.CAMERA_FACING_FRONT; + } else { + // 表示使用后置摄像头 + cameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isUseCameraFacing"); + } + return cameraFacing; + } + + // = + + /** + * 释放摄像头资源 + * @param camera {@link android.hardware.Camera} + */ + public static void freeCameraResource(final Camera camera) { + try { + if (camera != null) { + camera.setPreviewCallback(null); + camera.stopPreview(); + camera.lock(); + camera.release(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "freeCameraResource"); + } + } + + /** + * 初始化摄像头 + * @param camera {@link android.hardware.Camera} + * @param isFrontCamera {@code true} 前置 ( 屏幕面 ), {@code false} 后置 ( 手机背面 ) + * @return {@link android.hardware.Camera} + */ + public static Camera initCamera(Camera camera, final boolean isFrontCamera) { + // 如果之前存在摄像头数据, 则释放资源 + if (camera != null) { + freeCameraResource(camera); + } + try { + // 进行判断想要使用的是前置, 还是后置 + if (isFrontCamera && checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_FRONT)) { // 使用前置, 必须也支持前置 + // 初始化前置摄像头 + camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT); + } else { + // 初始化后置摄像头 + camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "initCamera"); + // 释放资源 + freeCameraResource(camera); + } + return camera; + } + + /** + * 打开摄像头 + * @param cameraId {@link Camera.CameraInfo} CAMERA_FACING_FRONT( 前置 ), CAMERA_FACING_BACK( 后置 ) + * @return {@link android.hardware.Camera} + */ + public static Camera open(int cameraId) { + // 判断支持的摄像头数量 + int numCameras = Camera.getNumberOfCameras(); + if (numCameras == 0) { + return null; + } + try { + // 判断是否指定哪个摄像头 + boolean explicitRequest = cameraId >= 0; + // 如果没指定, 则进行判断处理 + if (!explicitRequest) { // 默认使用 后置摄像头, 没有后置才用其他 ( 前置 ) + int index = 0; + while (index < numCameras) { + Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); + Camera.getCameraInfo(index, cameraInfo); + if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { + break; + } + index++; + } + cameraId = index; + } + + Camera camera; + if (cameraId < numCameras) { + camera = Camera.open(cameraId); + } else { + if (explicitRequest) { + camera = null; + } else { + // 默认使用后置 + camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); + } + } + return camera; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "open - cameraId: " + cameraId); + } + return null; + } + + /** + * 打开摄像头 ( 默认后置摄像头 ) + * @return {@link android.hardware.Camera} + */ + public static Camera open() { + return open(-1); // Camera.CameraInfo.CAMERA_FACING_BACK + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/image/BitmapUtils.java b/lib/DevApp/src/main/java/dev/utils/app/image/BitmapUtils.java new file mode 100644 index 0000000000..7021bba98d --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/image/BitmapUtils.java @@ -0,0 +1,1402 @@ +package dev.utils.app.image; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.media.ExifInterface; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.FloatRange; +import android.support.annotation.IntRange; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.InputStream; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Bitmap 工具类 + * @author Ttt + *
+ *     Android PorterDuffXfermode 的正确使用方式
+ *     @see 
+ * 
+ */ +public final class BitmapUtils { + + private BitmapUtils() { + } + + // 日志 TAG + private static final String TAG = BitmapUtils.class.getSimpleName(); + + // ============== + // = ImageUtils = + // ============== + + /** + * 判断 Bitmap 对象是否为 null + * @param bitmap {@link Bitmap} + * @return {@code true} yes, {@code false} no + */ + public static boolean isEmpty(final Bitmap bitmap) { + return bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0; + } + + /** + * 判断 Bitmap 对象是否不为 null + * @param bitmap {@link Bitmap} + * @return {@code true} yes, {@code false} no + */ + public static boolean isNotEmpty(final Bitmap bitmap) { + return bitmap != null && bitmap.getWidth() != 0 && bitmap.getHeight() != 0; + } + + // ============ + // = 图片判断 = + // ============ + + /** + * 根据文件判断是否为图片 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + public static boolean isImage(final File file) { + return file != null && isImage(file.getPath()); + } + + /** + * 根据文件判断是否为图片 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + public static boolean isImage(final String filePath) { + if (!isFileExists(filePath)) return false; + BitmapFactory.Options options = new BitmapFactory.Options(); + // 只解析图片信息, 不加载到内存中 + options.inJustDecodeBounds = true; + try { + BitmapFactory.decodeFile(filePath, options); + return options.outWidth != -1 && options.outHeight != -1; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isImage"); + } + return false; + } + + // ======== + // = 宽高 = + // ======== + + /** + * 获取 Bitmap 宽度 + * @param bitmap 源图片 + * @return Bitmap 宽度 + */ + public static int getBitmapWidth(final Bitmap bitmap) { + if (isEmpty(bitmap)) return 0; + return bitmap.getWidth(); + } + + /** + * 获取 Bitmap 高度 + * @param bitmap 源图片 + * @return Bitmap 高度 + */ + public static int getBitmapHeight(final Bitmap bitmap) { + if (isEmpty(bitmap)) return 0; + return bitmap.getHeight(); + } + + /** + * 获取 Bitmap 宽高 + * @param bitmap 源图片 + * @return int[] { 宽度, 高度 } + */ + public static int[] getBitmapWidthHeight(final Bitmap bitmap) { + if (isEmpty(bitmap)) return new int[]{0, 0}; + return new int[]{bitmap.getWidth(), bitmap.getHeight()}; + } + + // = + + /** + * 获取 Bitmap 宽高 + * @param file 文件 + * @return int[] { 宽度, 高度 } + */ + public static int[] getBitmapWidthHeight(final File file) { + return getBitmapWidthHeight(getAbsolutePath(file)); + } + + /** + * 获取 Bitmap 宽高 + * @param filePath 文件路径 + * @return int[] { 宽度, 高度 } + */ + public static int[] getBitmapWidthHeight(final String filePath) { + if (!isFileExists(filePath)) return new int[]{0, 0}; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + // 只解析图片信息, 不加载到内存中 + options.inJustDecodeBounds = true; + // 返回的 bitmap 为 null + BitmapFactory.decodeFile(filePath, options); + // options.outHeight 为原始图片的高 + return new int[]{options.outWidth, options.outHeight}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmapWidthHeight"); + } + return new int[]{0, 0}; + } + + // = + + /** + * 获取 Bitmap 宽高 + * @param resId resource identifier + * @return int[] { 宽度, 高度 } + */ + public static int[] getBitmapWidthHeight(@DrawableRes final int resId) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + // 只解析图片信息, 不加载到内存中 + options.inJustDecodeBounds = true; + // 返回的 bitmap 为 null + BitmapFactory.decodeResource(getResources(), resId, options); + // options.outHeight 为原始图片的高 + return new int[]{options.outWidth, options.outHeight}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmapWidthHeight"); + } + return new int[]{0, 0}; + } + + // = + + /** + * 获取 Bitmap 宽高 + * @param inputStream {@link InputStream} + * @return int[] { 宽度, 高度 } + */ + public static int[] getBitmapWidthHeight(final InputStream inputStream) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + // 只解析图片信息, 不加载到内存中 + options.inJustDecodeBounds = true; + // 返回的 bitmap 为 null + BitmapFactory.decodeStream(inputStream, null, options); + // options.outHeight 为原始图片的高 + return new int[]{options.outWidth, options.outHeight}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmapWidthHeight"); + } + return new int[]{0, 0}; + } + + // = + + /** + * 获取 Bitmap 宽高 + * @param fd 文件描述 + * @return int[] { 宽度, 高度 } + */ + public static int[] getBitmapWidthHeight(final FileDescriptor fd) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + // 只解析图片信息, 不加载到内存中 + options.inJustDecodeBounds = true; + // 返回的 bitmap 为 null + BitmapFactory.decodeFileDescriptor(fd, null, options); + // options.outHeight 为原始图片的高 + return new int[]{options.outWidth, options.outHeight}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmapWidthHeight"); + } + return new int[]{0, 0}; + } + + /** + * 获取 Bitmap 宽高 + * @param data byte[] + * @return int[] { 宽度, 高度 } + */ + public static int[] getBitmapWidthHeight(final byte[] data) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + // 只解析图片信息, 不加载到内存中 + options.inJustDecodeBounds = true; + // 返回的 bitmap 为 null + BitmapFactory.decodeByteArray(data, 0, data.length, options); + // options.outHeight 为原始图片的高 + return new int[]{options.outWidth, options.outHeight}; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmapWidthHeight"); + } + return new int[]{0, 0}; + } + + // = + + /** + * 复制 Bitmap + * @param bitmap {@link Bitmap} + * @return {@link Bitmap} + */ + public static Bitmap copy(final Bitmap bitmap) { + return copy(bitmap, true); + } + + /** + * 复制 Bitmap + * @param bitmap {@link Bitmap} + * @param isMutable 是否允许编辑 + * @return {@link Bitmap} + */ + public static Bitmap copy(final Bitmap bitmap, final boolean isMutable) { + if (isEmpty(bitmap)) return null; + return bitmap.copy(bitmap.getConfig(), isMutable); + } + + // = + + /** + * 获取 Alpha 位图 ( 获取源图片的轮廓 rgb 为 0) + * @param bitmap {@link Bitmap} + * @return Alpha 位图 + */ + public static Bitmap extractAlpha(final Bitmap bitmap) { + if (isEmpty(bitmap)) return null; + return bitmap.extractAlpha(); + } + + /** + * 重新编码 Bitmap + * @param bitmap 需要重新编码的 bitmap + * @param format 编码后的格式 如 Bitmap.CompressFormat.PNG + * @param quality 质量 + * @return 重新编码后的图片 + */ + public static Bitmap recode(final Bitmap bitmap, final Bitmap.CompressFormat format, + @IntRange(from = 0, to = 100) final int quality) { + return recode(bitmap, format, quality, null); + } + + /** + * 重新编码 Bitmap + * @param bitmap 需要重新编码的 bitmap + * @param format 编码后的格式 如 Bitmap.CompressFormat.PNG + * @param quality 质量 + * @param options {@link BitmapFactory.Options} + * @return 重新编码后的图片 + */ + public static Bitmap recode(final Bitmap bitmap, final Bitmap.CompressFormat format, + @IntRange(from = 0, to = 100) final int quality, + final BitmapFactory.Options options) { + if (isEmpty(bitmap) || format == null) return null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(format, quality, baos); + byte[] data = baos.toByteArray(); + return BitmapFactory.decodeByteArray(data, 0, data.length, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "recode"); + } + return null; + } + + /** + * Bitmap 通知回收 + * @param bitmap 待回收图片 + */ + public static void recycle(final Bitmap bitmap) { + if (bitmap == null) return; + if (!bitmap.isRecycled()) { + try { + bitmap.recycle(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "recycle"); + } + } + } + + // =============== + // = Bitmap 操作 = + // =============== + + // ======== + // = 旋转 = + // ======== + + /** + * 旋转图片 + * @param bitmap 待操作源图片 + * @param degrees 旋转角度 + * @return 旋转后的图片 + */ + public static Bitmap rotate(final Bitmap bitmap, final float degrees) { + if (isEmpty(bitmap)) return null; + Matrix matrix = new Matrix(); + matrix.postRotate(degrees); + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); + } + + /** + * 旋转图片 + * @param bitmap 待操作源图片 + * @param degrees 旋转角度 + * @param px 旋转中心点在 X 轴的坐标 + * @param py 旋转中心点在 Y 轴的坐标 + * @return 旋转后的图片 + */ + public static Bitmap rotate(final Bitmap bitmap, final float degrees, final float px, final float py) { + if (isEmpty(bitmap)) return null; + Matrix matrix = new Matrix(); + matrix.postRotate(degrees, px, py); + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); + } + + /** + * 读取图片属性, 获取图片被旋转的角度 + * @param filePath 文件路径 + * @return 旋转角度 + */ + public static int getRotateDegree(final String filePath) { + try { + ExifInterface exifInterface = new ExifInterface(filePath); + int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_90: + return 90; + case ExifInterface.ORIENTATION_ROTATE_180: + return 180; + case ExifInterface.ORIENTATION_ROTATE_270: + return 270; + default: + return 0; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getRotateDegree"); + return 0; + } + } + + // ======== + // = 翻转 = + // ======== + + /** + * 水平翻转图片 ( 左右颠倒 ) + * @param bitmap 待操作源图片 + * @return 翻转后的图片 + */ + public static Bitmap reverseByHorizontal(final Bitmap bitmap) { + return reverse(bitmap, true); + } + + /** + * 垂直翻转图片 ( 上下颠倒 ) + * @param bitmap 待操作源图片 + * @return 翻转后的图片 + */ + public static Bitmap reverseByVertical(final Bitmap bitmap) { + return reverse(bitmap, false); + } + + /** + * 翻转图片 + * @param bitmap 待操作源图片 + * @param horizontal 是否水平翻转 + * @return 翻转后的图片 + */ + public static Bitmap reverse(final Bitmap bitmap, final boolean horizontal) { + if (isEmpty(bitmap)) return null; + Matrix matrix = new Matrix(); + if (horizontal) { + matrix.preScale(-1, 1); + } else { + matrix.preScale(1, -1); + } + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); + } + + // ======== + // = 缩放 = + // ======== + + /** + * 缩放图片 ( 指定所需宽高 ) + * @param bitmap 待操作源图片 + * @param newSize 新尺寸 ( 宽高 ) + * @return 缩放后的图片 + */ + public static Bitmap zoom(final Bitmap bitmap, final int newSize) { + return zoom(bitmap, newSize, newSize); + } + + /** + * 缩放图片 ( 指定所需宽高 ) + * @param bitmap 待操作源图片 + * @param newWidth 新宽度 + * @param newHeight 新高度 + * @return 缩放后的图片 + */ + public static Bitmap zoom(final Bitmap bitmap, final int newWidth, final int newHeight) { + if (isEmpty(bitmap)) return null; + return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true); + } + + // = + + /** + * 缩放图片 ( 比例缩放 ) + * @param bitmap 待操作源图片 + * @param scale 缩放倍数 + * @return 缩放后的图片 + */ + public static Bitmap scale(final Bitmap bitmap, final float scale) { + return scale(bitmap, scale, scale); + } + + /** + * 缩放图片 ( 比例缩放 ) + * @param bitmap 待操作源图片 + * @param scaleX 横向缩放比例 ( 缩放宽度倍数 ) + * @param scaleY 纵向缩放比例 ( 缩放高度倍数 ) + * @return 缩放后的图片 + */ + public static Bitmap scale(final Bitmap bitmap, final float scaleX, final float scaleY) { + if (isEmpty(bitmap)) return null; + Matrix matrix = new Matrix(); + matrix.postScale(scaleX, scaleY); + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + } + + // ======== + // = 倾斜 = + // ======== + + /** + * 倾斜图片 + * @param bitmap 待操作源图片 + * @param kx X 轴倾斜因子 + * @param ky Y 轴倾斜因子 + * @return 倾斜后的图片 + */ + public static Bitmap skew(final Bitmap bitmap, final float kx, final float ky) { + return skew(bitmap, kx, ky, 0, 0); + } + + /** + * 倾斜图片 + *
+     *     倾斜因子 以小数点倾斜 如: 0.1 防止数值过大 Canvas: trying to draw too large
+     * 
+ * @param bitmap 待操作源图片 + * @param kx X 轴倾斜因子 + * @param ky Y 轴倾斜因子 + * @param px X 轴轴心点 + * @param py Y 轴轴心点 + * @return 倾斜后的图片 + */ + public static Bitmap skew(final Bitmap bitmap, final float kx, final float ky, final float px, final float py) { + if (isEmpty(bitmap)) return null; + Matrix matrix = new Matrix(); + matrix.setSkew(kx, ky, px, py); + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + } + + // ======== + // = 裁剪 = + // ======== + + /** + * 裁剪图片 + * @param bitmap 待操作源图片 + * @param width 裁剪宽度 + * @param height 裁剪高度 + * @return 裁剪后的图片 + */ + public static Bitmap clip(final Bitmap bitmap, final int width, final int height) { + return clip(bitmap, 0, 0, width, height); + } + + /** + * 裁剪图片 + * @param bitmap 待操作源图片 + * @param x X 轴开始坐标 + * @param y Y 轴开始坐标 + * @param width 裁剪宽度 + * @param height 裁剪高度 + * @return 裁剪后的图片 + */ + public static Bitmap clip(final Bitmap bitmap, final int x, final int y, final int width, final int height) { + if (isEmpty(bitmap)) return null; + return Bitmap.createBitmap(bitmap, x, y, width, height); + } + + // = + + /** + * 裁剪图片 ( 返回指定比例图片 ) + * @param bitmap 待操作源图片 + * @return 裁剪指定比例的图片 + */ + public static Bitmap crop(final Bitmap bitmap) { + return crop(bitmap, 16.0f, 9.0f); + } + + /** + * 裁剪图片 ( 返回指定比例图片 ) + * @param bitmap 待操作源图片 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return 裁剪指定比例的图片 + */ + public static Bitmap crop(final Bitmap bitmap, final float widthScale, final float heightScale) { + if (bitmap == null) return null; + try { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + // 获取需要裁剪的高度 + int reHeight = (int) ((width * heightScale) / widthScale); + // 判断需要裁剪的高度与偏移差距 + int diffHeight = height - reHeight; + + // 属于宽度 * 对应比例 >= 高度 + if (diffHeight >= 0) { // 以高度做偏移 + return Bitmap.createBitmap(bitmap, 0, diffHeight / 2, width, reHeight, null, false); + } else { // 以宽度做偏移 + // 获取需要裁剪的宽度 + int reWidth = (int) ((height * widthScale) / heightScale); + // 判断需要裁剪的宽度与偏移差距 + int diffWidth = width - reWidth; + // 创建图片 + return Bitmap.createBitmap(bitmap, diffWidth / 2, 0, reWidth, height, null, false); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "crop"); + } + return null; + } + + // =============== + // = 合并 / 叠加 = + // =============== + + /** + * 合并图片 + * @param bgd 后景 Bitmap + * @param fg 前景 Bitmap + * @return 合并后的图片 + */ + public static Bitmap combine(final Bitmap bgd, final Bitmap fg) { + return combine(bgd, fg, PorterDuff.Mode.SRC_ATOP, null, null); + } + + /** + * 合并图片 + * @param bgd 后景 Bitmap + * @param fg 前景 Bitmap + * @param mode 合并模式 {@link PorterDuff.Mode} + * @return 合并后的图片 + */ + public static Bitmap combine(final Bitmap bgd, final Bitmap fg, final PorterDuff.Mode mode) { + return combine(bgd, fg, mode, null, null); + } + + /** + * 合并图片 + * @param bgd 后景 Bitmap + * @param fg 前景 Bitmap + * @param mode 合并模式 {@link PorterDuff.Mode} + * @param bgdPoint 后景绘制 left、top 坐标 + * @param fgPoint 前景绘制 left、top 坐标 + * @return 合并后的图片 + */ + public static Bitmap combine(final Bitmap bgd, final Bitmap fg, final PorterDuff.Mode mode, final Point bgdPoint, final Point fgPoint) { + if (isEmpty(bgd) || isEmpty(fg)) return null; + + int width = bgd.getWidth() > fg.getWidth() ? bgd.getWidth() : fg.getWidth(); + int height = bgd.getHeight() > fg.getHeight() ? bgd.getHeight() : fg.getHeight(); + + Paint paint = new Paint(); + if (mode != null) { + paint.setXfermode(new PorterDuffXfermode(mode)); + } + + Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(newBitmap); + canvas.drawBitmap(bgd, (bgdPoint != null) ? bgdPoint.x : 0, (bgdPoint != null) ? bgdPoint.y : 0, null); + canvas.drawBitmap(fg, (fgPoint != null) ? fgPoint.x : 0, (fgPoint != null) ? fgPoint.y : 0, paint); + return newBitmap; + } + + // = + + /** + * 合并图片 ( 居中 ) + * @param bgd 后景 Bitmap + * @param fg 前景 Bitmap + * @return 合并后的图片 + */ + public static Bitmap combineToCenter(final Bitmap bgd, final Bitmap fg) { + return combineToCenter(bgd, fg, null); + } + + /** + * 合并图片 ( 居中 ) + * @param bgd 后景 Bitmap + * @param fg 前景 Bitmap + * @param mode 合并模式 {@link PorterDuff.Mode} + * @return 合并后的图片 + */ + public static Bitmap combineToCenter(Bitmap bgd, Bitmap fg, final PorterDuff.Mode mode) { + if (isEmpty(bgd) || isEmpty(fg)) return null; + + // 绘制坐标点 + Point bgdPoint = new Point(); + Point fgPoint = new Point(); + + // 宽高信息 + int bgdWidth = bgd.getWidth(); + int bgdHeight = bgd.getHeight(); + + int fgWidth = fg.getWidth(); + int fgHeight = fg.getHeight(); + + if (bgdWidth > fgWidth) { + fgPoint.x = (bgdWidth - fgWidth) / 2; + } else { + bgdPoint.x = (fgWidth - bgdWidth) / 2; + } + + if (bgdHeight > fgHeight) { + fgPoint.y = (bgdHeight - fgHeight) / 2; + } else { + bgdPoint.y = (fgHeight - bgdHeight) / 2; + } + + return combine(bgd, fg, mode, bgdPoint, fgPoint); + } + + // = + + /** + * 合并图片 ( 转为相同大小 ) + * @param bgd 后景 Bitmap + * @param fg 前景 Bitmap + * @return 合并后的图片 + */ + public static Bitmap combineToSameSize(Bitmap bgd, Bitmap fg) { + return combineToSameSize(bgd, fg, PorterDuff.Mode.SRC_ATOP); + } + + /** + * 合并图片 ( 转为相同大小 ) + * @param bgd 后景 Bitmap + * @param fg 前景 Bitmap + * @param mode 合并模式 {@link PorterDuff.Mode} + * @return 合并后的图片 + */ + public static Bitmap combineToSameSize(Bitmap bgd, Bitmap fg, final PorterDuff.Mode mode) { + if (isEmpty(bgd) || isEmpty(fg)) return null; + + int width = bgd.getWidth() < fg.getWidth() ? bgd.getWidth() : fg.getWidth(); + int height = bgd.getHeight() < fg.getHeight() ? bgd.getHeight() : fg.getHeight(); + + if (fg.getWidth() != width && fg.getHeight() != height) { + fg = zoom(fg, width, height); + } + + if (bgd.getWidth() != width && bgd.getHeight() != height) { + bgd = zoom(bgd, width, height); + } + + Paint paint = new Paint(); + if (mode != null) { + paint.setXfermode(new PorterDuffXfermode(mode)); + } + + Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(newBitmap); + canvas.drawBitmap(bgd, 0, 0, null); + canvas.drawBitmap(fg, 0, 0, paint); + return newBitmap; + } + + // ======== + // = 倒影 = + // ======== + + /** + * 图片倒影处理 + * @param bitmap 待操作源图片 + * @return 倒影处理后的图片 + */ + public static Bitmap reflection(final Bitmap bitmap) { + return reflection(bitmap, 0, getBitmapHeight(bitmap)); + } + + /** + * 图片倒影处理 + * @param bitmap 待操作源图片 + * @param reflectionHeight 倒影高度 + * @return 倒影处理后的图片 + */ + public static Bitmap reflection(final Bitmap bitmap, final int reflectionHeight) { + return reflection(bitmap, 0, reflectionHeight); + } + + /** + * 图片倒影处理 + * @param bitmap 待操作源图片 + * @param reflectionSpacing 源图片与倒影之间的间距 + * @param reflectionHeight 倒影高度 + * @return 倒影处理后的图片 + */ + public static Bitmap reflection(final Bitmap bitmap, final int reflectionSpacing, final int reflectionHeight) { + if (isEmpty(bitmap)) return null; + if (reflectionHeight <= 0) return null; + // 获取图片宽高 + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + // 创建画布, 画布分为上中下三部分, 上: 是源图片, 中: 是源图片与倒影的间距, 下: 是倒影 + + // 创建倒影图片 + Bitmap reflectionImage = reverseByVertical(bitmap); // 垂直翻转图片 ( 上下颠倒 ) + // 创建一张宽度与源图片相同, 但高度等于 源图片的高度 + 间距 + 倒影的高度的图片 + Bitmap bitmapWithReflection = Bitmap.createBitmap(width, height + reflectionSpacing + reflectionHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmapWithReflection); + + // 将源图片画到画布的上半部分, 将倒影画到画布的下半部分, 倒影与画布顶部的间距是源图片的高度加上源图片与倒影之间的间距 + canvas.drawBitmap(bitmap, 0, 0, null); + canvas.drawBitmap(reflectionImage, 0, height + reflectionSpacing, null); + reflectionImage.recycle(); + + // 边距负数处理 + int spacing = Math.max(reflectionSpacing, 0); + + // 将倒影改成半透明, 创建画笔, 并设置画笔的渐变从半透明的白色到全透明的白色, 然后再倒影上面画半透明效果 + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setShader(new LinearGradient(0, bitmap.getHeight(), 0, bitmapWithReflection.getHeight() + spacing, + 0x70ffffff, 0x00ffffff, Shader.TileMode.CLAMP)); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); + canvas.drawRect(0, height + spacing, width, bitmapWithReflection.getHeight() + spacing, paint); + return bitmapWithReflection; + } + + // ======== + // = 圆角 = + // ======== + + /** + * 图片圆角处理 ( 非圆形 ) + *
+     *     以宽高中最小值设置为圆角尺寸, 如果宽高一致, 则处理为圆形图片
+     * 
+ * @param bitmap 待操作源图片 + * @return 圆角处理后的图片 + */ + public static Bitmap roundCorner(final Bitmap bitmap) { + if (isEmpty(bitmap)) return null; + return roundCorner(bitmap, Math.min(bitmap.getWidth(), bitmap.getHeight())); + } + + /** + * 图片圆角处理 ( 非圆形 ) + * @param bitmap 待操作源图片 + * @param pixels 圆角大小 + * @return 圆角处理后的图片 + */ + public static Bitmap roundCorner(final Bitmap bitmap, final float pixels) { + if (isEmpty(bitmap)) return null; + // 创建一个同源图片一样大小的矩形, 用于把源图片绘制到这个矩形上 + Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + RectF rectF = new RectF(rect); // 创建一个精度更高的矩形, 用于画出圆角效果 + + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setColor(0xff424242); // 设置画笔的颜色为不透明的灰色 + + Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(newBitmap); + canvas.drawARGB(0, 0, 0, 0); + canvas.drawRoundRect(rectF, pixels, pixels, paint); + // 绘制底圆后, 进行合并 ( 交集处理 ) + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + canvas.drawBitmap(bitmap, rect, rect, paint); + return newBitmap; + } + + // = + + /** + * 图片圆角处理 ( 非圆形 ) - 只有 leftTop、rightTop + * @param bitmap 待操作源图片 + * @param pixels 圆角大小 + * @return 圆角处理后的图片 + */ + public static Bitmap roundCornerTop(final Bitmap bitmap, final float pixels) { + return roundCorner(bitmap, pixels, new boolean[]{true, true, true, false}); + } + + /** + * 图片圆角处理 ( 非圆形 ) - 只有 leftBottom、rightBottom + * @param bitmap 待操作源图片 + * @param pixels 圆角大小 + * @return 圆角处理后的图片 + */ + public static Bitmap roundCornerBottom(final Bitmap bitmap, final float pixels) { + return roundCorner(bitmap, pixels, new boolean[]{true, false, true, true}); + } + + // = + + /** + * 图片圆角处理 ( 非圆形 ) + *
+     *     只要左上圆角: new boolean[] {true, true, false, false};
+     *     只要右上圆角: new boolean[] {false, true, true, false};
+     *     

+ * 只要左下圆角: new boolean[] {true, false, false, true}; + * 只要右下圆角: new boolean[] {false, false, true, true}; + *
+ * @param bitmap 待操作源图片 + * @param pixels 圆角大小 + * @param directions 需要圆角的方向 [left, top, right, bottom] + * @return 圆角处理后的图片 + */ + public static Bitmap roundCorner(final Bitmap bitmap, final float pixels, final boolean[] directions) { + if (isEmpty(bitmap)) return null; + if (directions == null || directions.length != 4) return null; + // 创建一个同源图片一样大小的矩形, 用于把源图片绘制到这个矩形上 + Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + RectF rectF = new RectF(rect); // 创建一个精度更高的矩形, 用于画出圆角效果 + + // ================ + // = 圆角方向控制 = + // ================ + + if (!directions[0]) + rectF.left -= pixels; + + if (!directions[1]) + rectF.top -= pixels; + + if (!directions[2]) + rectF.right += pixels; + + if (!directions[3]) + rectF.bottom += pixels; + + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setColor(0xff424242); // 设置画笔的颜色为不透明的灰色 + + Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(newBitmap); + canvas.drawARGB(0, 0, 0, 0); + canvas.drawRoundRect(rectF, pixels, pixels, paint); + // 绘制底圆后, 进行合并 ( 交集处理 ) + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + canvas.drawBitmap(bitmap, rect, rect, paint); + return newBitmap; + } + + // ======== + // = 圆形 = + // ======== + + /** + * 图片圆形处理 + * @param bitmap 待操作源图片 + * @return 圆形处理后的图片 + */ + public static Bitmap round(final Bitmap bitmap) { + return round(bitmap, 0, 0); + } + + /** + * 图片圆形处理 + * @param bitmap 待操作源图片 + * @param borderSize 边框尺寸 + * @param borderColor 边框颜色 + * @return 圆形处理后的图片 + */ + public static Bitmap round(final Bitmap bitmap, @IntRange(from = 0) final int borderSize, @ColorInt final int borderColor) { + if (isEmpty(bitmap)) return null; + + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + int size = Math.min(width, height); + + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + float center = size / 2f; + RectF rectF = new RectF(0, 0, width, height); + rectF.inset((width - size) / 2f, (height - size) / 2f); + + Matrix matrix = new Matrix(); + matrix.setTranslate(rectF.left, rectF.top); + if (width != height) { + matrix.preScale((float) size / width, (float) size / height); + } + BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + shader.setLocalMatrix(matrix); + paint.setShader(shader); + + Bitmap newBitmap = Bitmap.createBitmap(width, height, bitmap.getConfig()); + Canvas canvas = new Canvas(newBitmap); + canvas.drawRoundRect(rectF, center, center, paint); + + if (borderSize > 0) { + paint.setShader(null); + paint.setColor(borderColor); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(borderSize); + float radius = center - borderSize / 2f; + canvas.drawCircle(width / 2f, height / 2f, radius, paint); + } + return newBitmap; + } + + // ================== + // = 圆角、圆形边框 = + // ================== + + /** + * 添加圆角边框 + * @param bitmap 源图片 + * @param borderSize 边框尺寸 + * @param color 边框颜色 + * @param cornerRadius 圆角半径 + * @return 圆角边框图 + */ + public static Bitmap addCornerBorder(final Bitmap bitmap, @IntRange(from = 1) final int borderSize, @ColorInt final int color, + @FloatRange(from = 0) final float cornerRadius) { + return addBorder(bitmap, borderSize, color, false, cornerRadius); + } + + /** + * 添加圆形边框 + * @param bitmap 源图片 + * @param borderSize 边框尺寸 + * @param color 边框颜色 + * @return 圆形边框图 + */ + public static Bitmap addCircleBorder(final Bitmap bitmap, @IntRange(from = 1) final int borderSize, @ColorInt final int color) { + return addBorder(bitmap, borderSize, color, true, 0); + } + + /** + * 添加边框 + * @param bitmap 待操作源图片 + * @param borderSize 边框尺寸 + * @param color 边框颜色 + * @param isCircle 是否画圆 + * @param cornerRadius 圆角半径 + * @return 添加边框后的图片 + */ + private static Bitmap addBorder(final Bitmap bitmap, @IntRange(from = 1) final int borderSize, + @ColorInt final int color, final boolean isCircle, final float cornerRadius) { + if (isEmpty(bitmap)) return null; + + Bitmap newBitmap = bitmap.copy(bitmap.getConfig(), true); + int width = newBitmap.getWidth(); + int height = newBitmap.getHeight(); + + Canvas canvas = new Canvas(newBitmap); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(color); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(borderSize); + if (isCircle) { + float radius = Math.min(width, height) / 2f - borderSize / 2f; + canvas.drawCircle(width / 2f, height / 2f, radius, paint); + } else { + int halfBorderSize = borderSize >> 1; + RectF rectF = new RectF(halfBorderSize, halfBorderSize, width - halfBorderSize, height - halfBorderSize); + canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint); + } + return newBitmap; + } + + // ======== + // = 水印 = + // ======== + + /** + * 添加文字水印 + * @param bitmap 待操作源图片 + * @param content 水印文本 + * @param textSize 水印字体大小 pixel + * @param color 水印字体颜色 + * @param x 起始坐标 x + * @param y 起始坐标 y + * @return 添加文字水印后的图片 + */ + public static Bitmap addTextWatermark(final Bitmap bitmap, final String content, final float textSize, + @ColorInt final int color, final float x, final float y) { + if (isEmpty(bitmap) || content == null) return null; + + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(color); + paint.setTextSize(textSize); + Rect bounds = new Rect(); + paint.getTextBounds(content, 0, content.length(), bounds); + + Bitmap newBitmap = bitmap.copy(bitmap.getConfig(), true); + Canvas canvas = new Canvas(newBitmap); + canvas.drawText(content, x, y + textSize, paint); + return newBitmap; + } + + /** + * 添加图片水印 + * @param bitmap 待操作源图片 + * @param watermark 水印图片 + * @param x 起始坐标 x + * @param y 起始坐标 y + * @param alpha 透明度 + * @return 添加图片水印后的图片 + */ + public static Bitmap addImageWatermark(final Bitmap bitmap, final Bitmap watermark, final int x, final int y, + @IntRange(from = 0, to = 255) final int alpha) { + if (isEmpty(bitmap)) return null; + Bitmap newBitmap = bitmap.copy(bitmap.getConfig(), true); + if (!isEmpty(watermark)) { + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + Canvas canvas = new Canvas(newBitmap); + paint.setAlpha(alpha); + canvas.drawBitmap(watermark, x, y, paint); + } + return newBitmap; + } + + // ======== + // = 压缩 = + // ======== + + /** + * 按缩放宽高压缩 + * @param bitmap 待操作源图片 + * @param newWidth 新宽度 + * @param newHeight 新高度 + * @return 缩放宽高压缩后的图片 + */ + public static Bitmap compressByZoom(final Bitmap bitmap, final int newWidth, final int newHeight) { + return zoom(bitmap, newWidth, newHeight); + } + + /** + * 按缩放比例压缩 + * @param bitmap 待操作源图片 + * @param scaleX 横向缩放比例 ( 缩放宽度倍数 ) + * @param scaleY 纵向缩放比例 ( 缩放高度倍数 ) + * @return 缩放比例压缩后的图片 + */ + public static Bitmap compressByScale(final Bitmap bitmap, final float scaleX, final float scaleY) { + return scale(bitmap, scaleX, scaleY); + } + + // = + + /** + * 按质量压缩 + * @param bitmap 待操作源图片 + * @param quality 质量 + * @return 质量压缩过的图片 + */ + public static Bitmap compressByQuality(final Bitmap bitmap, @IntRange(from = 0, to = 100) final int quality) { + return compressByQuality(bitmap, Bitmap.CompressFormat.JPEG, quality, null); + } + + /** + * 按质量压缩 + * @param bitmap 待操作源图片 + * @param quality 质量 + * @param options {@link BitmapFactory.Options} + * @return 质量压缩过的图片 + */ + public static Bitmap compressByQuality(final Bitmap bitmap, @IntRange(from = 0, to = 100) final int quality, + final BitmapFactory.Options options) { + return compressByQuality(bitmap, Bitmap.CompressFormat.JPEG, quality, options); + } + + /** + * 按质量压缩 + * @param bitmap 待操作源图片 + * @param format 图片压缩格式 + * @param quality 质量 + * @param options {@link BitmapFactory.Options} + * @return 质量压缩过的图片 + */ + public static Bitmap compressByQuality(final Bitmap bitmap, final Bitmap.CompressFormat format, + @IntRange(from = 0, to = 100) final int quality, + final BitmapFactory.Options options) { + if (isEmpty(bitmap) || format == null) return null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(format, quality, baos); + byte[] data = baos.toByteArray(); + return BitmapFactory.decodeByteArray(data, 0, data.length, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "compressByQuality"); + } + return null; + } + + // = + + /** + * 按质量压缩 ( 图片大小 ) + * @param bitmap 待操作源图片 + * @param maxByteSize 允许最大值字节数 + * @return 质量压缩过的图片 + */ + public static Bitmap compressByByteSize(final Bitmap bitmap, final long maxByteSize) { + return compressByByteSize(bitmap, Bitmap.CompressFormat.JPEG, maxByteSize, null); + } + + /** + * 按质量压缩 ( 图片大小 ) + * @param bitmap 待操作源图片 + * @param format 图片压缩格式 + * @param maxByteSize 允许最大值字节数 + * @return 质量压缩过的图片 + */ + public static Bitmap compressByByteSize(final Bitmap bitmap, final Bitmap.CompressFormat format, final long maxByteSize) { + return compressByByteSize(bitmap, format, maxByteSize, null); + } + + /** + * 按质量压缩 ( 图片大小 ) + * @param bitmap 待操作源图片 + * @param format 图片压缩格式 + * @param maxByteSize 允许最大值字节数 + * @param options {@link BitmapFactory.Options} + * @return 质量压缩过的图片 + */ + public static Bitmap compressByByteSize(final Bitmap bitmap, final Bitmap.CompressFormat format, final long maxByteSize, final BitmapFactory.Options options) { + if (isEmpty(bitmap) || maxByteSize <= 0) return null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(format, 100, baos); + byte[] data; + if (baos.size() <= maxByteSize) { // 最好质量的不大于最大字节, 则返回最佳质量 + data = baos.toByteArray(); + } else { + baos.reset(); + bitmap.compress(format, 0, baos); + if (baos.size() >= maxByteSize) { // 最差质量不小于最大字节, 则返回最差质量 + data = baos.toByteArray(); + } else { // 二分法寻找最佳质量 + int start = 0; + int end = 100; + int mid = 0; + while (start < end) { + mid = (start + end) / 2; + baos.reset(); + bitmap.compress(format, mid, baos); + int len = baos.size(); + if (len == maxByteSize) { + break; + } else if (len > maxByteSize) { + end = mid - 1; + } else { + start = mid + 1; + } + } + if (end == mid - 1) { + baos.reset(); + bitmap.compress(format, start, baos); + } + data = baos.toByteArray(); + } + } + return BitmapFactory.decodeByteArray(data, 0, data.length, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "compressByByteSize"); + } + return null; + } + + // = + + /** + * 按采样大小压缩 + * @param bitmap 待操作源图片 + * @param sampleSize 采样率大小 + * @return 按采样率压缩后的图片 + */ + public static Bitmap compressBySampleSize(final Bitmap bitmap, final int sampleSize) { + return compressBySampleSize(bitmap, Bitmap.CompressFormat.JPEG, sampleSize); + } + + /** + * 按采样大小压缩 + * @param bitmap 待操作源图片 + * @param format 图片压缩格式 + * @param sampleSize 采样率大小 + * @return 按采样率压缩后的图片 + */ + public static Bitmap compressBySampleSize(final Bitmap bitmap, final Bitmap.CompressFormat format, final int sampleSize) { + if (isEmpty(bitmap) || format == null) return null; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = sampleSize; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(format, 100, baos); + byte[] data = baos.toByteArray(); + return BitmapFactory.decodeByteArray(data, 0, data.length, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "compressBySampleSize"); + } + return null; + } + + // = + + /** + * 按采样大小压缩 + * @param bitmap 待操作源图片 + * @param maxWidth 最大宽度 + * @param maxHeight 最大高度 + * @return 按采样率压缩后的图片 + */ + public static Bitmap compressBySampleSize(final Bitmap bitmap, final int maxWidth, final int maxHeight) { + return compressBySampleSize(bitmap, Bitmap.CompressFormat.JPEG, maxWidth, maxHeight); + } + + /** + * 按采样大小压缩 + * @param bitmap 待操作源图片 + * @param format 图片压缩格式 + * @param maxWidth 最大宽度 + * @param maxHeight 最大高度 + * @return 按采样率压缩后的图片 + */ + public static Bitmap compressBySampleSize(final Bitmap bitmap, final Bitmap.CompressFormat format, final int maxWidth, final int maxHeight) { + if (isEmpty(bitmap)) return null; + try { + // 获取宽高信息 + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(format, 100, baos); + + // 进行采样压缩 + byte[] data = baos.toByteArray(); + BitmapFactory.decodeByteArray(data, 0, data.length, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeByteArray(data, 0, data.length, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "compressBySampleSize"); + } + return null; + } + + // = + + /** + * 计算采样大小 + *
+     *     最大宽高只是阀值, 实际算出来的图片将小于等于这个值
+     * 
+ * @param options {@link BitmapFactory.Options} + * @param maxWidth 最大宽度 + * @param maxHeight 最大高度 + * @return 采样大小 + */ + public static int calculateInSampleSize(final BitmapFactory.Options options, final int maxWidth, final int maxHeight) { + if (options == null) return 0; + + int height = options.outHeight; + int width = options.outWidth; + int inSampleSize = 1; + while (height > maxHeight || width > maxWidth) { + height >>= 1; + width >>= 1; + inSampleSize <<= 1; + } + return inSampleSize; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = FileUtils = + // ============= + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 获取文件绝对路径 + * @param file 文件 + * @return 文件绝对路径 + */ + private static String getAbsolutePath(final File file) { + return file != null ? file.getAbsolutePath() : null; + } + + /** + * 检查是否存在某个文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + private static boolean isFileExists(final File file) { + return file != null && file.exists(); + } + + /** + * 检查是否存在某个文件 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + private static boolean isFileExists(final String filePath) { + return isFileExists(getFileByPath(filePath)); + } + + // ================= + // = ResourceUtils = + // ================= + + /** + * 获取 Resources + * @return {@link Resources} + */ + private static Resources getResources() { + try { + return DevUtils.getContext().getResources(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getResources"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/image/ImageConvertUtils.java b/lib/DevApp/src/main/java/dev/utils/app/image/ImageConvertUtils.java new file mode 100644 index 0000000000..156e8ed06e --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/image/ImageConvertUtils.java @@ -0,0 +1,187 @@ +package dev.utils.app.image; + +import android.graphics.Bitmap; + +import dev.utils.LogPrintUtils; + +/** + * detail: 图片格式转换工具类 + * @author Ttt + */ +public final class ImageConvertUtils { + + private ImageConvertUtils() { + } + + // 日志 TAG + private static final String TAG = ImageConvertUtils.class.getSimpleName(); + + // ================= + // = 转换 BMP 图片 = + // ================= + + /** + * detail: 转换 BMP 内部类 + * @author Ttt + */ + private static final class BMP { + + private BMP() { + } + + /** + * BMP 位图, 头结构 + * @param size 文件总大小 ( 字节数 ) + * @return BMP 头结构 byte[] + */ + private static byte[] addBMPImageHeader(final int size) { + byte[] buffer = new byte[14]; + // = 文件标识 BM = + buffer[0] = 0x42; + buffer[1] = 0x4D; + // = 位图文件大小 = + buffer[2] = (byte) (size >> 0); + buffer[3] = (byte) (size >> 8); + buffer[4] = (byte) (size >> 16); + buffer[5] = (byte) (size >> 24); + // = 位图文件保留字, 必须为 0 = + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + // = 位图数据开始之间的偏移量 = + buffer[10] = 0x36; + buffer[11] = 0x00; + buffer[12] = 0x00; + buffer[13] = 0x00; + return buffer; + } + + /** + * BMP 位图, 头信息 + * @param width 宽度 + * @param height 高度 + * @return BMP 头信息 byte[] + */ + private static byte[] addBMPImageInfosHeader(final int width, final int height) { + byte[] buffer = new byte[40]; + // = + buffer[0] = 0x28; + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + // = + buffer[4] = (byte) (width >> 0); + buffer[5] = (byte) (width >> 8); + buffer[6] = (byte) (width >> 16); + buffer[7] = (byte) (width >> 24); + // = + buffer[8] = (byte) (height >> 0); + buffer[9] = (byte) (height >> 8); + buffer[10] = (byte) (height >> 16); + buffer[11] = (byte) (height >> 24); + // = + buffer[12] = 0x01; + buffer[13] = 0x00; + // = + buffer[14] = 0x20; // 位数 0x20 - 32 位 + buffer[15] = 0x00; + // = + buffer[16] = 0x00; + buffer[17] = 0x00; + buffer[18] = 0x00; + buffer[19] = 0x00; + // = + buffer[20] = 0x00; + buffer[21] = 0x00; + buffer[22] = 0x00; + buffer[23] = 0x00; + // = + buffer[24] = (byte) 0xE0; + buffer[25] = 0x01; + buffer[26] = 0x00; + buffer[27] = 0x00; + // = + buffer[28] = 0x02; + buffer[29] = 0x03; + buffer[30] = 0x00; + buffer[31] = 0x00; + // = + buffer[32] = 0x00; + buffer[33] = 0x00; + buffer[34] = 0x00; + buffer[35] = 0x00; + // = + buffer[36] = 0x00; + buffer[37] = 0x00; + buffer[38] = 0x00; + buffer[39] = 0x00; + return buffer; + } + + /** + * 增加位图 ARGB 值 + * @param data 图片数据 + * @param width 宽度 + * @param height 高度 + * @return BMP ARGB byte[] + */ + private static byte[] addBMP_ARGB_8888(final int[] data, final int width, final int height) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + byte[] buffer = new byte[width * height * 4]; // A + R + G + B = 4 + int offset = 0; // 计算偏移量 + for (int i = len - 1; i >= 0; i -= width) { + // DIB 文件格式最后一行为第一行, 每行按从左到右顺序 + int end = i, start = i - width + 1; + for (int j = start; j <= end; j++) { + buffer[offset] = (byte) (data[j] >> 0); + buffer[offset + 1] = (byte) (data[j] >> 8); + buffer[offset + 2] = (byte) (data[j] >> 16); + buffer[offset + 3] = (byte) (data[j] >> 24); + offset += 4; + } + } + return buffer; + } + + /** + * 图片转换 BMP 格式 byte[] 数据 + * @param bitmap 待转换图片 + * @return BMP 格式 byte[] 数据 + */ + public static byte[] convertBMP(final Bitmap bitmap) { + if (bitmap == null) return null; + try { + int width = bitmap.getWidth(), height = bitmap.getHeight(); + int[] pixels = new int[width * height]; + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + + byte[] rgb = addBMP_ARGB_8888(pixels, width, height); + byte[] header = addBMPImageHeader(rgb.length); + byte[] infos = addBMPImageInfosHeader(width, height); + byte[] buffer = new byte[54 + rgb.length]; + + System.arraycopy(header, 0, buffer, 0, header.length); + System.arraycopy(infos, 0, buffer, 14, infos.length); + System.arraycopy(rgb, 0, buffer, 54, rgb.length); + return buffer; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "convertBMP"); + } + return null; + } + } + + // = + + /** + * 图片转换 BMP 格式 byte[] 数据 + * @param bitmap 待转换图片 + * @return BMP 格式 byte[] 数据 + */ + public static byte[] convertBMP(final Bitmap bitmap) { + return BMP.convertBMP(bitmap); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/image/ImageFilterUtils.java b/lib/DevApp/src/main/java/dev/utils/app/image/ImageFilterUtils.java new file mode 100644 index 0000000000..7e4f84ffd8 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/image/ImageFilterUtils.java @@ -0,0 +1,882 @@ +package dev.utils.app.image; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; +import android.os.Build; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.renderscript.ScriptIntrinsicBlur; +import android.support.annotation.IntRange; +import android.support.annotation.RequiresApi; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 图片 ( 滤镜、效果 ) 工具类 + * @author Ttt + */ +public final class ImageFilterUtils { + + private ImageFilterUtils() { + } + + // 日志 TAG + private static final String TAG = ImageFilterUtils.class.getSimpleName(); + + // ======== + // = 效果 = + // ======== + + // ======== + // = 模糊 = + // ======== + + /** + * 图片模糊处理 ( Android RenderScript 实现, 效率最高 ) + * @param bitmap 待模糊图片 + * @param radius 模糊度 (0-25) + * @return 模糊后的图片 + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public static Bitmap blur(final Bitmap bitmap, @IntRange(from = 0, to = 25) final int radius) { + if (bitmap == null) return null; + try { + // 创建 RenderScript 内核对象 + RenderScript rs = RenderScript.create(DevUtils.getContext()); + // 由于 RenderScript 并没有使用 VM 来分配内存, 所以需要使用 Allocation 类来创建和分配内存空间 + // 创建 Allocation 对象的时候其实内存是空的, 需要使用 copyTo() 将数据填充进去 + Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + Allocation output = Allocation.createTyped(rs, input.getType()); + // 创建一个模糊效果的 RenderScript 的工具对象, 第二个参数 Element 相当于一种像素处理的算法, 高斯模糊的话用这个就好 + ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + + // 设置 blur 对象的输入内存 + blur.setInput(input); + // 设置渲染的模糊程度, 25f 是最大模糊度 + blur.setRadius(radius); + // 将输出数据保存到输出内存中 + blur.forEach(output); + // 将数据填充到 bitmap 中 + output.copyTo(bitmap); + + // 销毁它们释放内存 + input.destroy(); + output.destroy(); + blur.destroy(); + rs.destroy(); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "blur"); + } + return null; + } + + /** + * 图片模糊处理 ( 毛玻璃化 FastBlur Java 实现 ) + *
+     *     模糊程度越低, 处理速度越快
+     * 
+ * @param bitmap 待模糊图片 + * @param radius 模糊度 + * @return 模糊后的图片 + */ + public static Bitmap fastBlur(final Bitmap bitmap, final int radius) { + if (bitmap == null) return null; + // 如果 Bitmap 不允许编辑, 则返回 null + if (!bitmap.isMutable()) return null; + + try { + int width = 0; + int height = 0; + int[] pix = new int[0]; + + width = bitmap.getWidth(); + height = bitmap.getHeight(); + + pix = new int[width * height]; + bitmap.getPixels(pix, 0, width, 0, 0, width, height); + + int wm = width - 1; + int hm = height - 1; + int wh = width * height; + int div = radius + radius + 1; + + int[] r = new int[wh]; + int[] g = new int[wh]; + int[] b = new int[wh]; + int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; + int[] vmin = new int[Math.max(width, height)]; + + int divsum = (div + 1) >> 1; + divsum *= divsum; + int[] dv = new int[256 * divsum]; + for (i = 0; i < 256 * divsum; i++) { + dv[i] = (i / divsum); + } + + yw = yi = 0; + + int[][] stack = new int[div][3]; + int stackpointer; + int stackstart; + int[] sir; + int rbs; + int r1 = radius + 1; + int routsum, goutsum, boutsum; + int rinsum, ginsum, binsum; + + for (y = 0; y < height; y++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + for (i = -radius; i <= radius; i++) { + p = pix[yi + Math.min(wm, Math.max(i, 0))]; + sir = stack[i + radius]; + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + rbs = r1 - Math.abs(i); + rsum += sir[0] * rbs; + gsum += sir[1] * rbs; + bsum += sir[2] * rbs; + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + } + stackpointer = radius; + + for (x = 0; x < width; x++) { + + r[yi] = dv[rsum]; + g[yi] = dv[gsum]; + b[yi] = dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (y == 0) { + vmin[x] = Math.min(x + radius + 1, wm); + } + p = pix[yw + vmin[x]]; + + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[(stackpointer) % div]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi++; + } + yw += width; + } + for (x = 0; x < width; x++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + yp = -radius * width; + for (i = -radius; i <= radius; i++) { + yi = Math.max(0, yp) + x; + + sir = stack[i + radius]; + + sir[0] = r[yi]; + sir[1] = g[yi]; + sir[2] = b[yi]; + + rbs = r1 - Math.abs(i); + + rsum += r[yi] * rbs; + gsum += g[yi] * rbs; + bsum += b[yi] * rbs; + + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + + if (i < hm) { + yp += width; + } + } + yi = x; + stackpointer = radius; + for (y = 0; y < height; y++) { + // Preserve alpha channel: ( 0xff000000 & pix[yi] ) + pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (x == 0) { + vmin[y] = Math.min(y + r1, hm) * width; + } + p = x + vmin[y]; + + sir[0] = r[p]; + sir[1] = g[p]; + sir[2] = b[p]; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi += width; + } + } + bitmap.setPixels(pix, 0, width, 0, 0, width, height); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "fastBlur"); + } + return null; + } + + // ============ + // = 滤镜效果 = + // ============ + + /** + * 怀旧效果处理 + * @param bitmap 待操作源图片 + * @return 怀旧效果处理后的图片 + */ + public static Bitmap nostalgic(final Bitmap bitmap) { + if (bitmap == null) return null; + try { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + int pixColor = 0; + int pixR = 0; + int pixG = 0; + int pixB = 0; + int newR = 0; + int newG = 0; + int newB = 0; + int[] pixels = new int[width * height]; + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + for (int i = 0; i < height; i++) { + for (int k = 0; k < width; k++) { + pixColor = pixels[width * i + k]; + pixR = Color.red(pixColor); + pixG = Color.green(pixColor); + pixB = Color.blue(pixColor); + newR = (int) (0.393 * pixR + 0.769 * pixG + 0.189 * pixB); + newG = (int) (0.349 * pixR + 0.686 * pixG + 0.168 * pixB); + newB = (int) (0.272 * pixR + 0.534 * pixG + 0.131 * pixB); + int newColor = Color.argb(255, newR > 255 ? 255 : newR, + newG > 255 ? 255 : newG, newB > 255 ? 255 : newB); + pixels[width * i + k] = newColor; + } + } + + Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "nostalgic"); + } + return null; + } + + /** + * 光照效果处理 + * @param bitmap 待操作源图片 + * @param centerX 光源在 X 轴的位置 + * @param centerY 光源在 Y 轴的位置 + * @return 光照效果处理后的图片 + */ + public static Bitmap sunshine(final Bitmap bitmap, final int centerX, final int centerY) { + if (bitmap == null) return null; + try { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + int pixR = 0; + int pixG = 0; + int pixB = 0; + + int pixColor = 0; + + int newR = 0; + int newG = 0; + int newB = 0; + int radius = Math.min(centerX, centerY); + + final float strength = 150F; // 光照强度 100~150 + int[] pixels = new int[width * height]; + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + int pos = 0; + for (int i = 1, length = height - 1; i < length; i++) { + for (int k = 1, len = width - 1; k < len; k++) { + pos = i * width + k; + pixColor = pixels[pos]; + + pixR = Color.red(pixColor); + pixG = Color.green(pixColor); + pixB = Color.blue(pixColor); + + newR = pixR; + newG = pixG; + newB = pixB; + + // 计算当前点到光照中心的距离, 平面座标系中求两点之间的距离 + int distance = (int) (Math.pow((centerY - i), 2) + Math.pow(centerX - k, 2)); + if (distance < radius * radius) { + // 按照距离大小计算增加的光照值 + int result = (int) (strength * (1.0 - Math.sqrt(distance) / radius)); + newR = pixR + result; + newG = pixG + result; + newB = pixB + result; + } + + newR = Math.min(255, Math.max(0, newR)); + newG = Math.min(255, Math.max(0, newG)); + newB = Math.min(255, Math.max(0, newB)); + + pixels[pos] = Color.argb(255, newR, newG, newB); + } + } + + Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "sunshine"); + } + return null; + } + + /** + * 底片效果处理 + * @param bitmap 待操作源图片 + * @return 底片效果处理后的图片 + */ + public static Bitmap film(final Bitmap bitmap) { + if (bitmap == null) return null; + try { + // ARGB 的最大值 + final int MAX_VALUE = 255; + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + int pixR = 0; + int pixG = 0; + int pixB = 0; + + int pixColor = 0; + + int newR = 0; + int newG = 0; + int newB = 0; + + int[] pixels = new int[width * height]; + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + int pos = 0; + for (int i = 1, length = height - 1; i < length; i++) { + for (int k = 1, len = width - 1; k < len; k++) { + pos = i * width + k; + pixColor = pixels[pos]; + + pixR = Color.red(pixColor); + pixG = Color.green(pixColor); + pixB = Color.blue(pixColor); + + newR = MAX_VALUE - pixR; + newG = MAX_VALUE - pixG; + newB = MAX_VALUE - pixB; + + newR = Math.min(MAX_VALUE, Math.max(0, newR)); + newG = Math.min(MAX_VALUE, Math.max(0, newG)); + newB = Math.min(MAX_VALUE, Math.max(0, newB)); + + pixels[pos] = Color.argb(MAX_VALUE, newR, newG, newB); + } + } + + Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "film"); + } + return null; + } + + /** + * 柔化效果处理 + *
+     *     delta 取值范围只要大于等于 1 就可以, 但是避免太大, 导致变得很暗, 限制 1-24
+     * 
+ * @param bitmap 待操作源图片 + * @param delta 图片的亮暗程度值, 越小图片会越亮 + * @return 柔化效果处理后的图片 + */ + public static Bitmap soften(final Bitmap bitmap, @IntRange(from = 1, to = 24) final int delta) { + if (bitmap == null) return null; + if (delta > 24 || delta <= 0) return null; + try { + // 高斯矩阵 + int[] gauss = new int[]{1, 2, 1, 2, 4, 2, 1, 2, 1}; + + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + int pixR = 0; + int pixG = 0; + int pixB = 0; + + int pixColor = 0; + + int newR = 0; + int newG = 0; + int newB = 0; + + int idx = 0; + int[] pixels = new int[width * height]; + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + for (int i = 1, length = height - 1; i < length; i++) { + for (int k = 1, len = width - 1; k < len; k++) { + idx = 0; + for (int m = -1; m <= 1; m++) { + for (int n = -1; n <= 1; n++) { + pixColor = pixels[(i + m) * width + k + n]; + pixR = Color.red(pixColor); + pixG = Color.green(pixColor); + pixB = Color.blue(pixColor); + + newR += (pixR * gauss[idx]); + newG += (pixG * gauss[idx]); + newB += (pixB * gauss[idx]); + idx++; + } + } + newR /= delta; + newG /= delta; + newB /= delta; + + newR = Math.min(255, Math.max(0, newR)); + newG = Math.min(255, Math.max(0, newG)); + newB = Math.min(255, Math.max(0, newB)); + + pixels[i * width + k] = Color.argb(255, newR, newG, newB); + + newR = 0; + newG = 0; + newB = 0; + } + } + + Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "soften"); + } + return null; + } + + /** + * 锐化效果处理 + * @param bitmap 待操作源图片 + * @return 锐化效果处理后的图片 + */ + public static Bitmap sharpen(final Bitmap bitmap) { + if (bitmap == null) return null; + try { + // 拉普拉斯矩阵 + int[] laplacian = new int[]{-1, -1, -1, -1, 9, -1, -1, -1, -1}; + + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + int pixR = 0; + int pixG = 0; + int pixB = 0; + + int pixColor = 0; + + int newR = 0; + int newG = 0; + int newB = 0; + + int idx = 0; + float alpha = 0.3F; + int[] pixels = new int[width * height]; + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + for (int i = 1, length = height - 1; i < length; i++) { + for (int k = 1, len = width - 1; k < len; k++) { + idx = 0; + for (int m = -1; m <= 1; m++) { + for (int n = -1; n <= 1; n++) { + pixColor = pixels[(i + n) * width + k + m]; + pixR = Color.red(pixColor); + pixG = Color.green(pixColor); + pixB = Color.blue(pixColor); + + newR = newR + (int) (pixR * laplacian[idx] * alpha); + newG = newG + (int) (pixG * laplacian[idx] * alpha); + newB = newB + (int) (pixB * laplacian[idx] * alpha); + idx++; + } + } + + newR = Math.min(255, Math.max(0, newR)); + newG = Math.min(255, Math.max(0, newG)); + newB = Math.min(255, Math.max(0, newB)); + + pixels[i * width + k] = Color.argb(255, newR, newG, newB); + newR = 0; + newG = 0; + newB = 0; + } + } + + Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "sharpen"); + } + return null; + } + + /** + * 浮雕效果处理 + * @param bitmap 待操作源图片 + * @return 浮雕效果处理后的图片 + */ + public static Bitmap emboss(final Bitmap bitmap) { + if (bitmap == null) return null; + try { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + int pixR = 0; + int pixG = 0; + int pixB = 0; + + int pixColor = 0; + + int newR = 0; + int newG = 0; + int newB = 0; + + int[] pixels = new int[width * height]; + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + int pos = 0; + for (int i = 1, length = height - 1; i < length; i++) { + for (int k = 1, len = width - 1; k < len; k++) { + pos = i * width + k; + pixColor = pixels[pos]; + + pixR = Color.red(pixColor); + pixG = Color.green(pixColor); + pixB = Color.blue(pixColor); + + pixColor = pixels[pos + 1]; + newR = Color.red(pixColor) - pixR + 127; + newG = Color.green(pixColor) - pixG + 127; + newB = Color.blue(pixColor) - pixB + 127; + + newR = Math.min(255, Math.max(0, newR)); + newG = Math.min(255, Math.max(0, newG)); + newB = Math.min(255, Math.max(0, newB)); + + pixels[pos] = Color.argb(255, newR, newG, newB); + } + } + + Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "emboss"); + } + return null; + } + +// /** +// * 转为灰度图片 +// * @param bitmap 待操作源图片 +// * @return 灰度图 +// */ +// public static Bitmap toGray(final Bitmap bitmap) { +// if (bitmap == null) return null; +// try { +// int width = bitmap.getWidth(); // 获取位图的宽 +// int height = bitmap.getHeight(); // 获取位图的高 +// int[] pixels = new int[width * height]; // 通过位图的大小创建像素点数组 +// bitmap.getPixels(pixels, 0, width, 0, 0, width, height); +// +// int alpha = 0xFF << 24; // 默认将 bitmap 当成 24 色图片 +// for (int i = 0; i < height; i++) { +// for (int j = 0; j < width; j++) { +// int grey = pixels[width * i + j]; +// +// int red = ((grey & 0x00FF0000) >> 16); +// int green = ((grey & 0x0000FF00) >> 8); +// int blue = (grey & 0x000000FF); +// +// grey = (int) ((float) red * 0.3 + (float) green * 0.59 + (float) blue * 0.11); +// grey = alpha | (grey << 16) | (grey << 8) | grey; +// pixels[width * i + j] = grey; +// } +// } +// Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); +// newBitmap.setPixels(pixels, 0, width, 0, 0, width, height); +// return newBitmap; +// } catch (Exception e) { +// LogPrintUtils.eTag(TAG, e, "toGray"); +// } +// return null; +// } + + /** + * 转为灰度图片 + * @param bitmap 待操作源图片 + * @return 灰度图 + */ + public static Bitmap toGray(final Bitmap bitmap) { + if (bitmap == null) return null; + try { + Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig()); + Canvas canvas = new Canvas(newBitmap); + Paint paint = new Paint(); + ColorMatrix colorMatrix = new ColorMatrix(); + colorMatrix.setSaturation(0); + ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix); + paint.setColorFilter(colorMatrixColorFilter); + canvas.drawBitmap(bitmap, 0, 0, paint); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "toGray"); + } + return null; + } + + // ======== + // = 色彩 = + // ======== + + /** + * 饱和度处理 + * @param bitmap 待操作源图片 + * @param saturationValue 新的饱和度值 + * @return 改变了饱和度值之后的图片 + */ + public static Bitmap saturation(final Bitmap bitmap, final int saturationValue) { + if (bitmap == null) return null; + try { + // 计算出符合要求的饱和度值 + float newSaturationValue = saturationValue * 1.0F / 127; + // 创建一个颜色矩阵 + ColorMatrix saturationColorMatrix = new ColorMatrix(); + // 设置饱和度值 + saturationColorMatrix.setSaturation(newSaturationValue); + // 创建一个画笔并设置其颜色过滤器 + Paint paint = new Paint(); + paint.setColorFilter(new ColorMatrixColorFilter(saturationColorMatrix)); + // 创建一个新的图片并创建画布 + Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(newBitmap); + // 将源图片使用给定的画笔画到画布上 + canvas.drawBitmap(bitmap, 0, 0, paint); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "saturation"); + } + return null; + } + + /** + * 亮度处理 + * @param bitmap 待操作源图片 + * @param lumValue 新的亮度值 + * @return 改变了亮度值之后的图片 + */ + public static Bitmap lum(final Bitmap bitmap, final int lumValue) { + if (bitmap == null) return null; + try { + // 计算出符合要求的亮度值 + float newlumValue = lumValue * 1.0F / 127; + // 创建一个颜色矩阵 + ColorMatrix lumColorMatrix = new ColorMatrix(); + // 设置亮度值 + lumColorMatrix.setScale(newlumValue, newlumValue, newlumValue, 1); + // 创建一个画笔并设置其颜色过滤器 + Paint paint = new Paint(); + paint.setColorFilter(new ColorMatrixColorFilter(lumColorMatrix)); + // 创建一个新的图片并创建画布 + Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(newBitmap); + // 将源图片使用给定的画笔画到画布上 + canvas.drawBitmap(bitmap, 0, 0, paint); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "lum"); + } + return null; + } + + /** + * 色相处理 + * @param bitmap 待操作源图片 + * @param hueValue 新的色相值 + * @return 改变了色相值之后的图片 + */ + public static Bitmap hue(final Bitmap bitmap, final int hueValue) { + if (bitmap == null) return null; + try { + // 计算出符合要求的色相值 + float newHueValue = (hueValue - 127) * 1.0F / 127 * 180; + // 创建一个颜色矩阵 + ColorMatrix hueColorMatrix = new ColorMatrix(); + // 控制让红色区在色轮上旋转的角度 + hueColorMatrix.setRotate(0, newHueValue); + // 控制让绿红色区在色轮上旋转的角度 + hueColorMatrix.setRotate(1, newHueValue); + // 控制让蓝色区在色轮上旋转的角度 + hueColorMatrix.setRotate(2, newHueValue); + // 创建一个画笔并设置其颜色过滤器 + Paint paint = new Paint(); + paint.setColorFilter(new ColorMatrixColorFilter(hueColorMatrix)); + // 创建一个新的图片并创建画布 + Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(newBitmap); + // 将源图片使用给定的画笔画到画布上 + canvas.drawBitmap(bitmap, 0, 0, paint); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "hue"); + } + return null; + } + + /** + * 亮度、色相、饱和度处理 + * @param bitmap 待操作源图片 + * @param lumValue 亮度值 + * @param hueValue 色相值 + * @param saturationValue 饱和度值 + * @return 亮度、色相、饱和度处理后的图片 + */ + public static Bitmap lumHueSaturation(final Bitmap bitmap, final int lumValue, final int hueValue, final int saturationValue) { + if (bitmap == null) return null; + try { + // 计算出符合要求的饱和度值 + float newSaturationValue = saturationValue * 1.0F / 127; + // 计算出符合要求的亮度值 + float newlumValue = lumValue * 1.0F / 127; + // 计算出符合要求的色相值 + float newHueValue = (hueValue - 127) * 1.0F / 127 * 180; + // 创建一个颜色矩阵并设置其饱和度 + ColorMatrix colorMatrix = new ColorMatrix(); + // 设置饱和度值 + colorMatrix.setSaturation(newSaturationValue); + // 设置亮度值 + colorMatrix.setScale(newlumValue, newlumValue, newlumValue, 1); + // 控制让红色区在色轮上旋转的角度 + colorMatrix.setRotate(0, newHueValue); + // 控制让绿红色区在色轮上旋转的角度 + colorMatrix.setRotate(1, newHueValue); + // 控制让蓝色区在色轮上旋转的角度 + colorMatrix.setRotate(2, newHueValue); + // 创建一个画笔并设置其颜色过滤器 + Paint paint = new Paint(); + paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); + // 创建一个新的图片并创建画布 + Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(newBitmap); + // 将源图片使用给定的画笔画到画布上 + canvas.drawBitmap(bitmap, 0, 0, paint); + return newBitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "lumHueSaturation"); + } + return null; + } + + // = + + /** + * 将 YUV 格式的图片的源数据从横屏模式转为竖屏模式 + *
+     *     注: 将源图片的宽高互换一下就是新图片的宽高
+     * 
+ * @param sourceData YUV 格式的图片的源数据 + * @param width 宽 + * @param height 高 + * @return byte[] + */ + public static byte[] yuvLandscapeToPortrait(final byte[] sourceData, final int width, final int height) { + if (sourceData == null || sourceData.length == 0) return null; + byte[] rotatedData = new byte[sourceData.length]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + rotatedData[x * height + height - y - 1] = sourceData[x + y * width]; + } + return rotatedData; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/image/ImageUtils.java b/lib/DevApp/src/main/java/dev/utils/app/image/ImageUtils.java new file mode 100644 index 0000000000..50f571d416 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/image/ImageUtils.java @@ -0,0 +1,1270 @@ +package dev.utils.app.image; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.NinePatchDrawable; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.IntRange; +import android.view.View; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.app.ResourceUtils; + +/** + * detail: Image ( Bitmap、Drawable 等 ) 工具类 + * @author Ttt + *
+ *     图片文件头标识信息
+ *     @see 
+ *     @see 
+ *     各类文件的文件头标志
+ *     @see 
+ *     @see 
+ *     @see 
+ * 
+ */ +public final class ImageUtils { + + private ImageUtils() { + } + + // 日志 TAG + private static final String TAG = ImageUtils.class.getSimpleName(); + + /** + * 判断 Bitmap 对象是否为 null + * @param bitmap {@link Bitmap} + * @return {@code true} yes, {@code false} no + */ + public static boolean isEmpty(final Bitmap bitmap) { + return bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0; + } + + /** + * 判断 Bitmap 对象是否不为 null + * @param bitmap {@link Bitmap} + * @return {@code true} yes, {@code false} no + */ + public static boolean isNotEmpty(final Bitmap bitmap) { + return bitmap != null && bitmap.getWidth() != 0 && bitmap.getHeight() != 0; + } + + // = + + /** + * 判断 Drawable 对象是否为 null + * @param drawable {@link Drawable} + * @return {@code true} yes, {@code false} no + */ + public static boolean isEmpty(final Drawable drawable) { + return drawable == null; + } + + /** + * 判断 Drawable 对象是否不为 null + * @param drawable {@link Drawable} + * @return {@code true} yes, {@code false} no + */ + public static boolean isNotEmpty(final Drawable drawable) { + return drawable != null; + } + + // ================ + // = 图片类型判断 = + // ================ + + // 图片格式 + private static final String[] IMAGE_FORMATS = new String[]{".PNG", ".JPG", ".JPEG", ".BMP", ".GIF", ".WEBP"}; + + /** + * 根据文件名判断文件是否为图片 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + public static boolean isImageFormats(final File file) { + return file != null && isImageFormats(file.getPath(), IMAGE_FORMATS); + } + + /** + * 根据文件名判断文件是否为图片 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + public static boolean isImageFormats(final String filePath) { + return isImageFormats(filePath, IMAGE_FORMATS); + } + + /** + * 根据文件名判断文件是否为图片 + * @param filePath 文件路径 + * @param imageFormats 图片格式 + * @return {@code true} yes, {@code false} no + */ + public static boolean isImageFormats(final String filePath, final String[] imageFormats) { + if (filePath == null || imageFormats == null || imageFormats.length == 0) return false; + String path = filePath.toUpperCase(); + for (String format : imageFormats) { + if (format != null) { + if (path.endsWith(format.toUpperCase())) { + return true; + } + } + } + return false; + } + + // = + + /** + * detail: 图片类型 + * @author Ttt + */ + public enum ImageType { + + TYPE_PNG("png"), + + TYPE_JPG("jpg"), + + TYPE_BMP("bmp"), + + TYPE_GIF("gif"), + + TYPE_WEBP("webp"), + + TYPE_ICO("ico"), + + TYPE_TIFF("tiff"), + + TYPE_UNKNOWN("unknown"); + + private String value; + + ImageType(String value) { + this.value = value; + } + + /** + * 获取图片类型字符串 + * @return 图片类型字符串 + */ + public String getValue() { + return value; + } + } + + /** + * 获取图片类型 + * @param filePath 文件路径 + * @return {@link ImageType} 图片类型 + */ + public static ImageType getImageType(final String filePath) { + return getImageType(getFileByPath(filePath)); + } + + /** + * 获取图片类型 + * @param file 文件 + * @return {@link ImageType} 图片类型 + */ + public static ImageType getImageType(final File file) { + if (file == null) return ImageType.TYPE_UNKNOWN; + InputStream is = null; + try { + is = new FileInputStream(file); + return getImageType(is); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getImageType"); + return ImageType.TYPE_UNKNOWN; + } finally { + closeIOQuietly(is); + } + } + + /** + * 获取图片类型 + * @param inputStream 输入流 + * @return {@link ImageType} 图片类型 + */ + public static ImageType getImageType(final InputStream inputStream) { + if (inputStream == null) return ImageType.TYPE_UNKNOWN; + try { + byte[] bytes = new byte[12]; + return inputStream.read(bytes, 0, 12) != -1 ? getImageType(bytes) : null; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getImageType"); + return ImageType.TYPE_UNKNOWN; + } + } + + /** + * 获取图片类型 + * @param data 图片 byte[] + * @return {@link ImageType} 图片类型 + */ + public static ImageType getImageType(final byte[] data) { + if (isPNG(data)) return ImageType.TYPE_PNG; + if (isJPEG(data)) return ImageType.TYPE_JPG; + if (isBMP(data)) return ImageType.TYPE_BMP; + if (isGif(data)) return ImageType.TYPE_GIF; + if (isWEBP(data)) return ImageType.TYPE_WEBP; + if (isICO(data)) return ImageType.TYPE_ICO; + if (isTIFF(data)) return ImageType.TYPE_TIFF; + return ImageType.TYPE_UNKNOWN; + } + + // = + + /** + * 判断是否 PNG 图片 + * @param data 图片 byte[] + * @return {@code true} yes, {@code false} no + */ + public static boolean isPNG(final byte[] data) { + return data != null && data.length >= 8 + && (data[0] == (byte) 137 && data[1] == (byte) 80 + && data[2] == (byte) 78 && data[3] == (byte) 71 + && data[4] == (byte) 13 && data[5] == (byte) 10 + && data[6] == (byte) 26 && data[7] == (byte) 10); + } + + /** + * 判断是否 JPG 图片 + * @param data 图片 byte[] + * @return {@code true} yes, {@code false} no + */ + public static boolean isJPEG(final byte[] data) { + return data != null && data.length >= 2 && (data[0] == (byte) 0xFF) && (data[1] == (byte) 0xD8); + } + + /** + * 判断是否 BMP 图片 + * @param data 图片 byte[] + * @return {@code true} yes, {@code false} no + */ + public static boolean isBMP(final byte[] data) { + return data != null && data.length >= 2 && (data[0] == (byte) 0x42) && (data[1] == (byte) 0x4d); + } + + /** + * 判断是否 GIF 图片 + * @param data 图片 byte[] + * @return {@code true} yes, {@code false} no + */ + public static boolean isGif(final byte[] data) { + return data != null && data.length >= 6 && data[0] == 'G' && data[1] == 'I' && data[2] == 'F' + && data[3] == '8' && (data[4] == '7' || data[4] == '9') && data[5] == 'a'; + } + + /** + * 判断是否 WEBP 图片 + * @param data 图片 byte[] + * @return {@code true} yes, {@code false} no + */ + public static boolean isWEBP(final byte[] data) { + return data != null && data.length >= 12 && data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F' + && data[8] == 'W' && (data[9] == 'E' || data[10] == 'B') && data[11] == 'P'; + } + + /** + * 判断是否 ICO 图片 + * @param data 图片 byte[] + * @return {@code true} yes, {@code false} no + */ + public static boolean isICO(final byte[] data) { + return data != null && data.length >= 4 && data[0] == 0 && data[1] == 0 && data[2] == 1 && data[3] == 0; + } + + /** + * 判断是否 TIFF 图片 + * @param data 图片 byte[] + * @return {@code true} yes, {@code false} no + */ + public static boolean isTIFF(final byte[] data) { + if (data != null && data.length >= 4){ + if (data[0] == (byte) 73 && data[1] == (byte) 73 && data[2] == (byte) 0x2a && data[3] == 0){ + return true; // 49 49 2a 00 + } else if (data[0] == (byte) 0x4d && data[1] == (byte) 0x4d && data[2] == 0 && data[3] == (byte) 0x2a) { + return true; // 4d 4d 00 2a + } else if (data[0] == (byte) 0x4d && data[1] == (byte) 0x4d && data[2] == 0 && data[3] == (byte) 0x2b) { + return true; // 4d 4d 00 2b + } else if (data[0] == (byte) 73 && data[1] == (byte) 32 && data[2] == (byte) 73){ + return true; // 49 20 49 + } + } + return false; + } + + // ============ + // = 本地获取 = + // ============ + + /** + * 获取 Bitmap + * @param file 文件 + * @return {@link Bitmap} + */ + public static Bitmap decodeFile(final File file) { + return decodeFile(getAbsolutePath(file), null); + } + + /** + * 获取 Bitmap + * @param file 文件 + * @param options {@link BitmapFactory.Options} + * @return {@link Bitmap} + */ + public static Bitmap decodeFile(final File file, final BitmapFactory.Options options) { + return decodeFile(getAbsolutePath(file), options); + } + + /** + * 获取 Bitmap + * @param filePath 文件路径 + * @return {@link Bitmap} + */ + public static Bitmap decodeFile(final String filePath) { + return decodeFile(filePath, null); + } + + /** + * 获取 Bitmap + * @param filePath 文件路径 + * @param options {@link BitmapFactory.Options} + * @return {@link Bitmap} + */ + public static Bitmap decodeFile(final String filePath, final BitmapFactory.Options options) { + if (filePath == null) return null; + try { + return BitmapFactory.decodeFile(filePath, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "decodeFile"); + return null; + } + } + + // = + + /** + * 获取 Bitmap + * @param resId resource identifier + * @return {@link Bitmap} + */ + public static Bitmap decodeResource(@DrawableRes final int resId) { + return decodeResource(resId, null); + } + + /** + * 获取 Bitmap + * @param resId resource identifier + * @param options {@link BitmapFactory.Options} + * @return {@link Bitmap} + */ + public static Bitmap decodeResource(@DrawableRes final int resId, final BitmapFactory.Options options) { + try { + return BitmapFactory.decodeResource(getResources(), resId, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "decodeResource"); + return null; + } + } + + // = + + /** + * 获取 Bitmap + * @param inputStream {@link InputStream} + * @return {@link Bitmap} + */ + public static Bitmap decodeStream(final InputStream inputStream) { + return decodeStream(inputStream, null); + } + + /** + * 获取 Bitmap + * @param inputStream {@link InputStream} + * @param options {@link BitmapFactory.Options} + * @return {@link Bitmap} + */ + public static Bitmap decodeStream(final InputStream inputStream, final BitmapFactory.Options options) { + if (inputStream == null) return null; + try { + return BitmapFactory.decodeStream(inputStream, null, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "decodeStream"); + return null; + } + } + + // = + + /** + * 获取 Bitmap + * @param fd 文件描述 + * @return {@link Bitmap} + */ + public static Bitmap decodeFileDescriptor(final FileDescriptor fd) { + return decodeFileDescriptor(fd, null); + } + + /** + * 获取 Bitmap + * @param fd 文件描述 + * @param options {@link BitmapFactory.Options} + * @return {@link Bitmap} + */ + public static Bitmap decodeFileDescriptor(final FileDescriptor fd, final BitmapFactory.Options options) { + if (fd == null) return null; + try { + return BitmapFactory.decodeFileDescriptor(fd, null, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "decodeFileDescriptor"); + return null; + } + } + + // = + + /** + * 获取 Bitmap + * @param data byte[] + * @return {@link Bitmap} + */ + public static Bitmap decodeByteArray(final byte[] data) { + return decodeByteArray(data, 0, (data == null) ? 0 : data.length, null); + } + + /** + * 获取 Bitmap + * @param data byte[] + * @param options {@link BitmapFactory.Options} + * @return {@link Bitmap} + */ + public static Bitmap decodeByteArray(final byte[] data, final BitmapFactory.Options options) { + return decodeByteArray(data, 0, (data == null) ? 0 : data.length, options); + } + + /** + * 获取 Bitmap + * @param data byte[] + * @param offset 偏移量 + * @param length 所需长度 + * @return {@link Bitmap} + */ + public static Bitmap decodeByteArray(final byte[] data, final int offset, final int length) { + return decodeByteArray(data, offset, length, null); + } + + /** + * 获取 Bitmap + * @param data byte[] + * @param offset 偏移量 + * @param length 所需长度 + * @param options {@link BitmapFactory.Options} + * @return {@link Bitmap} + */ + public static Bitmap decodeByteArray(final byte[] data, final int offset, final int length, final BitmapFactory.Options options) { + if (data == null) return null; + if ((offset | length) < 0 || data.length < offset + length) return null; + try { + return BitmapFactory.decodeByteArray(data, offset, length, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "decodeByteArray"); + return null; + } + } + + // ============ + // = 本地保存 = + // ============ + + /** + * 保存图片到 SDCard - JPEG + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardJPEG(final Bitmap bitmap, final String filePath) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.JPEG, 100); + } + + /** + * 保存图片到 SDCard - JPEG + * @param bitmap 待保存图片 + * @param file 保存路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardJPEG(final Bitmap bitmap, final File file) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.JPEG, 100); + } + + // = + + /** + * 保存图片到 SDCard - JPEG + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @param quality 质量 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardJPEG(final Bitmap bitmap, final String filePath, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.JPEG, quality); + } + + /** + * 保存图片到 SDCard - JPEG + * @param bitmap 待保存图片 + * @param file 保存路径 + * @param quality 质量 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardJPEG(final Bitmap bitmap, final File file, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.JPEG, quality); + } + + // = + + /** + * 保存图片到 SDCard - PNG + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardPNG(final Bitmap bitmap, final String filePath) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.PNG, 100); + } + + /** + * 保存图片到 SDCard - PNG + * @param bitmap 待保存图片 + * @param file 保存路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardPNG(final Bitmap bitmap, final File file) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.PNG, 100); + } + + // = + + /** + * 保存图片到 SDCard - PNG + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @param quality 质量 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardPNG(final Bitmap bitmap, final String filePath, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.PNG, quality); + } + + /** + * 保存图片到 SDCard - PNG + * @param bitmap 待保存图片 + * @param file 保存路径 + * @param quality 质量 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardPNG(final Bitmap bitmap, final File file, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.PNG, quality); + } + + // = + + /** + * 保存图片到 SDCard - WEBP + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardWEBP(final Bitmap bitmap, final String filePath) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.WEBP, 100); + } + + /** + * 保存图片到 SDCard - WEBP + * @param bitmap 待保存图片 + * @param file 保存路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardWEBP(final Bitmap bitmap, final File file) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.WEBP, 100); + } + + // = + + /** + * 保存图片到 SDCard - WEBP + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @param quality 质量 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardWEBP(final Bitmap bitmap, final String filePath, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.WEBP, quality); + } + + /** + * 保存图片到 SDCard - WEBP + * @param bitmap 待保存图片 + * @param file 保存路径 + * @param quality 质量 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCardWEBP(final Bitmap bitmap, final File file, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.WEBP, quality); + } + + // = + + /** + * 保存图片到 SDCard + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @param format 如 Bitmap.CompressFormat.PNG + * @param quality 质量 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCard(final Bitmap bitmap, final String filePath, final Bitmap.CompressFormat format, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, getFileByPath(filePath), format, quality); + } + + /** + * 保存图片到 SDCard + * @param bitmap 待保存图片 + * @param file 保存路径 + * @param format 如 Bitmap.CompressFormat.PNG + * @param quality 质量 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveBitmapToSDCard(final Bitmap bitmap, final File file, final Bitmap.CompressFormat format, + @IntRange(from = 0, to = 100) final int quality) { + if (bitmap == null || file == null || format == null) return false; + // 防止 Bitmap 为 null, 或者创建文件夹失败 ( 文件存在则删除 ) + if (isEmpty(bitmap) || !createFileByDeleteOldFile(file)) return false; + OutputStream os = null; + try { + os = new BufferedOutputStream(new FileOutputStream(file)); + if (os != null) { + bitmap.compress(format, quality, os); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "saveBitmapToSDCard"); + return false; + } finally { + closeIOQuietly(os); + } + return true; + } + + // ============ + // = Drawable = + // ============ + + /** + * 获取 .9 Drawable + *
+     *     .9 图片如果需要着色, 需要传入 NinePatchDrawable
+     *     setColorFilter(get9PatchDrawable(drawable), color);
+     * 
+ * @param drawable {@link Drawable} + * @return .9 {@link NinePatchDrawable} + */ + public static NinePatchDrawable get9PatchDrawable(final Drawable drawable) { + try { + return (NinePatchDrawable) drawable; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "get9PatchDrawable"); + } + return null; + } + + /** + * 图片着色 - tint + * @param drawable {@link Drawable} + * @param color 颜色值 + * @return 着色后的 {@link Drawable} + */ + public static Drawable setColorFilter(final Drawable drawable, @ColorInt final int color) { + return setColorFilter(drawable, color, PorterDuff.Mode.SRC_IN); + } + + /** + * 图片着色 - tint + * @param drawable {@link Drawable} + * @param color 颜色值 + * @param mode 着色模式 {@link PorterDuff.Mode} + * @return 着色后的 {@link Drawable} + */ + public static Drawable setColorFilter(final Drawable drawable, @ColorInt final int color, final PorterDuff.Mode mode) { + if (drawable != null && mode != null) { + try { + drawable.setColorFilter(color, mode); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setColorFilter"); + } + } + return drawable; + } + + // = + + /** + * 图片着色 - tint + * @param drawable {@link Drawable} + * @param colorFilter 颜色过滤 ( 效果 ) + * @return 着色后的 {@link Drawable} + */ + public static Drawable setColorFilter(final Drawable drawable, final ColorFilter colorFilter) { + if (drawable != null && colorFilter != null) { + try { + drawable.setColorFilter(colorFilter); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setColorFilter"); + } + } + return drawable; + } + + // ========== + // = Bitmap = + // ========== + + /** + * 获取 Bitmap + * @param file 文件 + * @param maxWidth 最大宽度 + * @param maxHeight 最大高度 + * @return {@link Bitmap} + */ + public static Bitmap getBitmap(final File file, final int maxWidth, final int maxHeight) { + return getBitmap(getAbsolutePath(file), maxWidth, maxHeight); + } + + /** + * 获取 Bitmap + * @param filePath 文件路径 + * @param maxWidth 最大宽度 + * @param maxHeight 最大高度 + * @return {@link Bitmap} + */ + public static Bitmap getBitmap(final String filePath, final int maxWidth, final int maxHeight) { + if (filePath == null) return null; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(filePath, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeFile(filePath, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmap"); + return null; + } + } + + /** + * 获取 Bitmap + * @param resId resource identifier + * @param maxWidth 最大宽度 + * @param maxHeight 最大高度 + * @return {@link Bitmap} + */ + public static Bitmap getBitmap(@DrawableRes final int resId, final int maxWidth, final int maxHeight) { + try { + Resources resources = getResources(); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeResource(resources, resId, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeResource(resources, resId, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmap"); + return null; + } + } + + /** + * 获取 Bitmap + * @param inputStream {@link InputStream} + * @param maxWidth 最大宽度 + * @param maxHeight 最大高度 + * @return {@link Bitmap} + */ + public static Bitmap getBitmap(final InputStream inputStream, final int maxWidth, final int maxHeight) { + if (inputStream == null) return null; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(inputStream, null, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeStream(inputStream, null, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmap"); + return null; + } + } + + /** + * 获取 Bitmap + * @param fd 文件描述 + * @param maxWidth 最大宽度 + * @param maxHeight 最大高度 + * @return {@link Bitmap} + */ + public static Bitmap getBitmap(final FileDescriptor fd, final int maxWidth, final int maxHeight) { + if (fd == null) return null; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFileDescriptor(fd, null, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeFileDescriptor(fd, null, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmap"); + return null; + } + } + + /** + * 获取 Bitmap + * @param data byte[] + * @param maxWidth 最大宽度 + * @param maxHeight 最大高度 + * @return {@link Bitmap} + */ + public static Bitmap getBitmap(final byte[] data, final int maxWidth, final int maxHeight) { + if (data == null) return null; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeByteArray(data, 0, data.length, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeByteArray(data, 0, data.length, options); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmap"); + return null; + } + } + + // = + + /** + * 通过 View 绘制为 Bitmap + * @param view {@link View} + * @return {@link Bitmap} + */ + public static Bitmap getBitmapFromView(final View view) { + if (view == null) return null; + try { + Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + view.layout(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); + view.draw(canvas); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmapFromView"); + } + return null; + } + + /** + * 通过 View Cache 绘制为 Bitmap + * @param view {@link View} + * @return {@link Bitmap} + */ + public static Bitmap getBitmapFromViewCache(final View view) { + if (view == null) return null; + try { + // 清除视图焦点 + view.clearFocus(); + // 将视图设为不可点击 + view.setPressed(false); + + // 获取视图是否可以保存画图缓存 + boolean willNotCache = view.willNotCacheDrawing(); + view.setWillNotCacheDrawing(false); + + // 获取绘制缓存位图的背景颜色 + int color = view.getDrawingCacheBackgroundColor(); + // 设置绘图背景颜色 + view.setDrawingCacheBackgroundColor(0); + if (color != 0) { // 获取的背景不是黑色的则释放以前的绘图缓存 + view.destroyDrawingCache(); // 释放绘图资源所使用的缓存 + } + + // 重新创建绘图缓存, 此时的背景色是黑色 + view.buildDrawingCache(); + // 获取绘图缓存, 注意这里得到的只是一个图像的引用 + Bitmap cacheBitmap = view.getDrawingCache(); + if (cacheBitmap == null) return null; + + Bitmap bitmap = Bitmap.createBitmap(cacheBitmap); + // 释放位图内存 + view.destroyDrawingCache(); + // 回滚以前的缓存设置、缓存颜色设置 + view.setWillNotCacheDrawing(willNotCache); + view.setDrawingCacheBackgroundColor(color); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getBitmapFromViewCache"); + } + return null; + } + + // ========================= + // = Bitmap、Drawable 转换 = + // ========================= + + // =============== + // = 转为 byte[] = + // =============== + + /** + * Bitmap 转换成 byte[] + * @param bitmap 待转换图片 + * @return byte[] + */ + public static byte[] bitmapToByte(final Bitmap bitmap) { + return bitmapToByte(bitmap, 100, Bitmap.CompressFormat.PNG); + } + + /** + * Bitmap 转换成 byte[] + * @param bitmap 待转换图片 + * @param format 如 Bitmap.CompressFormat.PNG + * @return byte[] + */ + public static byte[] bitmapToByte(final Bitmap bitmap, final Bitmap.CompressFormat format) { + return bitmapToByte(bitmap, 100, format); + } + + /** + * Bitmap 转换成 byte[] + * @param bitmap 待转换图片 + * @param quality 质量 + * @param format 如 Bitmap.CompressFormat.PNG + * @return byte[] + */ + public static byte[] bitmapToByte(final Bitmap bitmap, @IntRange(from = 0, to = 100) final int quality, + final Bitmap.CompressFormat format) { + if (bitmap == null || format == null) return null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(format, quality, baos); + return baos.toByteArray(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "bitmapToByte"); + } + return null; + } + + // = + + /** + * Drawable 转换成 byte[] + * @param drawable 待转换图片 + * @return byte[] + */ + public static byte[] drawableToByte(final Drawable drawable) { + return drawableToByte(drawable, 100, Bitmap.CompressFormat.PNG); + } + + /** + * Drawable 转换成 byte[] + * @param drawable 待转换图片 + * @param format 如 Bitmap.CompressFormat.PNG + * @return byte[] + */ + public static byte[] drawableToByte(final Drawable drawable, final Bitmap.CompressFormat format) { + return drawableToByte(drawable, 100, format); + } + + /** + * Drawable 转换成 byte[] + * @param drawable 待转换图片 + * @param quality 质量 + * @param format 如 Bitmap.CompressFormat.PNG + * @return byte[] + */ + public static byte[] drawableToByte(final Drawable drawable, @IntRange(from = 0, to = 100) final int quality, + final Bitmap.CompressFormat format) { + if (drawable == null || format == null) return null; + return bitmapToByte(drawableToBitmap(drawable), quality, format); + } + + // ========== + // = Bitmap = + // ========== + + /** + * byte[] 转 Bitmap + * @param data byte[] + * @return {@link Bitmap} + */ + public static Bitmap byteToBitmap(final byte[] data) { + return decodeByteArray(data); + } + + /** + * Bitmap 转 Drawable + * @param bitmap 待转换图片 + * @return {@link Drawable} + */ + public static Drawable bitmapToDrawable(final Bitmap bitmap) { + if (bitmap == null) return null; + try { + return new BitmapDrawable(ResourceUtils.getResources(), bitmap); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "bitmapToDrawable"); + } + return null; + } + + // ============ + // = Drawable = + // ============ + + /** + * byte[] 转 Drawable + * @param data byte[] + * @return {@link Drawable} + */ + public static Drawable byteToDrawable(final byte[] data) { + return bitmapToDrawable(decodeByteArray(data)); + } + + /** + * Drawable 转 Bitmap + * @param drawable 待转换图片 + * @return {@link Bitmap} + */ + public static Bitmap drawableToBitmap(final Drawable drawable) { + if (drawable == null) return null; + // 属于 BitmapDrawable 直接转换 + if (drawable instanceof BitmapDrawable) { + try { + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + if (bitmapDrawable.getBitmap() != null) { + return bitmapDrawable.getBitmap(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "drawableToBitmap - BitmapDrawable"); + } + } + try { + // 获取 drawable 的宽高 + int width = drawable.getIntrinsicWidth(); + int height = drawable.getIntrinsicHeight(); + // 获取 drawable 的颜色格式 + Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + // 创建 bitmap + Bitmap bitmap = Bitmap.createBitmap(width, height, config); + // 创建 bitmap 画布 + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, width, height); + // 把 drawable 内容画到画布中 + drawable.draw(canvas); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "drawableToBitmap"); + } + return null; + } + + // = + + /** + * 设置 Drawable 绘制区域 + * @param drawable {@link Drawable} + * @return {@link Drawable} + */ + public static Drawable setBounds(final Drawable drawable) { + try { + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + return drawable; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setBounds"); + } + return null; + } + + /** + * 设置 Drawable 绘制区域 + * @param drawable {@link Drawable} + * @param right right 坐标 + * @param bottom bottom 坐标 + * @return {@link Drawable} + */ + public static Drawable setBounds(final Drawable drawable, final int right, final int bottom) { + return setBounds(drawable, 0, 0, right, bottom); + } + + /** + * 设置 Drawable 绘制区域 + * @param drawable {@link Drawable} + * @param left left 坐标 + * @param top top 坐标 + * @param right right 坐标 + * @param bottom bottom 坐标 + * @return {@link Drawable} + */ + public static Drawable setBounds(final Drawable drawable, final int left, final int top, final int right, final int bottom) { + try { + drawable.setBounds(left, top, right, bottom); + return drawable; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setBounds"); + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = FileUtils = + // ============= + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 获取文件绝对路径 + * @param file 文件 + * @return 文件绝对路径 + */ + private static String getAbsolutePath(final File file) { + return file != null ? file.getAbsolutePath() : null; + } + + /** + * 判断文件是否存在, 存在则在创建之前删除 + * @param file 文件 + * @return {@code true} 创建成功, {@code false} 创建失败 + */ + private static boolean createFileByDeleteOldFile(final File file) { + if (file == null) return false; + // 文件存在并且删除失败返回 false + if (file.exists() && !file.delete()) return false; + // 创建目录失败返回 false + if (!createOrExistsDir(file.getParentFile())) return false; + try { + return file.createNewFile(); + } catch (IOException e) { + LogPrintUtils.eTag(TAG, e, "createFileByDeleteOldFile"); + return false; + } + } + + /** + * 判断目录是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + private static boolean createOrExistsDir(final File file) { + // 如果存在, 是目录则返回 true, 是文件则返回 false, 不存在则返回是否创建成功 + return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); + } + + // ============== + // = CloseUtils = + // ============== + + /** + * 安静关闭 IO + * @param closeables Closeable[] + */ + private static void closeIOQuietly(final Closeable... closeables) { + if (closeables == null) return; + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception ignore) { + } + } + } + } + + // ================= + // = ResourceUtils = + // ================= + + /** + * 获取 Resources + * @return {@link Resources} + */ + private static Resources getResources() { + try { + return DevUtils.getContext().getResources(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getResources"); + } + return null; + } + + // =============== + // = BitmapUtils = + // =============== + + /** + * 计算采样大小 + *
+     *     最大宽高只是阀值, 实际算出来的图片将小于等于这个值
+     * 
+ * @param options {@link BitmapFactory.Options} + * @param maxWidth 最大宽度 + * @param maxHeight 最大高度 + * @return 采样大小 + */ + private static int calculateInSampleSize(final BitmapFactory.Options options, final int maxWidth, final int maxHeight) { + if (options == null) return 0; + + int height = options.outHeight; + int width = options.outWidth; + int inSampleSize = 1; + while (height > maxHeight || width > maxWidth) { + height >>= 1; + width >>= 1; + inSampleSize <<= 1; + } + return inSampleSize; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/info/ApkInfoItem.java b/lib/DevApp/src/main/java/dev/utils/app/info/ApkInfoItem.java new file mode 100644 index 0000000000..ae5554d812 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/info/ApkInfoItem.java @@ -0,0 +1,364 @@ +package dev.utils.app.info; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.Signature; +import android.support.annotation.Keep; +import android.text.format.Formatter; + +import java.io.File; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.R; +import dev.utils.app.AppCommonUtils; +import dev.utils.app.SignaturesUtils; +import dev.utils.common.ConvertUtils; +import dev.utils.common.FileUtils; + +/** + * detail: APK 信息 Item + * @author Ttt + */ +public final class ApkInfoItem { + + // 日志 TAG + private static final String TAG = ApkInfoItem.class.getSimpleName(); + @Keep // APP 基本信息实体类 + private AppInfoBean appInfoBean; + @Keep // APP MD5 签名 + private String appMD5; + @Keep // APP SHA1 签名 + private String appSHA1; + @Keep // APP SHA256 签名 + private String appSHA256; + @Keep // APP 最低支持 Android SDK 版本 + private int minSdkVersion = -1; + @Keep // APP 兼容 SDK 版本 + private int targetSdkVersion = -1; + @Keep // APP 安装包大小 + private String apkLength; + @Keep // 证书对象 + private X509Certificate cert; + @Keep // 证书生成日期 + private Date notBefore; + @Keep // 证书有效期 + private Date notAfter; + @Keep // 证书是否过期 + private boolean effective; + @Keep // 证书发布方 + private String certPrincipal; + @Keep // 证书版本号 + private String certVersion; + @Keep // 证书算法名称 + private String certSigalgname; + @Keep // 证书算法 OID + private String certSigalgoid; + @Keep // 证书机器码 + private String certSerialnumber; + @Keep // 证书 DER 编码 + private String certDercode; + @Keep // APP 参数集 + private List listKeyValues = new ArrayList<>(); + + /** + * 获取 ApkInfoItem + * @param packageInfo {@link PackageInfo} + * @return {@link ApkInfoItem} + */ + protected static ApkInfoItem obtain(final PackageInfo packageInfo) { + try { + return new ApkInfoItem(packageInfo); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "obtain"); + } + return null; + } + + /** + * 初始化 ApkInfoItem + * @param packageInfo {@link PackageInfo} + */ + private ApkInfoItem(final PackageInfo packageInfo) { + // 获取 Context + Context context = DevUtils.getContext(); + // 格式化日期 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // = + // 获取 APP 信息 + appInfoBean = new AppInfoBean(packageInfo); + // 获取签名信息 + Signature[] signatures = SignaturesUtils.getSignaturesFromApk(new File(appInfoBean.getSourceDir())); + // = + // APP MD5 签名 + appMD5 = SignaturesUtils.signatureMD5(signatures); + // APP SHA1 + appSHA1 = SignaturesUtils.signatureSHA1(signatures); + // APP SHA256 + appSHA256 = SignaturesUtils.signatureSHA256(signatures); + // 属于 7.0 以上才有的方法 + if (AppCommonUtils.isN()) { + // APP 最低支持 Android SDK 版本 + minSdkVersion = packageInfo.applicationInfo.minSdkVersion; + } + // APP 兼容 SDK 版本 + targetSdkVersion = packageInfo.applicationInfo.targetSdkVersion; + // APP 安装包大小 + apkLength = Formatter.formatFileSize(DevUtils.getContext(), FileUtils.getFileLength(appInfoBean.getSourceDir())); + + // 是否保存 + boolean isError = false; + // 临时签名信息 + List listTemps = new ArrayList<>(); + + try { + // 证书对象 + cert = SignaturesUtils.getX509Certificate(signatures); + // 证书生成日期 + notBefore = cert.getNotBefore(); + // 证书有效期 + notAfter = cert.getNotAfter(); + // 设置有效期 + StringBuilder builder = new StringBuilder(); + builder.append(sdf.format(notBefore)); + builder.append(" " + context.getString(R.string.dev_str_to) + " "); // 至 + builder.append(sdf.format(notAfter)); + builder.append("\n\n"); + builder.append(notBefore); + builder.append(" " + context.getString(R.string.dev_str_to) + " "); + builder.append(notAfter); + // 保存有效期转换信息 + String effectiveStr = builder.toString(); + // 证书是否过期 + effective = false; + try { + cert.checkValidity(); + // CertificateExpiredException - 如果证书已过期 + // CertificateNotYetValidException - 如果证书不再有效 + } catch (CertificateExpiredException ce) { + effective = true; + } catch (CertificateNotYetValidException ce) { + effective = true; + } + // 证书发布方 + certPrincipal = cert.getIssuerX500Principal().toString(); + // 证书版本号 + certVersion = cert.getVersion() + ""; + // 证书算法名称 + certSigalgname = cert.getSigAlgName(); + // 证书算法 OID + certSigalgoid = cert.getSigAlgOID(); + // 证书机器码 + certSerialnumber = cert.getSerialNumber().toString(); + try { + // 证书 DER 编码 + certDercode = ConvertUtils.toHexString(cert.getTBSCertificate()); + } catch (CertificateEncodingException e) { + } + // 证书有效期 + listTemps.add(KeyValueBean.get(R.string.dev_str_effective, effectiveStr)); + // 判断是否过期 + listTemps.add(KeyValueBean.get(R.string.dev_str_iseffective, effective ? context.getString(R.string.dev_str_overdue) : context.getString(R.string.dev_str_notoverdue))); + // 证书发布方 + listTemps.add(KeyValueBean.get(R.string.dev_str_principal, certPrincipal)); + // 证书版本号 + listTemps.add(KeyValueBean.get(R.string.dev_str_version, certVersion)); + // 证书算法名称 + listTemps.add(KeyValueBean.get(R.string.dev_str_sigalgname, certSigalgname)); + // 证书算法 OID + listTemps.add(KeyValueBean.get(R.string.dev_str_sigalgoid, certSigalgoid)); + // 证书机器码 + listTemps.add(KeyValueBean.get(R.string.dev_str_dercode, certSerialnumber)); + // 证书 DER 编码 + listTemps.add(KeyValueBean.get(R.string.dev_str_serialnumber, certDercode)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "ApkInfoItem"); + isError = true; + } + + // = 保存集合 = + + // APP 包名 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_packname, appInfoBean.getAppPackName())); + // 没报错才存储 MD5 信息 + if (!isError) { + // APP MD5 签名 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_md5, appMD5)); + } + // APP 版本号 - 主要用于 APP 内部版本判断 int 类型 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_version_code, appInfoBean.getVersionCode() + "")); + // APP 版本名 - 主要用于对用户显示版本信息 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_version_name, appInfoBean.getVersionName())); + // 安装包地址 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_apk_uri, appInfoBean.getSourceDir())); + // 没报错才存储 SHA 信息 + if (!isError) { + // APP SHA1 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_sha1, appSHA1)); + // APP SHA256 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_sha256, appSHA256)); + } + // APP 最低支持 Android SDK 版本 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_minsdkversion, minSdkVersion + " ( " + AppCommonUtils.convertSDKVersion(minSdkVersion) + "+ )")); + // APP 兼容 SDK 版本 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_targetsdkversion, targetSdkVersion + " ( " + AppCommonUtils.convertSDKVersion(targetSdkVersion) + "+ )")); + // 获取 APK 大小 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_apk_length, apkLength)); + // 没报错才存储 其他签名信息 + if (!isError) { + listKeyValues.addAll(listTemps); + } + } + + /** + * 获取 AppInfoBean + * @return {@link AppInfoBean} + */ + public AppInfoBean getAppInfoBean() { + return appInfoBean; + } + + /** + * 获取 List 信息键对值集合 + * @return APP 信息键对值集合 + */ + public List getListKeyValues() { + return listKeyValues; + } + + /** + * 获取 APP MD5 签名 + * @return APP MD5 签名 + */ + public String getAppMD5() { + return appMD5; + } + + /** + * 获取 APP SHA1 签名 + * @return APP SHA1 签名 + */ + public String getAppSHA1() { + return appSHA1; + } + + /** + * 获取 APP SHA256 签名 + * @return APP SHA256 签名 + */ + public String getAppSHA256() { + return appSHA256; + } + + /** + * 获取 APP 最低支持 Android SDK 版本 + * @return APP 最低支持 Android SDK 版本 + */ + public int getMinSdkVersion() { + return minSdkVersion; + } + + /** + * 获取 APP 兼容 SDK 版本 + * @return APP 兼容 SDK 版本 + */ + public int getTargetSdkVersion() { + return targetSdkVersion; + } + + /** + * 获取 APP 安装包大小 + * @return APP 安装包大小 + */ + public String getApkLength() { + return apkLength; + } + + /** + * 获取证书对象 + * @return {@link X509Certificate} + */ + public X509Certificate getX509Certificate() { + return cert; + } + + /** + * 获取证书生成日期 + * @return 证书生成日期 + */ + public Date getNotBefore() { + return notBefore; + } + + /** + * 获取证书有效期 + * @return 证书有效期 + */ + public Date getNotAfter() { + return notAfter; + } + + /** + * 获取证书是否过期 + * @return {@code true} 过期, {@code false} 未过期 + */ + public boolean isEffective() { + return effective; + } + + /** + * 获取证书发布方 + * @return 证书发布方 + */ + public String getCertPrincipal() { + return certPrincipal; + } + + /** + * 获取证书版本号 + * @return 证书版本号 + */ + public String getCertVersion() { + return certVersion; + } + + /** + * 获取证书算法名称 + * @return 证书算法名称 + */ + public String getCertSigalgname() { + return certSigalgname; + } + + /** + * 获取证书算法 OID + * @return 证书算法 OID + */ + public String getCertSigalgoid() { + return certSigalgoid; + } + + /** + * 获取证书机器码 + * @return 证书机器码 + */ + public String getCertSerialnumber() { + return certSerialnumber; + } + + /** + * 获取证书 DER 编码 + * @return 证书 DER 编码 + */ + public String getCertDercode() { + return certDercode; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoBean.java b/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoBean.java new file mode 100644 index 0000000000..293d6f4c08 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoBean.java @@ -0,0 +1,216 @@ +package dev.utils.app.info; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.support.annotation.Keep; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.common.FileUtils; + +/** + * detail: APP 信息实体类 + * @author Ttt + */ +public class AppInfoBean { + + // 日志 TAG + private static final String TAG = AppInfoBean.class.getSimpleName(); + @Keep // APP 包名 + private String appPackName; + @Keep // APP 应用名 + private String appName; + @Keep // APP 图标 + private transient Drawable appIcon; + @Keep // APP 类型 + private AppType appType; + @Keep // APP 版本号 + private int versionCode; + @Keep // APP 版本名 + private String versionName; + @Keep // APP 首次安装时间 + private long firstInstallTime; + @Keep // APP 最后一次更新时间 + private long lastUpdateTime; + @Keep // APP 地址 + private String sourceDir; + @Keep // APK 大小 + private long apkSize; + + /** + * 获取 AppInfoBean + * @param packageInfo {@link PackageInfo} + * @return {@link AppInfoBean} + */ + protected static AppInfoBean obtain(final PackageInfo packageInfo) { + try { + return new AppInfoBean(packageInfo); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "obtain"); + } + return null; + } + + /** + * 初始化 AppInfoBean + * @param packageInfo {@link PackageInfo} + */ + protected AppInfoBean(final PackageInfo packageInfo) { + this(packageInfo, DevUtils.getContext().getPackageManager()); + } + + /** + * 初始化 AppInfoBean + * @param packageInfo {@link PackageInfo} + * @param packageManager {@link PackageManager} + */ + protected AppInfoBean(final PackageInfo packageInfo, final PackageManager packageManager) { + // APP 包名 + appPackName = packageInfo.applicationInfo.packageName; + // APP 应用名 + appName = packageManager.getApplicationLabel(packageInfo.applicationInfo).toString(); + // APP 图标 + appIcon = packageManager.getApplicationIcon(packageInfo.applicationInfo); + // APP 类型 + appType = AppInfoBean.getAppType(packageInfo); + // APP 版本号 + versionCode = packageInfo.versionCode; + // APP 版本名 + versionName = packageInfo.versionName; + // APP 首次安装时间 + firstInstallTime = packageInfo.firstInstallTime; + // APP 最后一次更新时间 + lastUpdateTime = packageInfo.lastUpdateTime; + // APP 地址 + sourceDir = packageInfo.applicationInfo.sourceDir; + // APK 大小 + apkSize = FileUtils.getFileLength(sourceDir); + } + + /** + * 获取 APP 包名 + * @return APP 包名 + */ + public String getAppPackName() { + return appPackName; + } + + /** + * 获取 APP 应用名 + * @return APP 应用名 + */ + public String getAppName() { + return appName; + } + + /** + * 获取 APP 图标 + * @return APP 图标 + */ + public Drawable getAppIcon() { + return appIcon; + } + + /** + * 获取 APP 类型 + * @return APP 类型 + */ + public AppType getAppType() { + return appType; + } + + /** + * 获取 versionCode + * @return versionCode + */ + public int getVersionCode() { + return versionCode; + } + + /** + * 获取 versionName + * @return versionName + */ + public String getVersionName() { + return versionName; + } + + /** + * 获取 APP 首次安装时间 + * @return APP 首次安装时间 + */ + public long getFirstInstallTime() { + return firstInstallTime; + } + + /** + * 获取 APP 最后更新时间 + * @return APP 最后更新时间 + */ + public long getLastUpdateTime() { + return lastUpdateTime; + } + + /** + * 获取 APK 地址 + * @return APK 地址 + */ + public String getSourceDir() { + return sourceDir; + } + + /** + * 获取 APK 大小 + * @return APK 大小 + */ + public long getApkSize() { + return apkSize; + } + + // = + + /** + * detail: 应用类型 + * @author Ttt + */ + public enum AppType { + + USER, // 用户 APP + + SYSTEM, // 系统 APP + + ALL // 全部 APP + } + + /** + * 获取 APP 类型 + * @param packageInfo {@link PackageInfo} + * @return {@link AppType} 应用类型 + */ + public static AppType getAppType(final PackageInfo packageInfo) { + if (!isSystemApp(packageInfo) && !isSystemUpdateApp(packageInfo)) { + return AppType.USER; + } + return AppType.SYSTEM; + } + + /** + * 是否系统程序 + * @param packageInfo {@link PackageInfo} + * @return {@code true} yes, {@code false} no + */ + public static boolean isSystemApp(final PackageInfo packageInfo) { + return ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); + } + + /** + * 是否系统程序被手动更新后, 也成为第三方应用程序 + * @param packageInfo {@link PackageInfo} + * @return {@code true} yes, {@code false} no + */ + public static boolean isSystemUpdateApp(final PackageInfo packageInfo) { + return ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoItem.java b/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoItem.java new file mode 100644 index 0000000000..fc4ef7b72c --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoItem.java @@ -0,0 +1,340 @@ +package dev.utils.app.info; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.support.annotation.Keep; +import android.text.format.Formatter; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.R; +import dev.utils.app.AppCommonUtils; +import dev.utils.app.SignaturesUtils; +import dev.utils.common.ConvertUtils; +import dev.utils.common.FileUtils; + +/** + * detail: APP 信息 Item + * @author Ttt + */ +public final class AppInfoItem { + + // 日志 TAG + private static final String TAG = AppInfoItem.class.getSimpleName(); + @Keep // APP 基本信息实体类 + private AppInfoBean appInfoBean; + @Keep // APP MD5 签名 + private String appMD5; + @Keep // APP SHA1 签名 + private String appSHA1; + @Keep // APP SHA256 签名 + private String appSHA256; + @Keep // APP 最低支持 Android SDK 版本 + private int minSdkVersion = -1; + @Keep // APP 兼容 SDK 版本 + private int targetSdkVersion = -1; + @Keep // APP 安装包大小 + private String apkLength; + @Keep // 证书对象 + private X509Certificate cert; + @Keep // 证书生成日期 + private Date notBefore; + @Keep // 证书有效期 + private Date notAfter; + @Keep // 证书是否过期 + private boolean effective; + @Keep // 证书发布方 + private String certPrincipal; + @Keep // 证书版本号 + private String certVersion; + @Keep // 证书算法名称 + private String certSigalgname; + @Keep // 证书算法 OID + private String certSigalgoid; + @Keep // 证书机器码 + private String certSerialnumber; + @Keep // 证书 DER 编码 + private String certDercode; + @Keep // APP 参数集 + private List listKeyValues = new ArrayList<>(); + + /** + * 获取 AppInfoItem + * @param packageInfo {@link PackageInfo} + * @return {@link AppInfoItem} + */ + protected static AppInfoItem obtain(final PackageInfo packageInfo) { + try { + return new AppInfoItem(packageInfo); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "obtain"); + } + return null; + } + + /** + * 初始化 AppInfoItem + * @param packageInfo {@link PackageInfo} + */ + private AppInfoItem(final PackageInfo packageInfo) { + // 获取 Context + Context context = DevUtils.getContext(); + // 格式化日期 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // = + // 获取 APP 信息 + appInfoBean = new AppInfoBean(packageInfo); + // APP MD5 签名 + appMD5 = SignaturesUtils.signatureMD5(packageInfo.signatures); + // APP SHA1 + appSHA1 = SignaturesUtils.signatureSHA1(packageInfo.signatures); + // APP SHA256 + appSHA256 = SignaturesUtils.signatureSHA256(packageInfo.signatures); + // 属于 7.0 以上才有的方法 + if (AppCommonUtils.isN()) { + // APP 最低支持 Android SDK 版本 + minSdkVersion = packageInfo.applicationInfo.minSdkVersion; + } + // APP 兼容 SDK 版本 + targetSdkVersion = packageInfo.applicationInfo.targetSdkVersion; + // APP 安装包大小 + apkLength = Formatter.formatFileSize(DevUtils.getContext(), FileUtils.getFileLength(appInfoBean.getSourceDir())); + // 证书对象 + cert = SignaturesUtils.getX509Certificate(packageInfo.signatures); + // 证书生成日期 + notBefore = cert.getNotBefore(); + // 证书有效期 + notAfter = cert.getNotAfter(); + // 设置有效期 + StringBuilder builder = new StringBuilder(); + builder.append(sdf.format(notBefore)); + builder.append(" " + context.getString(R.string.dev_str_to) + " "); // 至 + builder.append(sdf.format(notAfter)); + builder.append("\n\n"); + builder.append(notBefore); + builder.append(" " + context.getString(R.string.dev_str_to) + " "); + builder.append(notAfter); + // 保存有效期转换信息 + String effectiveStr = builder.toString(); + // 证书是否过期 + effective = false; + try { + cert.checkValidity(); + // CertificateExpiredException - 如果证书已过期 + // CertificateNotYetValidException - 如果证书不再有效 + } catch (CertificateExpiredException ce) { + effective = true; + } catch (CertificateNotYetValidException ce) { + effective = true; + } + // 证书发布方 + certPrincipal = cert.getIssuerX500Principal().toString(); + // 证书版本号 + certVersion = cert.getVersion() + ""; + // 证书算法名称 + certSigalgname = cert.getSigAlgName(); + // 证书算法 OID + certSigalgoid = cert.getSigAlgOID(); + // 证书机器码 + certSerialnumber = cert.getSerialNumber().toString(); + try { + // 证书 DER 编码 + certDercode = ConvertUtils.toHexString(cert.getTBSCertificate()); + } catch (CertificateEncodingException e) { + } + + // = 保存集合 = + + // APP 包名 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_packname, appInfoBean.getAppPackName())); + // APP MD5 签名 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_md5, appMD5)); + // APP 版本号 - 主要用于 app 内部版本判断 int 类型 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_version_code, appInfoBean.getVersionCode() + "")); + // APP 版本名 - 主要用于对用户显示版本信息 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_version_name, appInfoBean.getVersionName())); + // APP SHA1 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_sha1, appSHA1)); + // APP SHA256 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_sha256, appSHA256)); + // APP 首次安装时间 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_first_install_time, sdf.format(packageInfo.firstInstallTime))); + // 获取最后一次更新时间 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_last_update_time, sdf.format(packageInfo.lastUpdateTime))); + // APP 最低支持 Android SDK 版本 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_minsdkversion, minSdkVersion + " ( " + AppCommonUtils.convertSDKVersion(minSdkVersion) + "+ )")); + // APP 兼容 SDK 版本 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_targetsdkversion, targetSdkVersion + " ( " + AppCommonUtils.convertSDKVersion(targetSdkVersion) + "+ )")); + // APK 大小 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_apk_length, apkLength)); + // 证书有效期 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_effective, effectiveStr)); + // 判断是否过期 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_iseffective, effective ? context.getString(R.string.dev_str_overdue) : context.getString(R.string.dev_str_notoverdue))); + // 证书发布方 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_principal, certPrincipal)); + // 证书版本号 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_version, certVersion)); + // 证书算法名称 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_sigalgname, certSigalgname)); + // 证书算法 OID + listKeyValues.add(KeyValueBean.get(R.string.dev_str_sigalgoid, certSigalgoid)); + // 证书机器码 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_dercode, certSerialnumber)); + // 证书 DER 编码 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_serialnumber, certDercode)); + } + + /** + * 获取 AppInfoBean + * @return {@link AppInfoBean} + */ + public AppInfoBean getAppInfoBean() { + return appInfoBean; + } + + /** + * 获取 List 信息键对值集合 + * @return APP 信息键对值集合 + */ + public List getListKeyValues() { + return listKeyValues; + } + + /** + * 获取 APP MD5 签名 + * @return APP MD5 签名 + */ + public String getAppMD5() { + return appMD5; + } + + /** + * 获取 APP SHA1 签名 + * @return APP SHA1 签名 + */ + public String getAppSHA1() { + return appSHA1; + } + + /** + * 获取 APP SHA256 签名 + * @return APP SHA256 签名 + */ + public String getAppSHA256() { + return appSHA256; + } + + /** + * 获取 APP 最低支持 Android SDK 版本 + * @return APP 最低支持 Android SDK 版本 + */ + public int getMinSdkVersion() { + return minSdkVersion; + } + + /** + * 获取 APP 兼容 SDK 版本 + * @return APP 兼容 SDK 版本 + */ + public int getTargetSdkVersion() { + return targetSdkVersion; + } + + /** + * 获取 APP 安装包大小 + * @return APP 安装包大小 + */ + public String getApkLength() { + return apkLength; + } + + /** + * 获取证书对象 + * @return {@link X509Certificate} + */ + public X509Certificate getX509Certificate() { + return cert; + } + + /** + * 获取证书生成日期 + * @return 证书生成日期 + */ + public Date getNotBefore() { + return notBefore; + } + + /** + * 获取证书有效期 + * @return 证书有效期 + */ + public Date getNotAfter() { + return notAfter; + } + + /** + * 获取证书是否过期 + * @return {@code true} 过期, {@code false} 未过期 + */ + public boolean isEffective() { + return effective; + } + + /** + * 获取证书发布方 + * @return 证书发布方 + */ + public String getCertPrincipal() { + return certPrincipal; + } + + /** + * 获取证书版本号 + * @return 证书版本号 + */ + public String getCertVersion() { + return certVersion; + } + + /** + * 获取证书算法名称 + * @return 证书算法名称 + */ + public String getCertSigalgname() { + return certSigalgname; + } + + /** + * 获取证书算法 OID + * @return 证书算法 OID + */ + public String getCertSigalgoid() { + return certSigalgoid; + } + + /** + * 获取证书机器码 + * @return 证书机器码 + */ + public String getCertSerialnumber() { + return certSerialnumber; + } + + /** + * 获取证书 DER 编码 + * @return 证书 DER 编码 + */ + public String getCertDercode() { + return certDercode; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoUtils.java b/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoUtils.java new file mode 100644 index 0000000000..ff4feb5884 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/info/AppInfoUtils.java @@ -0,0 +1,349 @@ +package dev.utils.app.info; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: APP 信息获取工具类 + * @author Ttt + */ +public final class AppInfoUtils { + + private AppInfoUtils() { + } + + // 日志 TAG + private static final String TAG = AppInfoUtils.class.getSimpleName(); + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + + /** + * 通过 APK 路径 初始化 PackageInfo + * @param file APK 文件路径 + * @return {@link PackageInfo} + */ + public static PackageInfo getPackageInfoToFile(final File file) { + if (!isFileExists(file)) return null; + return getPackageInfoToPath(file.getAbsolutePath()); + } + + /** + * 通过 APK 路径 初始化 PackageInfo + * @param apkUri APK 文件路径 + * @return {@link PackageInfo} + */ + public static PackageInfo getPackageInfoToPath(final String apkUri) { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageArchiveInfo(apkUri, PackageManager.GET_ACTIVITIES); + // 设置 APK 位置信息 + ApplicationInfo appInfo = packageInfo.applicationInfo; + // 必须加这两句, 不然下面 icon 获取是 default icon 而不是应用包的 icon + appInfo.sourceDir = apkUri; + appInfo.publicSourceDir = apkUri; + return packageInfo; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPackageInfoToPath"); + } + return null; + } + + /** + * 获取当前应用 PackageInfo + * @return {@link PackageInfo} + */ + public static PackageInfo getPackageInfo() { + return getPackageInfo(DevUtils.getContext().getPackageName()); + } + + /** + * 通过包名 获取 PackageInfo + * @param packageName 应用包名 + * @return {@link PackageInfo} + */ + public static PackageInfo getPackageInfo(final String packageName) { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + // 获取对应的 PackageInfo ( 原始的 PackageInfo 获取 signatures 等于 null, 需要这样获取 ) + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + // 返回 APP 信息 + return packageInfo; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPackageInfo"); + } + return null; + } + + // ================ + // = 获取基本信息 = + // ================ + + /** + * 通过 APK 路径 获取 AppInfoBean + * @param file APK 文件路径 + * @return {@link AppInfoBean} + */ + public static AppInfoBean getAppInfoBeanToFile(final File file) { + return AppInfoBean.obtain(getPackageInfoToFile(file)); + } + + /** + * 通过 APK 路径 获取 AppInfoBean + * @param apkUri APK 文件路径 + * @return {@link AppInfoBean} + */ + public static AppInfoBean getAppInfoBeanToPath(final String apkUri) { + return AppInfoBean.obtain(getPackageInfoToPath(apkUri)); + } + + /** + * 获取当前应用 AppInfoBean + * @return {@link AppInfoBean} + */ + public static AppInfoBean getAppInfoBean() { + return AppInfoBean.obtain(getPackageInfo()); + } + + /** + * 通过包名 获取 AppInfoBean + * @param packageName 应用包名 + * @return {@link AppInfoBean} + */ + public static AppInfoBean getAppInfoBean(final String packageName) { + return AppInfoBean.obtain(getPackageInfo(packageName)); + } + + // ================ + // = 获取详细信息 = + // ================ + + /** + * 获取 APK 详细信息 + * @param file APK 文件路径 + * @return {@link ApkInfoItem} + */ + public static ApkInfoItem getApkInfoItem(final File file) { + if (!isFileExists(file)) return null; + return getApkInfoItem(file.getAbsolutePath()); + } + + /** + * 获取 APK 详细信息 + * @param apkUri APK 文件路径 + * @return {@link ApkInfoItem} + */ + public static ApkInfoItem getApkInfoItem(final String apkUri) { + try { + return ApkInfoItem.obtain(getPackageInfoToPath(apkUri)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getApkInfoItem"); + return null; + } + } + + // = + + /** + * 获取 APP 详细信息 + * @return {@link AppInfoItem} + */ + public static AppInfoItem getAppInfoItem() { + try { + return AppInfoItem.obtain(getPackageInfo()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppInfoItem"); + return null; + } + } + + /** + * 获取 APP 详细信息 + * @param packageName 应用包名 + * @return {@link AppInfoItem} + */ + public static AppInfoItem getAppInfoItem(final String packageName) { + try { + return AppInfoItem.obtain(getPackageInfo(packageName)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppInfoItem"); + return null; + } + } + + // = + + /** + * 获取全部 APP 列表 + * @return APP 列表 + */ + public static List getAppLists() { + return getAppLists(AppInfoBean.AppType.ALL); + } + + /** + * 获取 APP 列表 + * @param appType APP 类型 + * @return APP 列表 + */ + public static List getAppLists(final AppInfoBean.AppType appType) { + // APP 信息 + ArrayList listApps = new ArrayList<>(); + // 防止为 null + if (appType != null) { + // 管理应用程序包 + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + // 获取手机内所有应用 + List packlist = packageManager.getInstalledPackages(0); + // 判断是否属于添加全部 + if (appType == AppInfoBean.AppType.ALL) { + // 遍历 APP 列表 + for (int i = 0, len = packlist.size(); i < len; i++) { + PackageInfo packageInfo = packlist.get(i); + // 添加符合条件的 APP 应用信息 + listApps.add(new AppInfoBean(packageInfo, packageManager)); + } + } else { + // 遍历 APP 列表 + for (int i = 0, len = packlist.size(); i < len; i++) { + PackageInfo packageInfo = packlist.get(i); + // 获取 APP 类型 + AppInfoBean.AppType cAppType = AppInfoBean.getAppType(packageInfo); + // 判断类型 + if (appType == cAppType) { + // 添加符合条件的 APP 应用信息 + listApps.add(new AppInfoBean(packageInfo, packageManager)); + } + } + } + } + return listApps; + } + + // = + + /** + * 获取 APP 注册的权限 + * @return APP 注册的权限 + */ + public static List getAppPermissionToList() { + return new ArrayList<>(getAppPermissionToSet()); + } + + /** + * 获取 APP 注册的权限 + * @return APP 注册的权限 + */ + public static Set getAppPermissionToSet() { + String[] permissions = getAppPermission(); + // 防止数据为 null + if (permissions != null && permissions.length != 0) { + Set permissionSets = new HashSet<>(); + for (String permission : permissions) { + permissionSets.add(permission); + } + return permissionSets; + } + return Collections.emptySet(); + } + + /** + * 获取 APP 注册的权限 + * @return APP 注册的权限数组 + */ + public static String[] getAppPermission() { + return getAppPermission(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP 注册的权限 + * @param packageName 应用包名 + * @return APP 注册的权限数组 + */ + public static String[] getAppPermission(final String packageName) { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); + return packageInfo.requestedPermissions; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppPermission"); + } + return null; + } + + /** + * 打印 APP 注册的权限 + * @param packageName 应用包名 + */ + public static void printAppPermission(final String packageName) { + try { + StringBuilder builder = new StringBuilder(); + // = + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); + String[] usesPermissionsArray = packageInfo.requestedPermissions; + for (int i = 0; i < usesPermissionsArray.length; i++) { + // 获取每个权限的名字, 如: android.permission.INTERNET + String usesPermissionName = usesPermissionsArray[i]; + // 拼接日志 + builder.append("usesPermissionName = " + usesPermissionName); + builder.append(NEW_LINE_STR); + + // 通过 usesPermissionName 获取该权限的详细信息 + PermissionInfo permissionInfo = packageManager.getPermissionInfo(usesPermissionName, 0); + + // 获取该权限属于哪个权限组, 如: 网络通信 + PermissionGroupInfo permissionGroupInfo = packageManager.getPermissionGroupInfo(permissionInfo.group, 0); + // 拼接日志 + builder.append("permissionGroup = " + permissionGroupInfo.loadLabel(packageManager).toString()); + builder.append(NEW_LINE_STR); + + // 获取该权限的标签信息, 比如: 完全的网络访问权限 + String permissionLabel = permissionInfo.loadLabel(packageManager).toString(); + // 拼接日志 + builder.append("permissionLabel = " + permissionLabel); + builder.append(NEW_LINE_STR); + + // 获取该权限的详细描述信息, 比如: 允许该应用创建网络套接字和使用自定义网络协议 + // 浏览器和其他某些应用提供了向互联网发送数据的途径, 因此应用无需该权限即可向互联网发送数据 + String permissionDescription = permissionInfo.loadDescription(packageManager).toString(); + // 拼接日志 + builder.append("permissionDescription = " + permissionDescription); + builder.append(NEW_LINE_STR); + } + // 打印日志 + LogPrintUtils.dTag(TAG, builder.toString()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "printAppPermission"); + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = FileUtils = + // ============= + + /** + * 检查是否存在某个文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + private static boolean isFileExists(final File file) { + return file != null && file.exists(); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/info/KeyValueBean.java b/lib/DevApp/src/main/java/dev/utils/app/info/KeyValueBean.java new file mode 100644 index 0000000000..043b4a1b29 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/info/KeyValueBean.java @@ -0,0 +1,59 @@ +package dev.utils.app.info; + +import android.support.annotation.Keep; +import android.support.annotation.StringRes; + +import dev.DevUtils; + +/** + * detail: 键对值实体类 + * @author Ttt + */ +public class KeyValueBean { + + @Keep // 键 - 提示 + protected String key = ""; + @Keep // 值 - 参数值 + protected String value = ""; + + /** + * 构造函数 + * @param key key + * @param value value + */ + public KeyValueBean(final String key, final String value) { + this.key = key; + this.value = value; + } + + /** + * 获取 key + * @return key + */ + public String getKey() { + return key; + } + + /** + * 获取 value + * @return value + */ + public String getValue() { + return value; + } + + @Override + public String toString() { + return key + ": " + value; + } + + /** + * 通过 resId 设置 key, 并且初始化 KeyValueBean + * @param resId R.string.id + * @param value value + * @return {@link KeyValueBean} + */ + public static KeyValueBean get(@StringRes final int resId, final String value) { + return new KeyValueBean(DevUtils.getContext().getString(resId), value); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/logger/DevLogger.java b/lib/DevApp/src/main/java/dev/utils/app/logger/DevLogger.java new file mode 100644 index 0000000000..79b61cc27e --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/logger/DevLogger.java @@ -0,0 +1,249 @@ +package dev.utils.app.logger; + +/** + * detail: 日志操作类 ( 对外公开直接调用 ) + * @author Ttt + */ +public final class DevLogger { + + private DevLogger() { + } + + // 包下 LoggerPrinter 类持有对象 + private static final IPrinter sPrinter = new LoggerPrinter(); + + // ============ + // = 配置方法 = + // ============ + + /** + * 使用单次其他日志配置 + * @param logConfig 日志配置 + * @return {@link IPrinter} + */ + public static IPrinter other(final LogConfig logConfig) { + return sPrinter.other(logConfig); + } + + /** + * 获取日志配置信息 + * @return {@link LogConfig} 日志配置 + */ + public static LogConfig getLogConfig() { + return sPrinter.getLogConfig(); + } + + /** + * 初始化日志配置信息 ( 使用默认配置 ) + * @return {@link LogConfig} 日志配置 + */ + public static LogConfig init() { + return sPrinter.init(); + } + + /** + * 自定义日志配置信息 + * @param logConfig 日志配置 + */ + public static void init(final LogConfig logConfig) { + sPrinter.init(logConfig); + } + + // =============================== + // = 使用默认 TAG - 日志打印方法 = + // =============================== + + /** + * 打印 Log.DEBUG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void d(final String message, final Object... args) { + sPrinter.d(message, args); + } + + /** + * 打印 Log.ERROR + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void e(final String message, final Object... args) { + sPrinter.e(message, args); + } + + /** + * 打印 Log.ERROR + * @param throwable 异常 + */ + public static void e(final Throwable throwable) { + sPrinter.e(throwable, null); + } + + /** + * 打印 Log.ERROR + * @param throwable 异常 + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void e(final Throwable throwable, final String message, final Object... args) { + sPrinter.e(throwable, message, args); + } + + /** + * 打印 Log.WARN + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void w(final String message, final Object... args) { + sPrinter.w(message, args); + } + + /** + * 打印 Log.INFO + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void i(final String message, final Object... args) { + sPrinter.i(message, args); + } + + /** + * 打印 Log.VERBOSE + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void v(final String message, final Object... args) { + sPrinter.v(message, args); + } + + /** + * 打印 Log.ASSERT + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void wtf(final String message, final Object... args) { + sPrinter.wtf(message, args); + } + + // = + + /** + * 格式化 JSON 格式数据, 并打印 + * @param json JSON 格式字符串 + */ + public static void json(final String json) { + sPrinter.json(json); + } + + /** + * 格式化 XML 格式数据, 并打印 + * @param xml XML 格式字符串 + */ + public static void xml(final String xml) { + sPrinter.xml(xml); + } + + // ================================= + // = 使用自定义 TAG - 日志打印方法 = + // ================================= + + /** + * 打印 Log.DEBUG + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void dTag(final String tag, final String message, final Object... args) { + sPrinter.dTag(tag, message, args); + } + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void eTag(final String tag, final String message, final Object... args) { + sPrinter.eTag(tag, message, args); + } + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param throwable 异常 + */ + public static void eTag(final String tag, final Throwable throwable) { + sPrinter.eTag(tag, throwable, null); + } + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param throwable 异常 + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void eTag(final String tag, final Throwable throwable, final String message, final Object... args) { + sPrinter.eTag(tag, throwable, message, args); + } + + /** + * 打印 Log.WARN + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void wTag(final String tag, final String message, final Object... args) { + sPrinter.wTag(tag, message, args); + } + + /** + * 打印 Log.INFO + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void iTag(final String tag, final String message, final Object... args) { + sPrinter.iTag(tag, message, args); + } + + /** + * 打印 Log.VERBOSE + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void vTag(final String tag, final String message, final Object... args) { + sPrinter.vTag(tag, message, args); + } + + /** + * 打印 Log.ASSERT + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void wtfTag(final String tag, final String message, final Object... args) { + sPrinter.wtfTag(tag, message, args); + } + + // = + + /** + * 格式化 JSON 格式数据, 并打印 + * @param tag 日志 TAG + * @param json JSON 格式字符串 + */ + public static void jsonTag(final String tag, final String json) { + sPrinter.jsonTag(tag, json); + } + + /** + * 格式化 XML 格式数据, 并打印 + * @param tag 日志 TAG + * @param xml XML 格式字符串 + */ + public static void xmlTag(final String tag, final String xml) { + sPrinter.xmlTag(tag, xml); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/logger/DevLoggerUtils.java b/lib/DevApp/src/main/java/dev/utils/app/logger/DevLoggerUtils.java new file mode 100644 index 0000000000..d85427ab4a --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/logger/DevLoggerUtils.java @@ -0,0 +1,278 @@ +package dev.utils.app.logger; + +import android.text.TextUtils; + +import java.io.File; + +import dev.utils.LogPrintUtils; + +/** + * detail: 日志操作工具类 + * @author Ttt + */ +public final class DevLoggerUtils { + + private DevLoggerUtils() { + } + + // 日志 TAG + private static final String TAG = DevLoggerUtils.class.getSimpleName(); + + /** + * 初始化调用方法 ( 内部已调用 ) + */ + public static void init() { + // 保存 APP 版本信息 + Utils.init(); + } + + // ======================== + // = 快速初始化 LogConfig = + // ======================== + + /** + * 获取 Release Log 配置 ( 打印线程信息、显示方法总数 3、从 0 开始、不进行排序、默认只打印 ERROR 级别日志 ) + * @param tag 日志 TAG + * @return {@link LogConfig} 日志配置 + */ + public static LogConfig getReleaseLogConfig(final String tag) { + return getLogConfig(tag, 3, 0, false, true, false, LogLevel.ERROR); + } + + /** + * 获取 Release Log 配置 ( 打印线程信息、显示方法总数 3、从 0 开始、不进行排序 ) + * @param tag 日志 TAG + * @param logLevel 日志级别 + * @return {@link LogConfig} 日志配置 + */ + public static LogConfig getReleaseLogConfig(final String tag, final LogLevel logLevel) { + return getLogConfig(tag, 3, 0, false, true, false, logLevel); + } + + // = + + /** + * 获取 Debug Log 配置 ( 打印线程信息、显示方法总数 3、从 0 开始、不进行排序、默认只打印 ERROR 级别日志 ) + * @param tag 日志 TAG + * @return {@link LogConfig} 日志配置 + */ + public static LogConfig getDebugLogConfig(final String tag) { + return getLogConfig(tag, 3, 0, false, true, false, LogLevel.DEBUG); + } + + /** + * 获取 Debug Log 配置 ( 打印线程信息、显示方法总数 3、从 0 开始、进行排序 ) + * @param tag 日志 TAG + * @param logLevel 日志级别 + * @return {@link LogConfig} 日志配置 + */ + public static LogConfig getDebugLogConfig(final String tag, final LogLevel logLevel) { + return getLogConfig(tag, 3, 0, false, true, false, logLevel); + } + + // = + + /** + * 获取 Log 配置 ( 打印线程信息、显示方法总数 3、从 0 开始、并且美化日志信息、默认打印 DEBUG 级别及以上日志 ) + * @param tag 日志 TAG + * @return {@link LogConfig} 日志配置 + */ + public static LogConfig getSortLogConfig(final String tag) { + return getLogConfig(tag, 3, 0, false, true, true, LogLevel.DEBUG); + } + + /** + * 获取 Log 配置 ( 打印线程信息、显示方法总数 3、从 0 开始、并且美化日志信息 ) + * @param tag 日志 TAG + * @param logLevel 日志级别 + * @return {@link LogConfig} 日志配置 + */ + public static LogConfig getSortLogConfig(final String tag, final LogLevel logLevel) { + return getLogConfig(tag, 3, 0, false, true, true, logLevel); + } + + // = + + /** + * 获取 Log 配置 + * @param tag 日志 TAG + * @param count 显示的方法总数 ( 推荐 3) + * @param offset 方法偏移索引 ( 从第几个方法开始打印, 默认推荐 0) + * @param allMethod 是否打印全部方法 + * @param threadInfo 是否显示线程信息 + * @param sortLog 是否排序日志 ( 美化 ) + * @param logLevel 日志级别 + * @return {@link LogConfig} 日志配置 + */ + public static LogConfig getLogConfig(final String tag, final int count, final int offset, final boolean allMethod, final boolean threadInfo, final boolean sortLog, final LogLevel logLevel) { + // 生成默认配置信息 + LogConfig logConfig = new LogConfig(); + // 堆栈方法总数 ( 显示经过的方法 ) + logConfig.methodCount = count; + // 堆栈方法索引偏移 (0 = 最新经过调用的方法信息, 偏移则往上推, 如 1 = 倒数第二条经过调用的方法信息 ) + logConfig.methodOffset = offset; + // 是否输出全部方法 ( 在特殊情况下, 如想要打印全部经过的方法, 但是不知道经过的总数 ) + logConfig.outputMethodAll = allMethod; + // 显示日志线程信息 ( 特殊情况, 显示经过的线程信息, 具体情况如上 ) + logConfig.displayThreadInfo = threadInfo; + // 是否排序日志 ( 格式化 ) + logConfig.sortLog = sortLog; + // 日志级别 + logConfig.logLevel = logLevel; + // 设置 TAG ( 特殊情况使用, 不使用全部的 TAG 时, 如单独输出在某个 TAG 下 ) + logConfig.tag = tag; + // 返回日志配置 + return logConfig; + } + + // ================ + // = 错误日志处理 = + // ================ + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param filePath 保存路径 + 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String filePath) { + return saveErrorLog(ex, null, null, filePath); + } + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String head, final String bottom, final String filePath) { + if (TextUtils.isEmpty(filePath)) return false; + try { + File file = new File(filePath); + // 判断是否属于文件夹 + if (file.isDirectory()) { + // 进行保存 + return saveErrorLog(ex, head, bottom, filePath, System.currentTimeMillis() + ".log"); + } else { + // 获取文件名 + String fileName = file.getName(); + // 重新裁剪 + String tempPath = filePath.substring(0, filePath.length() - fileName.length()); + // 进行保存 + return saveErrorLog(ex, head, bottom, tempPath, fileName); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "saveErrorLog"); + } + return false; + } + + // = + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String filePath, final String fileName) { + return saveErrorLog(ex, null, null, filePath, fileName); + } + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String head, final String bottom, final String filePath, final String fileName) { + if (TextUtils.isEmpty(filePath)) { + return false; + } else if (TextUtils.isEmpty(fileName)) { + return false; + } + return Utils.saveErrorLog(ex, head, bottom, filePath, fileName, true); + } + + // ============ + // = 日志处理 = + // ============ + + /** + * 保存日志 + * @param log 日志信息 + * @param filePath 保存路径 + 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String filePath) { + return saveLog(log, null, null, filePath); + } + + /** + * 保存日志 + * @param log 日志信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String head, final String bottom, final String filePath) { + if (TextUtils.isEmpty(filePath)) return false; + try { + File file = new File(filePath); + // 判断是否属于文件夹 + if (file.isDirectory()) { + // 进行保存 + return saveLog(log, head, bottom, filePath, System.currentTimeMillis() + ".log"); + } else { + // 获取文件名 + String fileName = file.getName(); + // 重新裁剪 + String tempPath = filePath.substring(0, filePath.length() - fileName.length()); + // 进行保存 + return saveLog(log, head, bottom, tempPath, fileName); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "saveLog"); + } + return false; + } + + // = + + /** + * 保存日志 + * @param log 日志信息 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String filePath, final String fileName) { + return saveLog(log, null, null, filePath, fileName); + } + + /** + * 保存日志 + * @param log 日志信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String head, final String bottom, + final String filePath, final String fileName) { + if (TextUtils.isEmpty(filePath)) { + return false; + } else if (TextUtils.isEmpty(fileName)) { + return false; + } + return Utils.saveLog(log, head, bottom, filePath, fileName, true); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/logger/IPrinter.java b/lib/DevApp/src/main/java/dev/utils/app/logger/IPrinter.java new file mode 100644 index 0000000000..48b9262acf --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/logger/IPrinter.java @@ -0,0 +1,195 @@ +package dev.utils.app.logger; + +/** + * detail: 日志接口 + * @author Ttt + */ +public interface IPrinter { + + // ============ + // = 配置方法 = + // ============ + + /** + * 使用单次其他日志配置 + * @param logConfig 日志配置 + * @return {@link IPrinter} + */ + IPrinter other(LogConfig logConfig); + + /** + * 获取日志配置信息 + * @return {@link LogConfig} 日志配置 + */ + LogConfig getLogConfig(); + + /** + * 初始化日志配置信息 ( 使用默认配置 ) + * @return {@link LogConfig} 日志配置 + */ + LogConfig init(); + + /** + * 自定义日志配置信息 + * @param logConfig 日志配置 + */ + void init(LogConfig logConfig); + + // =============================== + // = 使用默认 TAG - 日志打印方法 = + // =============================== + + /** + * 打印 Log.DEBUG + * @param message 日志信息 + * @param args 格式化参数 + */ + void d(String message, Object... args); + + /** + * 打印 Log.ERROR + * @param message 日志信息 + * @param args 格式化参数 + */ + void e(String message, Object... args); + + /** + * 打印 Log.ERROR + * @param throwable 异常 + */ + void e(Throwable throwable); + + /** + * 打印 Log.ERROR + * @param throwable 异常 + * @param message 日志信息 + * @param args 格式化参数 + */ + void e(Throwable throwable, String message, Object... args); + + /** + * 打印 Log.WARN + * @param message 日志信息 + * @param args 格式化参数 + */ + void w(String message, Object... args); + + /** + * 打印 Log.INFO + * @param message 日志信息 + * @param args 格式化参数 + */ + void i(String message, Object... args); + + /** + * 打印 Log.VERBOSE + * @param message 日志信息 + * @param args 格式化参数 + */ + void v(String message, Object... args); + + /** + * 打印 Log.ASSERT + * @param message 日志信息 + * @param args 格式化参数 + */ + void wtf(String message, Object... args); + + // = + + /** + * 格式化 JSON 格式数据, 并打印 + * @param json JSON 格式字符串 + */ + void json(String json); + + /** + * 格式化 XML 格式数据, 并打印 + * @param xml XML 格式字符串 + */ + void xml(String xml); + + // ================================= + // = 使用自定义 TAG - 日志打印方法 = + // ================================= + + /** + * 打印 Log.DEBUG + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void dTag(String tag, String message, Object... args); + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void eTag(String tag, String message, Object... args); + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param throwable 异常 + */ + void eTag(String tag, Throwable throwable); + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param throwable 异常 + * @param message 日志信息 + * @param args 格式化参数 + */ + void eTag(String tag, Throwable throwable, String message, Object... args); + + /** + * 打印 Log.WARN + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void wTag(String tag, String message, Object... args); + + /** + * 打印 Log.INFO + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void iTag(String tag, String message, Object... args); + + /** + * 打印 Log.VERBOSE + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void vTag(String tag, String message, Object... args); + + /** + * 打印 Log.ASSERT + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void wtfTag(String tag, String message, Object... args); + + // = + + /** + * 格式化 JSON 格式数据, 并打印 + * @param tag 日志 TAG + * @param json JSON 格式字符串 + */ + void jsonTag(String tag, String json); + + /** + * 格式化 XML 格式数据, 并打印 + * @param tag 日志 TAG + * @param xml XML 格式字符串 + */ + void xmlTag(String tag, String xml); +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/logger/LogConfig.java b/lib/DevApp/src/main/java/dev/utils/app/logger/LogConfig.java new file mode 100644 index 0000000000..59a869f61d --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/logger/LogConfig.java @@ -0,0 +1,43 @@ +package dev.utils.app.logger; + +/** + * detail: 日志配置类 + * @author Ttt + */ +public class LogConfig { + + /** + * 堆栈方法总数 ( 显示经过的方法 ) = 默认 3 + */ + public int methodCount = LogConstants.DEFAULT_LOG_METHOD_COUNT; + + /** + * 堆栈方法索引偏移 (0 = 最新经过调用的方法信息, 偏移则往上推, 如 1 = 倒数第二条经过调用的方法信息 ) = 默认 0 + */ + public int methodOffset = LogConstants.DEFAULT_LOG_METHOD_OFFSET; + + /** + * 是否输出全部方法 ( 在特殊情况下, 如想要打印全部经过的方法, 但是不知道经过的总数 ) = 默认 false + */ + public boolean outputMethodAll = LogConstants.JUDGE_OUTPUT_METHOD_ALL; + + /** + * 显示日志线程信息 ( 特殊情况, 显示经过的线程信息, 具体情况如上 ) = 默认 false + */ + public boolean displayThreadInfo = LogConstants.JUDGE_DISPLAY_THREAD_LOG; + + /** + * 是否排序日志 ( 格式化 ) = 默认 false + */ + public boolean sortLog = LogConstants.JUDGE_SORT_LOG; + + /** + * 日志级别 - 默认异常级别 ( 只有 e, wtf 才进行显示 ) + */ + public LogLevel logLevel = LogConstants.DEFAULT_LOG_LEVEL; + + /** + * 设置 TAG ( 特殊情况使用, 不使用全部的 TAG 时, 如单独输出在某个 TAG 下 ) + */ + public String tag = LogConstants.DEFAULT_LOG_TAG; +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/logger/LogConstants.java b/lib/DevApp/src/main/java/dev/utils/app/logger/LogConstants.java new file mode 100644 index 0000000000..7060494ca3 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/logger/LogConstants.java @@ -0,0 +1,86 @@ +package dev.utils.app.logger; + +/** + * detail: 日志常量类 + * @author Ttt + */ +final class LogConstants { + + // ================ + // = 日志配置常量 = + // ================ + + /** + * 判断是否排序日志 + */ + public static final boolean JUDGE_SORT_LOG = false; + + /** + * 判断是否输出全部方法 - 异常的全部方法 + */ + public static final boolean JUDGE_OUTPUT_METHOD_ALL = false; + + /** + * 判断是否显示日志线程信息 + */ + public static final boolean JUDGE_DISPLAY_THREAD_LOG = false; + + /** + * 默认的日志 TAG + */ + public static final String DEFAULT_LOG_TAG = DevLogger.class.getSimpleName(); + + /** + * 默认输出方法数量 + */ + public static final int DEFAULT_LOG_METHOD_COUNT = 3; + + /** + * 默认方法索引偏移 + */ + public static final int DEFAULT_LOG_METHOD_OFFSET = 0; + + /** + * 默认日志级别 - 异常级别 ( 只有 e, wtf 才进行显示 ) + */ + public static final LogLevel DEFAULT_LOG_LEVEL = LogLevel.ERROR; + + // ================ + // = 日志配置信息 = + // ================ + + /** + * Android 一个日志条目最大限制为 4076 字节, 设置 4000 字节作为块的大小从默认字符集是 UTF-8 + *
+     *     Android's max limit for a log entry is ~4076 bytes,
+     *     so 4000 bytes is used as chunk size since default charset is UTF-8
+     * 
+ */ + public static final int CHUNK_SIZE = 4000; + + /** + * JSON 格式内容缩进 + */ + public static final int JSON_INDENT = 4; + + /** + * 最小堆栈跟踪索引 + */ + public static final int MIN_STACK_OFFSET = 3; + + // ======================= + // = 绘制日志格式 - 字符 = + // ======================= + + public static final char TOP_LEFT_CORNER = '╔'; + public static final char BOTTOM_LEFT_CORNER = '╚'; + public static final char MIDDLE_CORNER = '╟'; + public static final char HORIZONTAL_DOUBLE_LINE = '║'; + public static final String DOUBLE_DIVIDER = "═══════"; + public static final String SINGLE_DIVIDER = "───────"; + + public static final String TOP_BORDER = TOP_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER; + public static final String BOTTOM_BORDER = BOTTOM_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER; + public static final String MIDDLE_BORDER = MIDDLE_CORNER + SINGLE_DIVIDER + SINGLE_DIVIDER; + +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/logger/LogLevel.java b/lib/DevApp/src/main/java/dev/utils/app/logger/LogLevel.java new file mode 100644 index 0000000000..7e6d6bc0d7 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/logger/LogLevel.java @@ -0,0 +1,40 @@ +package dev.utils.app.logger; + +/** + * detail: 日志级别 + * @author Ttt + *
+ *     Log.v 输出颜色为黑色, 任何消息都会输出, 这里的 v 代表 verbose 啰嗦的意思, 平时使用就是 Log.v
+ *     Log.d 输出颜色为蓝色, 仅输出 debug 调试的意思, 但他会输出上层的信息, 过滤起来可以通过 DDMS 的 Logcat 标签来选择
+ *     Log.i 输出颜色为绿色, 一般提示性的消息 information, 它不会输出 Log.v 和 Log.d 的信息, 但会显示 i、w 和 e 的信息
+ *     Log.w 输出颜色为橙色, 可以看作为 warning 警告, 一般需要我们注意优化 Android 代码, 同时选择它后还会输出 Log.e 的信息
+ *     Log.e 输出颜色为红色, 可以想到 error 错误, 这里仅显示红色的错误信息, 这些错误就需要我们认真的分析, 查看栈的信息了
+ * 
+ */ +public enum LogLevel { + + /** + * 全部不打印 + */ + NONE, + + /** + * 调试级别 v, d - 全部打印 + */ + DEBUG, + + /** + * 正常级别 i + */ + INFO, + + /** + * 警告级别 w + */ + WARN, + + /** + * 异常级别 e, wtf + */ + ERROR +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/logger/LoggerPrinter.java b/lib/DevApp/src/main/java/dev/utils/app/logger/LoggerPrinter.java new file mode 100644 index 0000000000..6558c58525 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/logger/LoggerPrinter.java @@ -0,0 +1,872 @@ +package dev.utils.app.logger; + +import android.text.TextUtils; +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.StringReader; +import java.io.StringWriter; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * detail: 日志输出类 ( 处理方法 ) + * @author Ttt + */ +final class LoggerPrinter implements IPrinter { + + // 日志配置 + private static LogConfig LOG_CONFIG = null; + // 每个线程的日志配置信息 + private static final ThreadLocal LOCAL_LOG_CONFIGS = new ThreadLocal<>(); + + // ==================================== + // = 实现 IPrinter 接口, 对外公开方法 = + // ==================================== + + /** + * 使用单次其他日志配置 + * @param logConfig 日志配置 + * @return {@link IPrinter} + */ + @Override + public IPrinter other(final LogConfig logConfig) { + if (logConfig != null) { + LOCAL_LOG_CONFIGS.set(logConfig); + } + return this; + } + + /** + * 获取日志配置信息 + * @return {@link LogConfig} 日志配置 + */ + @Override + public LogConfig getLogConfig() { + return LOG_CONFIG; + } + + /** + * 初始化日志配置信息 ( 使用默认配置 ) + * @return {@link LogConfig} 日志配置 + */ + @Override + public LogConfig init() { + // 判断日志配置信息是否等于 null + if (LOG_CONFIG == null) { + // 生成默认配置信息 + LOG_CONFIG = new LogConfig(); + } + // 返回配置信息 + return LOG_CONFIG; + } + + /** + * 自定义日志配置信息 + * @param logConfig 日志配置 + */ + @Override + public void init(final LogConfig logConfig) { + LOG_CONFIG = logConfig; + // 防止日志配置参数为 null + init(); + } + + // =============================== + // = 使用默认 TAG - 日志打印方法 = + // =============================== + + /** + * 打印 Log.DEBUG + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void d(final String message, final Object... args) { + logHandle(Log.DEBUG, message, args); + } + + /** + * 打印 Log.ERROR + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void e(final String message, final Object... args) { + e(null, message, args); + } + + /** + * 打印 Log.ERROR + * @param throwable 异常 + */ + @Override + public void e(final Throwable throwable) { + e(throwable, null); + } + + /** + * 打印 Log.ERROR + * @param throwable 异常 + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void e(final Throwable throwable, final String message, final Object... args) { + // 日志消息 + String logMsg = message; + // 判断消息 + if (throwable != null && message != null) { + logMsg = message + " : " + throwable.toString(); + } else if (throwable != null && message == null) { + logMsg = throwable.toString(); + } else if (message == null) { + // 没有日志信息, 也没有异常信息传入 + logMsg = "No message/exception is set"; + } + logHandle(Log.ERROR, logMsg, args); + } + + /** + * 打印 Log.WARN + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void w(final String message, final Object... args) { + logHandle(Log.WARN, message, args); + } + + /** + * 打印 Log.INFO + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void i(final String message, final Object... args) { + logHandle(Log.INFO, message, args); + } + + /** + * 打印 Log.VERBOSE + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void v(final String message, final Object... args) { + logHandle(Log.VERBOSE, message, args); + } + + /** + * 打印 Log.ASSERT + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void wtf(final String message, final Object... args) { + logHandle(Log.ASSERT, message, args); + } + + // = + + /** + * 格式化 JSON 格式数据, 并打印 + * @param json JSON 格式字符串 + */ + @Override + public void json(final String json) { + // 获取当前线程日志配置信息 + LogConfig logConfig = getThreadLogConfig(); + // 判断是否打印日志 ( 日志级别 ) + if (!isPrintLog(logConfig, Log.DEBUG)) { + return; + } + // 日志 TAG + String tag = logConfig.tag; + // 判断传入 JSON 格式信息是否为 null + if (TextUtils.isEmpty(json)) { + logHandle(logConfig, tag, Log.DEBUG, "Empty/Null json content"); + return; + } + try { + // 属于对象的 JSON 格式信息 + if (json.startsWith("{")) { + JSONObject jsonObject = new JSONObject(json); + // 进行缩进 + String message = jsonObject.toString(LogConstants.JSON_INDENT); + // 打印信息 + logHandle(logConfig, tag, Log.DEBUG, message); + } else if (json.startsWith("[")) { + // 属于数据的 JSON 格式信息 + JSONArray jsonArray = new JSONArray(json); + // 进行缩进 + String message = jsonArray.toString(LogConstants.JSON_INDENT); + // 打印信息 + logHandle(logConfig, tag, Log.DEBUG, message); + } else { + // 打印信息 + logHandle(logConfig, tag, Log.DEBUG, "json content format error"); + } + } catch (Exception e) { + String errorInfo = "null"; + if (e != null) { + Throwable throwable = e.getCause(); + if (throwable != null) { + errorInfo = throwable.toString(); + } else { + try { + errorInfo = e.toString(); + } catch (Exception e1) { + errorInfo = e1.toString(); + } + } + } + logHandle(logConfig, tag, Log.ERROR, errorInfo + "\n" + json); + } + } + + /** + * 格式化 XML 格式数据, 并打印 + * @param xml XML 格式字符串 + */ + @Override + public void xml(final String xml) { + // 获取当前线程日志配置信息 + LogConfig logConfig = getThreadLogConfig(); + // 判断是否打印日志 ( 日志级别 ) + if (!isPrintLog(logConfig, Log.DEBUG)) { + return; + } + // 日志 TAG + String tag = logConfig.tag; + // 判断传入 XML 格式信息是否为 null + if (TextUtils.isEmpty(xml)) { + logHandle(logConfig, tag, Log.DEBUG, "Empty/Null xml content"); + return; + } + try { + Source xmlInput = new StreamSource(new StringReader(xml)); + StreamResult xmlOutput = new StreamResult(new StringWriter()); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.transform(xmlInput, xmlOutput); + // 获取打印消息 + String message = xmlOutput.getWriter().toString().replaceFirst(">", ">\n"); + // 打印信息 + logHandle(logConfig, tag, Log.DEBUG, message); + } catch (Exception e) { + String errorInfo = "null"; + if (e != null) { + Throwable throwable = e.getCause(); + if (throwable != null) { + errorInfo = throwable.toString(); + } else { + try { + errorInfo = e.toString(); + } catch (Exception e1) { + errorInfo = e1.toString(); + } + } + } + logHandle(logConfig, tag, Log.ERROR, errorInfo + "\n" + xml); + } + } + + // ================================= + // = 使用自定义 TAG - 日志打印方法 = + // ================================= + + /** + * 打印 Log.DEBUG + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void dTag(final String tag, final String message, final Object... args) { + logHandle(tag, Log.DEBUG, message, args); + } + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void eTag(final String tag, final String message, final Object... args) { + eTag(tag, null, message, args); + } + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param throwable 异常 + */ + @Override + public void eTag(final String tag, final Throwable throwable) { + eTag(tag, throwable, null); + } + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param throwable 异常 + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void eTag(final String tag, final Throwable throwable, final String message, final Object... args) { + // 日志消息 + String logMsg = message; + // 判断消息 + if (throwable != null && message != null) { + logMsg = message + " : " + throwable.toString(); + } else if (throwable != null && message == null) { + logMsg = throwable.toString(); + } else if (message == null) { + // 没有日志信息, 也没有异常信息传入 + logMsg = "No message/exception is set"; + } + logHandle(tag, Log.ERROR, logMsg, args); + } + + /** + * 打印 Log.WARN + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void wTag(final String tag, final String message, final Object... args) { + logHandle(tag, Log.WARN, message, args); + } + + /** + * 打印 Log.INFO + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void iTag(final String tag, final String message, final Object... args) { + logHandle(tag, Log.INFO, message, args); + } + + /** + * 打印 Log.VERBOSE + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void vTag(final String tag, final String message, final Object... args) { + logHandle(tag, Log.VERBOSE, message, args); + } + + /** + * 打印 Log.ASSERT + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + @Override + public void wtfTag(final String tag, final String message, final Object... args) { + logHandle(tag, Log.ASSERT, message, args); + } + + // = + + /** + * 格式化 JSON 格式数据, 并打印 + * @param tag 日志 TAG + * @param json JSON 格式字符串 + */ + @Override + public void jsonTag(final String tag, final String json) { + // 获取当前线程日志配置信息 + LogConfig logConfig = getThreadLogConfig(); + // 判断是否打印日志 ( 日志级别 ) + if (!isPrintLog(logConfig, Log.DEBUG)) { + return; + } + // 判断传入 JSON 格式信息是否为 null + if (TextUtils.isEmpty(json)) { + logHandle(logConfig, tag, Log.DEBUG, "Empty/Null json content"); + return; + } + try { + // 属于对象的 JSON 格式信息 + if (json.startsWith("{")) { + JSONObject jsonObject = new JSONObject(json); + // 进行缩进 + String message = jsonObject.toString(LogConstants.JSON_INDENT); + // 打印信息 + logHandle(logConfig, tag, Log.DEBUG, message); + } else if (json.startsWith("[")) { + // 属于数据的 JSON 格式信息 + JSONArray jsonArray = new JSONArray(json); + // 进行缩进 + String message = jsonArray.toString(LogConstants.JSON_INDENT); + // 打印信息 + logHandle(logConfig, tag, Log.DEBUG, message); + } else { + // 打印信息 + logHandle(logConfig, tag, Log.DEBUG, "json content format error"); + } + } catch (Exception e) { + String errorInfo = "null"; + if (e != null) { + Throwable throwable = e.getCause(); + if (throwable != null) { + errorInfo = throwable.toString(); + } else { + try { + errorInfo = e.toString(); + } catch (Exception e1) { + errorInfo = e1.toString(); + } + } + } + logHandle(logConfig, tag, Log.ERROR, errorInfo + "\n" + json); + } + } + + /** + * 格式化 XML 格式数据, 并打印 + * @param tag 日志 TAG + * @param xml XML 格式字符串 + */ + @Override + public void xmlTag(final String tag, final String xml) { + // 获取当前线程日志配置信息 + LogConfig logConfig = getThreadLogConfig(); + // 判断是否打印日志 ( 日志级别 ) + if (!isPrintLog(logConfig, Log.DEBUG)) { + return; + } + // 判断传入 XML 格式信息是否为 null + if (TextUtils.isEmpty(xml)) { + logHandle(logConfig, tag, Log.DEBUG, "Empty/Null xml content"); + return; + } + try { + Source xmlInput = new StreamSource(new StringReader(xml)); + StreamResult xmlOutput = new StreamResult(new StringWriter()); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.transform(xmlInput, xmlOutput); + // 获取打印消息 + String message = xmlOutput.getWriter().toString().replaceFirst(">", ">\n"); + // 打印信息 + logHandle(logConfig, tag, Log.DEBUG, message); + } catch (Exception e) { + String errorInfo = "null"; + if (e != null) { + Throwable throwable = e.getCause(); + if (throwable != null) { + errorInfo = throwable.toString(); + } else { + try { + errorInfo = e.toString(); + } catch (Exception e1) { + errorInfo = e1.toString(); + } + } + } + logHandle(logConfig, tag, Log.ERROR, errorInfo + "\n" + xml); + } + } + + // ================ + // = 内部判断方法 = + // ================ + + /** + * 是否打印日志 + * @param logConfig 日志配置 + * @param logType 日志类型 + * @return {@code true} yes, {@code false} no + */ + private boolean isPrintLog(final LogConfig logConfig, final int logType) { + // 是否打印日志 - 默认不打印 + boolean isPrint = false; + // 日志级别 + LogLevel logLevel = logConfig.logLevel; + // = + switch (logLevel) { + case NONE: // 全部不打印 + break; + case DEBUG: // 调试级别 v, d - 全部打印 + isPrint = true; + break; + case INFO: // 正常级别 i + case WARN: // 警告级别 w + case ERROR: // 异常级别 e, wtf + isPrint = checkLogLevel(logLevel, logType); + break; + default: + break; + } + return isPrint; + } + + /** + * 判断日志级别是否允许输出 + * @param logLevel 日志级别 + * @param logType 日志类型 + * @return {@code true} yes, {@code false} no + */ + private boolean checkLogLevel(final LogLevel logLevel, final int logType) { + switch (logLevel) { + case INFO: // 正常级别 i + if (logType != Log.VERBOSE && logType != Log.DEBUG) { + return true; + } + break; + case WARN: // 警告级别 w + if (logType != Log.VERBOSE && logType != Log.DEBUG && logType != Log.INFO) { + return true; + } + break; + case ERROR: // 异常级别 e, wtf + if (logType == Log.ERROR || logType == Log.ASSERT) { + return true; + } + break; + default: + break; + } + return false; + } + + // ==================== + // = 打印日志处理方法 = + // ==================== + + /** + * 最终打印方法 + * @param logType 日志类型 + * @param tag 日志 TAG + * @param message 日志信息 + */ + private void finalLogPrinter(final int logType, final String tag, final String message) { + // 防止 null 处理 + if (message == null) return; + // 获取日志类型 + switch (logType) { + case Log.VERBOSE: + Log.v(tag, message); + break; + case Log.DEBUG: + Log.d(tag, message); + break; + case Log.INFO: + Log.i(tag, message); + break; + case Log.WARN: + Log.w(tag, message); + break; + case Log.ERROR: + Log.e(tag, message); + break; + case Log.ASSERT: + Log.wtf(tag, message); + break; + default: // 默认使用, 自定义级别 + Log.wtf(tag, message); + break; + } + } + + /** + * 日志处理方法 + * @param logType 日志类型 + * @param message 日志信息 + * @param args 占位符替换 + */ + private void logHandle(final int logType, final String message, final Object... args) { + logHandle(null, null, logType, message, args); + } + + /** + * 日志处理方法 + * @param tag 日志 TAG + * @param logType 日志类型 + * @param message 日志信息 + * @param args 占位符替换 + */ + private void logHandle(final String tag, final int logType, final String message, final Object... args) { + logHandle(null, tag, logType, message, args); + } + + /** + * 日志处理方法 - 此方法是同步的, 以避免混乱的日志的顺序 + * @param config 配置信息 + * @param tag 日志 TAG + * @param logType 日志类型 + * @param msg 日志信息 + * @param args 占位符替换 + */ + private synchronized void logHandle(final LogConfig config, final String tag, final int logType, final String msg, final Object... args) { + LogConfig logConfig = config; + // 如果配置为 null, 才进行获取 + if (logConfig == null) { + // 获取当前线程日志配置信息 + logConfig = getThreadLogConfig(); + } + // 判断是否打印日志 ( 日志级别 ) + if (!isPrintLog(logConfig, logType)) { + return; + } + String logTag = tag; + // 防止 TAG 为 null + if (TextUtils.isEmpty(logTag)) { + // 获取配置的 TAG + logTag = logConfig.tag; + // 防止配置的 TAG 也为 null + if (TextUtils.isEmpty(logTag)) { + // 使用默认的 TAG + logTag = LogConstants.DEFAULT_LOG_TAG; + } + } + // 判断是否显示排序后的日志 ( 如果不排序, 则显示默认 ) + if (!logConfig.sortLog) { + finalLogPrinter(logType, logTag, createMessage(msg, args)); + return; + } + // = 日志配置信息获取 = + // 获取方法总数 + int methodCount = logConfig.methodCount; + // 获取方法偏移索引 + int methodOffset = logConfig.methodOffset; + // 如果出现小于 0 的设置, 则设置默认值处理 + if (methodOffset < 0) { + methodOffset = LogConstants.DEFAULT_LOG_METHOD_OFFSET; + } + // 如果出现小于 0 的设置, 则设置默认值处理 + if (methodCount < 0) { + methodCount = LogConstants.DEFAULT_LOG_METHOD_COUNT; + } + // 获取打印的日志信息 + String message = createMessage(msg, args); + // 防止 null 处理 + if (message == null) return; + // 打印头部 + logTopBorder(logType, logTag); + // 打印头部线程信息 + logHeaderContent(logConfig, logType, logTag, methodCount, methodOffset); + // 获取系统的默认字符集的信息字节 (UTF-8) + byte[] bytes = message.getBytes(); + // 获取字节总数 + int length = bytes.length; + // 判断是否超过总数, 没有超过则一次性打印, 超过则遍历打印 + if (length <= LogConstants.CHUNK_SIZE) { + if (methodCount > 0) { + logDivider(logType, logTag); + } + // 打印日志内容 + logContent(logType, logTag, message); + // 打印结尾 + logBottomBorder(logType, logTag); + return; + } + // 打印换行符 + if (methodCount > 0) { + // 换行 + logDivider(logType, logTag); + } + // 因为超过系统打印字节总数, 遍历打印 + for (int i = 0; i < length; i += LogConstants.CHUNK_SIZE) { + int count = Math.min(length - i, LogConstants.CHUNK_SIZE); + // 创建系统的默认字符集的一个新的字符串 (UTF-8), 并打印日志内容 + logContent(logType, logTag, new String(bytes, i, count)); + } + // 打印结尾 + logBottomBorder(logType, logTag); + } + + // ================ + // = 日志格式拼接 = + // ================ + + /** + * 日志线程信息主体部分 + * @param logConfig 日志配置 + * @param logType 日志类型 + * @param tag 日志 TAG + * @param methodCount 方法总数 + * @param methodOffset 方法偏移索引 + */ + private void logHeaderContent(final LogConfig logConfig, final int logType, final String tag, int methodCount, int methodOffset) { + StackTraceElement[] trace = Thread.currentThread().getStackTrace(); + // 判断是否显示日志线程信息 + if (logConfig.displayThreadInfo) { + // 打印线程信息 ( 线程名 ) + finalLogPrinter(logType, tag, LogConstants.HORIZONTAL_DOUBLE_LINE + " Thread: " + Thread.currentThread().getName()); + // 进行换行 + logDivider(logType, tag); + } else { + // 不打印线程信息, 都设置为 0 + methodCount = methodOffset = 0; + return; + } + // 手动进行偏移 + String level = ""; + // 堆栈总数 + int traceCount = trace.length; + // 获取堆栈偏移量 + int stackOffset = getStackOffset(trace) + methodOffset; + // 对应的方法计数与当前堆栈可能超过, 进行堆栈跟踪 + if (methodCount + stackOffset > traceCount) { + methodCount = traceCount - stackOffset - 1; + } + // 判断是否显示全部方法 + if (logConfig.outputMethodAll) { + // 设置方法总数 + methodCount = traceCount; + // 设置方法偏移索引为 0 + stackOffset = 0; + } else if (methodCount <= 0) { + // 如果打印数小于等于 0, 则直接跳过 + return; + } + // 遍历打印的方法数量 ( 类名、行数、操作的方法名 ) + for (int i = methodCount; i > 0; i--) { + int stackIndex = i + stackOffset; + if (stackIndex >= traceCount) { + continue; + } + // 拼接中间内容、操作的类名、行数、方法名等信息 + StringBuilder builder = new StringBuilder(); + builder.append("║ "); + builder.append(level); + builder.append(getSimpleClassName(trace[stackIndex].getClassName())); + builder.append(".").append(trace[stackIndex].getMethodName()); + builder.append(" ("); + builder.append(trace[stackIndex].getFileName()); + builder.append(":"); + builder.append(trace[stackIndex].getLineNumber()); + builder.append(")"); + level += " "; + // 打印日志信息 + finalLogPrinter(logType, tag, builder.toString()); + } + } + + /** + * 日志顶部 + * @param logType 日志类型 + * @param tag 日志 TAG + */ + private void logTopBorder(final int logType, final String tag) { + finalLogPrinter(logType, tag, LogConstants.TOP_BORDER); + } + + /** + * 日志结尾 + * @param logType 日志类型 + * @param tag 日志 TAG + */ + private void logBottomBorder(final int logType, final String tag) { + finalLogPrinter(logType, tag, LogConstants.BOTTOM_BORDER); + } + + /** + * 日志换行 + * @param logType 日志类型 + * @param tag 日志 TAG + */ + private void logDivider(final int logType, final String tag) { + finalLogPrinter(logType, tag, LogConstants.MIDDLE_BORDER); + } + + /** + * 日志内容 + * @param logType 日志类型 + * @param tag 日志 TAG + * @param msg 日志信息 + */ + private void logContent(final int logType, final String tag, final String msg) { + String[] lines = msg.split(System.getProperty("line.separator")); + for (String line : lines) { + finalLogPrinter(logType, tag, LogConstants.HORIZONTAL_DOUBLE_LINE + " " + line); + } + } + + /** + * 处理信息 + * @param message 日志信息 + * @param args 占位符替换 + * @return 处理 ( 格式化 ) 后准备打印的日志信息 + */ + private String createMessage(final String message, final Object... args) { + if (message != null) { + try { + return args.length == 0 ? message : String.format(message, args); + } catch (Exception e) { + } + } + return "message is null"; + } + + // ================ + // = 获取堆栈信息 = + // ================ + + /** + * 获取类名 + * @param name 类.class + * @return ClassName + */ + private String getSimpleClassName(final String name) { + int lastIndex = name.lastIndexOf("."); + return name.substring(lastIndex + 1); + } + + /** + * 确定该类的方法调用后的堆栈跟踪的起始索引 + * @param trace 堆栈 + * @return 堆栈跟踪索引 + */ + private int getStackOffset(final StackTraceElement[] trace) { + for (int i = LogConstants.MIN_STACK_OFFSET, len = trace.length; i < len; i++) { + StackTraceElement e = trace[i]; + String name = e.getClassName(); + if (!name.equals(LoggerPrinter.class.getName()) && !name.equals(DevLogger.class.getName())) { + return --i; + } + } + return -1; + } + + // ================ + // = 日志配置获取 = + // ================ + + /** + * 返回对应线程的日志配置信息 + * @return {@link LogConfig} 日志配置 + */ + private LogConfig getThreadLogConfig() { + // 获取当前线程的日志配置信息 + LogConfig logConfig = LOCAL_LOG_CONFIGS.get(); + // 如果等于 null, 则返回默认配置信息 + if (logConfig == null) { + return init(); + } else { + LOCAL_LOG_CONFIGS.remove(); + } + // 如果存在当前线程的配置信息, 则返回 + return logConfig; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/logger/Utils.java b/lib/DevApp/src/main/java/dev/utils/app/logger/Utils.java new file mode 100644 index 0000000000..272a06516a --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/logger/Utils.java @@ -0,0 +1,494 @@ +package dev.utils.app.logger; + +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.text.TextUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 内部快捷操作工具类 ( 便于单独提取 Logger, 不依赖其他工具类 ) + * @author Ttt + *
+ *     代码与 FileRecordUtils ( 文件记录工具类 ) 完全一致
+ * 
+ */ +final class Utils { + + private Utils() { + } + + // 日志 TAG + private static final String TAG = Utils.class.getSimpleName(); + + // ============ + // = 配置信息 = + // ============ + + // APP 版本 ( 如 1.0.01) 显示给用户看的 + private static String APP_VERSION_NAME = ""; + // android:versionCode 整数值, 代表应用程序代码的相对版本 + private static String APP_VERSION_CODE = ""; + // 应用包名 + private static String PACKAGE_NAME = ""; + // 设备信息 + private static String DEVICE_INFO_STR = null; + // 设备信息存储 Map + private static Map DEVICE_INFO_MAPS = new HashMap<>(); + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + // 换行字符串 ( 两行 ) + private static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 初始化调用方法 ( 内部已调用 ) + */ + public static void init() { + // 如果版本信息为 null, 才进行处理 + if (TextUtils.isEmpty(APP_VERSION_CODE) || TextUtils.isEmpty(APP_VERSION_NAME)) { + // 获取 APP 版本信息 + String[] versions = getAppVersion(); + // 防止为 null + if (versions != null && versions.length == 2) { + // 保存 APP 版本信息 + APP_VERSION_NAME = versions[0]; + APP_VERSION_CODE = versions[1]; + } + } + + // 获取包名 + if (TextUtils.isEmpty(PACKAGE_NAME)) { + try { + PACKAGE_NAME = DevUtils.getContext().getPackageName(); + } catch (Exception e) { + } + } + + // 判断是否存在设备信息 + if (DEVICE_INFO_MAPS.size() == 0) { + // 获取设备信息 + getDeviceInfo(DEVICE_INFO_MAPS); + // 转换字符串 + handlerDeviceInfo(""); + } + } + + // ============ + // = 异常日志 = + // ============ + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String filePath, final String fileName) { + return saveErrorLog(ex, null, null, filePath, fileName, true); + } + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String head, final String bottom, + final String filePath, final String fileName) { + return saveErrorLog(ex, head, bottom, filePath, fileName, true); + } + + // = + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @param printDevice 是否打印设备信息 + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String filePath, final String fileName, final boolean printDevice) { + return saveErrorLog(ex, null, null, filePath, fileName, printDevice); + } + + /** + * 保存异常日志 + * @param ex 错误信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @param printDevice 是否打印设备信息 + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveErrorLog(final Throwable ex, final String head, final String bottom, + final String filePath, final String fileName, final boolean printDevice) { + return saveLog(getThrowableStackTrace(ex, "failed to get exception information"), + head, bottom, filePath, fileName, printDevice); + } + + // ============ + // = 正常日志 = + // ============ + + /** + * 保存日志 + * @param log 日志信息 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String filePath, final String fileName) { + return saveLog(log, null, null, filePath, fileName, true); + } + + /** + * 保存日志 + * @param log 日志信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String head, final String bottom, + final String filePath, final String fileName) { + return saveLog(log, head, bottom, filePath, fileName, true); + } + + // = + + /** + * 保存日志 + * @param log 日志信息 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @param printDevice 是否打印设备信息 + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String filePath, final String fileName, final boolean printDevice) { + return saveLog(log, null, null, filePath, fileName, printDevice); + } + + /** + * 保存日志 + * @param log 日志信息 + * @param head 顶部标题 + * @param bottom 底部内容 + * @param filePath 保存路径 + * @param fileName 文件名 ( 含后缀 ) + * @param printDevice 是否打印设备信息 + * @return {@code true} 保存成功, {@code false} 保存失败 + */ + public static boolean saveLog(final String log, final String head, final String bottom, + final String filePath, final String fileName, final boolean printDevice) { + // 日志拼接 + StringBuilder builder = new StringBuilder(); + // 如果存在顶部内容, 则进行添加 + if (!TextUtils.isEmpty(head)) { + builder.append(head); + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + } + // 保存 APP 信息 + builder.append("date: " + getDateNow()); + builder.append(NEW_LINE_STR); + builder.append("versionName: " + APP_VERSION_NAME); + builder.append(NEW_LINE_STR); + builder.append("versionCode: " + APP_VERSION_CODE); + builder.append(NEW_LINE_STR); + builder.append("package: " + PACKAGE_NAME); + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + // 如果需要打印设备信息 + if (printDevice) { + // 设备信息 + String deviceInfo = handlerDeviceInfo("failed to get device information"); + // 保存设备信息 + builder.append(deviceInfo); + builder.append(NEW_LINE_STR); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + } + // 保存日志信息 + builder.append(log); + // 如果存在顶部内容, 则进行添加 + if (!TextUtils.isEmpty(bottom)) { + builder.append(NEW_LINE_STR_X2); + builder.append("==========================="); + builder.append(NEW_LINE_STR_X2); + builder.append(bottom); + } + // 保存日志到文件 + return saveFile(filePath, fileName, builder.toString()); + } + + // ================ + // = 设备信息处理 = + // ================ + + /** + * 处理设备信息 + * @param errorInfo 错误提示信息, 如获取设备信息失败 + * @return 拼接后的设备信息字符串 + */ + private static String handlerDeviceInfo(final String errorInfo) { + // 如果不为 null, 则直接返回之前的信息 + if (!TextUtils.isEmpty(DEVICE_INFO_STR)) { + return DEVICE_INFO_STR; + } + // 设备信息 + String deviceInfo = handlerDeviceInfo(DEVICE_INFO_MAPS, null); + // 如果为 null + if (deviceInfo == null) { + return errorInfo; + } + // 保存设备信息 + DEVICE_INFO_STR = deviceInfo; + // 返回设备信息 + return DEVICE_INFO_STR; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============= + // = FileUtils = + // ============= + + /** + * 保存文件 + * @param filePath 保存路径 + * @param fileName 文件名. 后缀 + * @param content 保存内容 + * @return {@code true} success, {@code false} fail + */ + private static boolean saveFile(final String filePath, final String fileName, final String content) { + if (filePath != null && fileName != null && content != null) { + try { + // 防止文件没创建 + createFolder(filePath); + // 保存路径 + File file = new File(filePath, fileName); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + fos.write(content.getBytes()); + fos.close(); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param dirPath 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + private static boolean createFolder(final String dirPath) { + return createFolder(getFileByPath(dirPath)); + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param file 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + private static boolean createFolder(final File file) { + if (file != null) { + try { + // 当这个文件夹不存在的时候则创建文件夹 + if (!file.exists()) { + // 允许创建多级目录 + return file.mkdirs(); + // 这个无法创建多级目录 + // rootFile.mkdir(); + } + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createFolder"); + } + } + return false; + } + + // ================== + // = ThrowableUtils = + // ================== + + /** + * 获取异常栈信息 + * @param throwable 异常 + * @param errorInfo 获取失败返回字符串 + * @return 异常栈信息字符串 + */ + private static String getThrowableStackTrace(final Throwable throwable, final String errorInfo) { + if (throwable != null) { + PrintWriter printWriter = null; + try { + Writer writer = new StringWriter(); + printWriter = new PrintWriter(writer); + throwable.printStackTrace(printWriter); + return writer.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getThrowableStackTrace"); + return e.toString(); + } finally { + if (printWriter != null) { + try { + printWriter.close(); + } catch (Exception e) { + } + } + } + } + return errorInfo; + } + + // ============= + // = DateUtils = + // ============= + + /** + * 获取当前日期的字符串 + * @return 当前日期 yyyy-MM-dd HH:mm:ss 格式字符串 + */ + private static String getDateNow() { + try { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime()); + } catch (Exception e) { + } + return null; + } + + // =============== + // = DeviceUtils = + // =============== + + /** + * 获取设备信息 + * @param deviceInfoMap 设备信息 Map + */ + private static void getDeviceInfo(final Map deviceInfoMap) { + // 获取设备信息类的所有申明的字段, 即包括 public、private 和 proteced, 但是不包括父类的申明字段 + Field[] fields = Build.class.getDeclaredFields(); + // 遍历字段 + for (Field field : fields) { + try { + // 取消 Java 的权限控制检查 + field.setAccessible(true); + // 转换当前设备支持的 ABI - CPU 指令集 + if (field.getName().toLowerCase().startsWith("SUPPORTED".toLowerCase())) { + try { + Object object = field.get(null); + // 判断是否数组 + if (object instanceof String[]) { + if (object != null) { + // 获取类型对应字段的数据, 并保存支持的指令集 [arm64-v8a, armeabi-v7a, armeabi] + deviceInfoMap.put(field.getName(), Arrays.toString((String[]) object)); + } + continue; + } + } catch (Exception e) { + } + } + // 获取类型对应字段的数据, 并保存 + deviceInfoMap.put(field.getName(), field.get(null).toString()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); + } + } + } + + /** + * 处理设备信息 + * @param deviceInfoMap 设备信息 Map + * @param errorInfo 错误提示信息, 如获取设备信息失败 + * @return 拼接后的设备信息字符串 + */ + private static String handlerDeviceInfo(final Map deviceInfoMap, final String errorInfo) { + try { + // 初始化 Builder, 拼接字符串 + StringBuilder builder = new StringBuilder(); + // 获取设备信息 + Iterator> mapIter = deviceInfoMap.entrySet().iterator(); + // 遍历设备信息 + while (mapIter.hasNext()) { + // 获取对应的 key - value + Map.Entry rnEntry = mapIter.next(); + String rnKey = rnEntry.getKey(); // key + String rnValue = rnEntry.getValue(); // value + // 保存设备信息 + builder.append(rnKey); + builder.append(" = "); + builder.append(rnValue); + builder.append(NEW_LINE_STR); + } + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "handlerDeviceInfo"); + } + return errorInfo; + } + + // ================= + // = ManifestUtils = + // ================= + + /** + * 获取 APP 版本信息 + * @return 0 = versionName, 1 = versionCode + */ + private static String[] getAppVersion() { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_SIGNATURES); + if (packageInfo != null) { + String versionName = packageInfo.versionName == null ? "null" : packageInfo.versionName; + String versionCode = packageInfo.versionCode + ""; + return new String[]{versionName, versionCode}; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppVersion"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/permission/PermissionConstants.java b/lib/DevApp/src/main/java/dev/utils/app/permission/PermissionConstants.java new file mode 100644 index 0000000000..c0c03ed97a --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/permission/PermissionConstants.java @@ -0,0 +1,145 @@ +package dev.utils.app.permission; + +import android.Manifest; +import android.os.Build; +import android.support.annotation.StringDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * detail: 权限常量 + * @author Ttt + */ +public class PermissionConstants { + + // ==================== + // = permission_group = + // ==================== + + public static final String CALENDAR = Manifest.permission_group.CALENDAR; + public static final String CALL_LOG = Manifest.permission_group.CALL_LOG; + public static final String CAMERA = Manifest.permission_group.CAMERA; + public static final String CONTACTS = Manifest.permission_group.CONTACTS; + public static final String LOCATION = Manifest.permission_group.LOCATION; + public static final String MICROPHONE = Manifest.permission_group.MICROPHONE; + public static final String PHONE = Manifest.permission_group.PHONE; + public static final String SENSORS = Manifest.permission_group.SENSORS; + public static final String SMS = Manifest.permission_group.SMS; + public static final String STORAGE = Manifest.permission_group.STORAGE; + + // =================== + // = permission 数组 = + // =================== + + private static final String[] GROUP_CALENDAR = { + Manifest.permission.READ_CALENDAR, + Manifest.permission.WRITE_CALENDAR + }; + + private static final String[] GROUP_CALL_LOG = { + Manifest.permission.READ_CALL_LOG, + Manifest.permission.WRITE_CALL_LOG + }; + + private static final String[] GROUP_CAMERA = { + Manifest.permission.CAMERA + }; + + private static final String[] GROUP_CONTACTS = { + Manifest.permission.READ_CONTACTS, + Manifest.permission.WRITE_CONTACTS, + Manifest.permission.GET_ACCOUNTS + }; + + private static final String[] GROUP_LOCATION = { + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + }; + + private static final String[] GROUP_MICROPHONE = { + Manifest.permission.RECORD_AUDIO + }; + + private static final String[] GROUP_PHONE = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.READ_PHONE_NUMBERS, + Manifest.permission.CALL_PHONE, + Manifest.permission.READ_CALL_LOG, + Manifest.permission.WRITE_CALL_LOG, + Manifest.permission.ADD_VOICEMAIL, + Manifest.permission.USE_SIP, + Manifest.permission.PROCESS_OUTGOING_CALLS, + Manifest.permission.ANSWER_PHONE_CALLS + }; + + private static final String[] GROUP_PHONE_BELOW_O = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.READ_PHONE_NUMBERS, + Manifest.permission.CALL_PHONE, + Manifest.permission.READ_CALL_LOG, + Manifest.permission.WRITE_CALL_LOG, + Manifest.permission.ADD_VOICEMAIL, + Manifest.permission.USE_SIP, + Manifest.permission.PROCESS_OUTGOING_CALLS + }; + + private static final String[] GROUP_SENSORS = { + Manifest.permission.BODY_SENSORS + }; + + private static final String[] GROUP_SMS = { + Manifest.permission.SEND_SMS, + Manifest.permission.RECEIVE_SMS, + Manifest.permission.READ_SMS, + Manifest.permission.RECEIVE_WAP_PUSH, + Manifest.permission.RECEIVE_MMS, + }; + + private static final String[] GROUP_STORAGE = { + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + + // = + + @StringDef({CALENDAR, CALL_LOG, CAMERA, CONTACTS, LOCATION, MICROPHONE, PHONE, SENSORS, SMS, STORAGE,}) + @Retention(RetentionPolicy.SOURCE) + public @interface Permission { + } + + /** + * 获取权限组 + * @param permission 权限组类型 + * @return 权限组 + */ + public static String[] getPermissions(@Permission final String permission) { + switch (permission) { + case CALENDAR: + return GROUP_CALENDAR; + case CALL_LOG: + return GROUP_CALL_LOG; + case CAMERA: + return GROUP_CAMERA; + case CONTACTS: + return GROUP_CONTACTS; + case LOCATION: + return GROUP_LOCATION; + case MICROPHONE: + return GROUP_MICROPHONE; + case PHONE: + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return GROUP_PHONE_BELOW_O; + } else { + return GROUP_PHONE; + } + case SENSORS: + return GROUP_SENSORS; + case SMS: + return GROUP_SMS; + case STORAGE: + return GROUP_STORAGE; + } + return new String[]{permission}; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/permission/PermissionUtils.java b/lib/DevApp/src/main/java/dev/utils/app/permission/PermissionUtils.java new file mode 100644 index 0000000000..4211bba8e3 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/permission/PermissionUtils.java @@ -0,0 +1,504 @@ +package dev.utils.app.permission; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.text.TextUtils; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 权限请求工具类 + * @author Ttt + *
+ *     参考链接
+ *     @see 
+ *     

+ * 权限介绍 + * @see
+ *

+ * 第三方库 + * @see
PermissionsDispatcher + * @see RxPermissions + * @see Grant + *

+ * 使用方法: + * 第一种请求方式 + * PermissionUtils.permission("").callBack(null).request(Activity); + * 第二种请求方式 - 需要在 onRequestPermissionsResult 中通知调用 + * PermissionUtils.permission("").callBack(null).setRequestPermissionsResult(true).request(Activity); + *

+ * 刷新权限改变处理 ( 清空已拒绝的权限记录 ) + * notifyPermissionsChange() + *
+ */ +public final class PermissionUtils { + + // 日志 TAG + private static final String TAG = PermissionUtils.class.getSimpleName(); + // APP 注册的权限 + private static final Set sAppPermissionSets = getAppPermissionToSet(); + // 申请未通过的权限 - 永久拒绝 + private static final List sPermissionsDeniedForeverLists = new ArrayList<>(); + // 申请的权限 - 传入的权限参数 + private Set mPermissionSets = new HashSet<>(); + // 准备请求的权限 + private List mPermissionsRequestLists = new ArrayList<>(); + // 申请通过的权限 + private List mPermissionsGrantedLists = new ArrayList<>(); + // 申请未通过的权限 + private List mPermissionsDeniedLists = new ArrayList<>(); + // 查询不到的权限 ( 包含未注册 ) + private List mPermissionsNotFoundLists = new ArrayList<>(); + // 操作回调 + private PermissionCallBack mCallBack; + // 回调方法 + private Looper mLooper = Looper.getMainLooper(); + // 判断是否请求过 + private boolean mIsRequest = false; + // 是否需要在 Activity 的 onRequestPermissionsResult 回调中, 调用 PermissionUtils.onRequestPermissionsResult(this); + private boolean isRequestPermissionsResult = false; // 默认使用内部 PermissionActivity + // Permission 请求 Code + public static final int P_REQUEST_CODE = 10101; + + /** + * 构造函数 + * @param permissions 待申请权限 + */ + private PermissionUtils(final String... permissions) { + mPermissionSets.clear(); + // 防止数据为 null + if (permissions != null && permissions.length != 0) { + // 遍历全部需要申请的权限 + for (String permission : permissions) { + if (!TextUtils.isEmpty(permission)) { + mPermissionSets.add(permission); + } + } + } + } + + // ============ + // = 使用方法 = + // ============ + + /** + * 申请权限初始化 + * @param permissions 待申请权限 + * @return {@link PermissionUtils} + */ + public static PermissionUtils permission(final String... permissions) { + return new PermissionUtils(permissions); + } + + /** + * 设置回调方法 + * @param callBack {@link PermissionCallBack} + * @return {@link PermissionUtils} + */ + public PermissionUtils callBack(final PermissionCallBack callBack) { + if (mIsRequest) return this; + this.mCallBack = callBack; + return this; + } + + /** + * 设置是否需要在 Activity 的 onRequestPermissionsResult 回调中, 调用 PermissionUtils.onRequestPermissionsResult(this); + * @param requestPermissionsResult {@code true} yes, {@code false} no + * @return {@link PermissionUtils} + */ + public PermissionUtils setRequestPermissionsResult(final boolean requestPermissionsResult) { + if (mIsRequest) return this; + this.isRequestPermissionsResult = requestPermissionsResult; + return this; + } + + /** + * 请求权限 + * @param activity {@link Activity} + */ + public void request(final Activity activity) { + request(activity, P_REQUEST_CODE); + } + + /** + * 请求权限 + * @param activity {@link Activity} + * @param requestCode 请求 code + */ + public void request(final Activity activity, final int requestCode) { + if (checkPermissions(activity) == 1) { + // 如果 SDK 版本大于 23 才请求 + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + sInstance = this; + // 请求权限 + String[] permissions = mPermissionsRequestLists.toArray(new String[mPermissionsRequestLists.size()]); + // 判断请求方式 + if (this.isRequestPermissionsResult) { + // 请求权限 + ActivityCompat.requestPermissions(activity, permissions, requestCode); + } else { + // 自定义权限 Activity + PermissionUtils.PermissionActivity.start(activity); + } + } + } + } + + // ================ + // = 请求权限回调 = + // ================ + + /** + * detail: 权限请求回调 + * @author Ttt + */ + public interface PermissionCallBack { + + /** + * 授权通过权限回调 + */ + void onGranted(); + + /** + * 授权未通过权限回调 + * @param grantedList 申请通过的权限 + * @param deniedList 申请未通过的权限 + * @param notFoundList 查询不到的权限 ( 包含未注册 ) + */ + void onDenied(List grantedList, List deniedList, List notFoundList); + } + + // ================= + // = 内部 Activity = + // ================= + + // 内部持有对象 + private static PermissionUtils sInstance; + + /** + * detail: 请求权限 Activity + * @author Ttt + */ + @RequiresApi(api = Build.VERSION_CODES.M) + public static class PermissionActivity extends Activity { + + /** + * 跳转 PermissionActivity 请求权限 内部方法 + * @param context {@link Context} + */ + protected static void start(final Context context) { + Intent starter = new Intent(context, PermissionActivity.class); + starter.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(starter); + } + + /** + * PermissionActivity - onCreate 内部方法 + * @param savedInstanceState 关闭时存储数据 + */ + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // 请求权限 + int size = sInstance.mPermissionsRequestLists.size(); + requestPermissions(sInstance.mPermissionsRequestLists.toArray(new String[size]), 1); + } + + /** + * 请求权限回调 + * @param requestCode 请求 code + * @param permissions 请求权限 + * @param grantResults 权限授权结果 + */ + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + sInstance.onRequestPermissionsResultCommon(this); // 处理回调 + finish(); // 关闭当前页面 + } + } + + /** + * 请求回调权限回调处理 - 通用 + * @param activity {@link Activity} + */ + private void onRequestPermissionsResultCommon(final Activity activity) { + // 获取权限状态 + getPermissionsStatus(activity); + // 判断请求结果 + requestCallback(); + } + + // = + + /** + * 请求权限回调 - 需要在 Activity 的 onRequestPermissionsResult 回调中, 调用 PermissionUtils.onRequestPermissionsResult(this); + * @param activity {@link Activity} + */ + public static void onRequestPermissionsResult(final Activity activity) { + if (activity != null && sInstance != null) { + // 触发回调 + sInstance.onRequestPermissionsResultCommon(activity); + } + } + + /** + * 刷新权限改变处理 ( 清空已拒绝的权限记录 ) + */ + public static void notifyPermissionsChange() { + sPermissionsDeniedForeverLists.clear(); + } + + // ============ + // = 判断方法 = + // ============ + + /** + * 判断是否授予了权限 + * @param permissions 待判断权限 + * @return {@code true} yes, {@code false} no + */ + public static boolean isGranted(final String... permissions) { + // 防止数据为 null + if (permissions != null && permissions.length != 0) { + // 遍历全部需要申请的权限 + for (String permission : permissions) { + if (!isGranted(DevUtils.getContext(), permission)) { + return false; + } + } + return true; + } + return false; + } + + /** + * 判断是否授予了权限 + * @param context {@link Context} + * @param permission 待判断权限 + * @return {@code true} yes, {@code false} no + */ + private static boolean isGranted(final Context context, final String permission) { + if (context == null || permission == null) return false; + // SDK 版本小于 23 则表示直接通过 || 检查是否通过权限 + return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(context, permission); + } + + /** + * 是否拒绝了权限 - 拒绝过一次, 再次申请时, 弹出选择不再提醒并拒绝才会触发 true + * @param activity {@link Activity} + * @param permission 待判断权限 + * @return {@code true} yes, {@code false} no + */ + public static boolean shouldShowRequestPermissionRationale(final Activity activity, final String permission) { + if (activity == null || permission == null) return false; + return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission); + } + + // ================ + // = 内部处理方法 = + // ================ + + /** + * 权限判断处理 + * @param activity {@link Activity} + * @return -1 已经请求 ( 中 ) 过, 0 = 不处理 ( 通知回调 ), 1 = 需要请求 + */ + private int checkPermissions(final Activity activity) { + if (activity == null) { + // 处理请求回调 + requestCallback(); + // 不处理 + return 0; + } + if (mIsRequest) { + return -1; // 已经请求 ( 中 ) 过 + } + mIsRequest = true; + // 如果 SDK 版本小于 23 则直接通过 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + // 表示全部权限都通过 + mPermissionsGrantedLists.addAll(mPermissionSets); + // 处理请求回调 + requestCallback(); + } else { + for (String permission : mPermissionSets) { + // 首先判断是否存在 + if (sAppPermissionSets.contains(permission)) { + // 判断是否通过请求 + if (isGranted(activity, permission)) { + mPermissionsGrantedLists.add(permission); // 权限允许通过 + } else { + // 判断是否已拒绝 + if (!sPermissionsDeniedForeverLists.contains(permission)) { // 不存在, 则进行保存 + mPermissionsRequestLists.add(permission); // 准备请求权限 + } + } + } else { + // 保存到没找到的权限集合 + mPermissionsNotFoundLists.add(permission); + } + } + // 判断是否存在等待请求的权限 + if (mPermissionsRequestLists.isEmpty()) { + // 处理请求回调 + requestCallback(); + } else { // 表示需要申请 + return 1; + } + } + return 0; + } + + /** + * 内部请求回调, 统一处理方法 + */ + private void requestCallback() { + if (mCallBack != null) { + // 判断是否授权全部权限 + boolean isGrantedAll = (mPermissionSets.size() == mPermissionsGrantedLists.size()); + // 允许则触发回调 + if (isGrantedAll) { + new Handler(mLooper).post(new Runnable() { + @Override + public void run() { + mCallBack.onGranted(); + } + }); + } else { + new Handler(mLooper).post(new Runnable() { + @Override + public void run() { + mCallBack.onDenied(mPermissionsGrantedLists, mPermissionsDeniedLists, mPermissionsNotFoundLists); + } + }); + } + } + } + + /** + * 获取权限状态 + * @param activity {@link Activity} + */ + private void getPermissionsStatus(final Activity activity) { + for (String permission : mPermissionsRequestLists) { + // 判断是否通过请求 + if (isGranted(activity, permission)) { + mPermissionsGrantedLists.add(permission); + } else { + // 未授权 + mPermissionsDeniedLists.add(permission); + // 拒绝权限 + if (!shouldShowRequestPermissionRationale(activity, permission)) { + sPermissionsDeniedForeverLists.add(permission); + } + } + } + } + + // ============ + // = 静态方法 = + // ============ + + /** + * 获取全部权限 + * @return {@link Set} 全部权限 + */ + public static Set getAllPermissionToSet() { + Set permissionSets = new HashSet<>(); + Field[] fields = Manifest.permission.class.getFields(); + for (Field field : fields) { + try { + String name = (String) field.get(""); + permissionSets.add(name); + } catch (Exception e) { + } + } + return permissionSets; + } + + /** + * 获取全部权限 + * @return {@link List} 全部权限 + */ + public static List getAllPermissionToList() { + return new ArrayList<>(getAllPermissionToSet()); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = AppInfoUtils = + // ================ + + /** + * 获取 APP 注册的权限 + * @return APP 注册的权限 + */ + public static List getAppPermissionToList() { + return new ArrayList<>(getAppPermissionToSet()); + } + + /** + * 获取 APP 注册的权限 + * @return APP 注册的权限 + */ + public static Set getAppPermissionToSet() { + String[] permissions = getAppPermission(); + // 防止数据为 null + if (permissions != null && permissions.length != 0) { + Set permissionSets = new HashSet<>(); + for (String permission : permissions) { + permissionSets.add(permission); + } + return permissionSets; + } + return Collections.emptySet(); + } + + /** + * 获取 APP 注册的权限 + * @return APP 注册的权限数组 + */ + public static String[] getAppPermission() { + return getAppPermission(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 APP 注册的权限 + * @param packageName 应用包名 + * @return APP 注册的权限数组 + */ + public static String[] getAppPermission(final String packageName) { + try { + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); + return packageInfo.requestedPermissions; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppPermission"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/player/DevMediaManager.java b/lib/DevApp/src/main/java/dev/utils/app/player/DevMediaManager.java new file mode 100644 index 0000000000..152d0f2984 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/player/DevMediaManager.java @@ -0,0 +1,718 @@ +package dev.utils.app.player; + +import android.content.res.AssetFileDescriptor; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnBufferingUpdateListener; +import android.media.MediaPlayer.OnCompletionListener; +import android.media.MediaPlayer.OnErrorListener; +import android.media.MediaPlayer.OnPreparedListener; +import android.media.MediaPlayer.OnSeekCompleteListener; +import android.media.MediaPlayer.OnVideoSizeChangedListener; +import android.support.annotation.RawRes; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: MediaPlayer 统一管理类 + * @author Ttt + */ +public final class DevMediaManager implements OnBufferingUpdateListener, + OnCompletionListener, OnPreparedListener, OnVideoSizeChangedListener, + OnErrorListener, OnSeekCompleteListener { + + // 日志 TAG + private String TAG = DevMediaManager.class.getSimpleName(); + // MediaPlayer 对象 + private MediaPlayer mMediaPlayer; + // 单例实例 + private static DevMediaManager sInstance; + + private DevMediaManager() { + } + + /** + * 获取 DevMediaManager 实例 + * @return {@link DevMediaManager} + */ + public static DevMediaManager getInstance() { + if (sInstance == null) { + sInstance = new DevMediaManager(); + } + return sInstance; + } + + // ================ + // = 内部处理方法 = + // ================ + + /** + * 创建 MediaPlayer + */ + private void createMedia() { + // 销毁 MediaPlayer + destroyMedia(); + // 初始化 MediaPlayer + mMediaPlayer = new MediaPlayer(); + mMediaPlayer.reset(); + // 绑定事件 + bindListener(); + // 设置默认流类型 + setAudioStreamType(mStreamType); + } + + /** + * 销毁 MediaPlayer + */ + private void destroyMedia() { + try { + // 表示非播放状态 + if (mMediaPlayer != null) { + if (mMediaPlayer.isPlaying()) { + mMediaPlayer.stop(); // 停止播放 + } + mMediaPlayer.release(); // 释放资源 + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "destroyMedia"); + } + // 重置为 null + mMediaPlayer = null; + // 清空播放信息 + clearMediaPlayerData(); + } + + /** + * 绑定事件 + * @return {@code true} 绑定成功, {@code false} 绑定失败 + */ + private boolean bindListener() { + if (mMediaPlayer != null) { + // 播放结束回调 + mMediaPlayer.setOnBufferingUpdateListener(this); + // 播放结束回调 + mMediaPlayer.setOnCompletionListener(this); + // 预加载完成回调 + mMediaPlayer.setOnPreparedListener(this); + // 视频宽高大小改变回调 + mMediaPlayer.setOnVideoSizeChangedListener(this); + // 错误回调 + mMediaPlayer.setOnErrorListener(this); + // 滑动加载完成回调 + mMediaPlayer.setOnSeekCompleteListener(this); + return true; + } + return false; + } + + /** + * 设置流类型 + * @param streamType Audio streamType + * @return {@link DevMediaManager} + */ + public DevMediaManager setAudioStreamType(final int streamType) { + this.mStreamType = streamType; + // 防止为 null + if (mMediaPlayer != null) { + try { + // 播放流类型 + mMediaPlayer.setAudioStreamType(streamType); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setAudioStreamType"); + } + } + return this; + } + + // ============ + // = 播放操作 = + // ============ + + /** + * 播放 Raw 资源 + * @param rawId 播放资源 + * @return {@code true} 执行成功, {@code false} 执行失败 + */ + public boolean playPrepareRaw(@RawRes final int rawId) { + return playPrepareRaw(rawId, false); + } + + /** + * 播放 Raw 资源 + * @param rawId 播放资源 + * @param isLooping 是否循环播放 + * @return {@code true} 执行成功, {@code false} 执行失败 + */ + public boolean playPrepareRaw(@RawRes final int rawId, final boolean isLooping) { + try { + mPlayRawId = rawId; + mPlayUri = null; + // 预播放 + return playPrepare(new MediaSet() { + @Override + public void setMediaConfig(MediaPlayer mediaPlayer) throws Exception { + // 获取资源文件 + AssetFileDescriptor file = DevUtils.getContext().getResources().openRawResourceFd(rawId); + try { + // 设置播放路径 + mMediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); + } finally { + if (file != null) { + try { + file.close(); + } catch (Exception e) { + } + } + } + } + + @Override + public boolean isLooping() { + return isLooping; + } + }); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "playPrepareRaw"); + // 销毁资源 + destroyMedia(); + } + return false; + } + + // = + + /** + * 播放 Assets 资源 + * @param playUri 播放地址 + * @return {@code true} 执行成功, {@code false} 执行失败 + */ + public boolean playPrepareAssets(final String playUri) { + return playPrepareAssets(playUri, false); + } + + /** + * 播放 Assets 资源 + * @param playUri 播放地址 + * @param isLooping 是否循环播放 + * @return {@code true} 执行成功, {@code false} 执行失败 + */ + public boolean playPrepareAssets(final String playUri, final boolean isLooping) { + try { + mPlayRawId = -1; + if (playUri.startsWith("/")) { + mPlayUri = playUri; + } else { + mPlayUri = "/" + playUri; + } + + final String tempPlayUri = mPlayUri; + // 预播放 + return playPrepare(new MediaSet() { + @Override + public void setMediaConfig(MediaPlayer mediaPlayer) throws Exception { + // 获取资源文件 + AssetFileDescriptor file = DevUtils.getContext().getResources().getAssets().openNonAssetFd("assets" + tempPlayUri); + try { + // 设置播放路径 + mMediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); + } finally { + if (file != null) { + try { + file.close(); + } catch (Exception e) { + } + } + } + } + + @Override + public boolean isLooping() { + return isLooping; + } + }); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "playPrepareAssets - " + playUri); + // 销毁资源 + destroyMedia(); + } + return false; + } + + // ============ + // = 播放路径 = + // ============ + + /** + * 预加载播放 - (file-path or http/rtsp URL) http 资源、本地资源 + * @param playUri 播放地址 + * @return {@code true} 执行成功, {@code false} 执行失败 + */ + public boolean playPrepare(final String playUri) { + return playPrepare(playUri, false); + } + + /** + * 预加载播放 - (file-path or http/rtsp URL) http 资源、本地资源 + * @param playUri 播放地址 + * @param isLooping 是否循环播放 + * @return {@code true} 执行成功, {@code false} 执行失败 + */ + public boolean playPrepare(final String playUri, final boolean isLooping) { + try { + mPlayRawId = -1; + mPlayUri = playUri; + // 预播放 + return playPrepare(new MediaSet() { + @Override + public void setMediaConfig(MediaPlayer mediaPlayer) throws Exception { + mediaPlayer.setDataSource(playUri); + } + + @Override + public boolean isLooping() { + return isLooping; + } + }); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "playPrepare - playUri: " + playUri); + // 销毁资源 + destroyMedia(); + } + return false; + } + + // ================ + // = 最终调用方法 = + // ================ + + /** + * 预加载播放 ( 最终调用方法 ) - 加载成功触发 onPrepared, 该方法内调用 mMediaPlayer.start() + * @param mediaSet 播放设置 + * @return {@code true} 执行成功, {@code false} 执行失败 + */ + public boolean playPrepare(final MediaSet mediaSet) { + // 防止为 null + if (mediaSet == null) { + return false; + } + try { + // 初始化 MediaPlayer + createMedia(); + // 设置循环播放 + mMediaPlayer.setLooping(mediaSet.isLooping()); + // 设置播放音量 + if (mediaSet.getVolume() >= 0f) { + mMediaPlayer.setVolume(mediaSet.getVolume(), mediaSet.getVolume()); + } + // 设置播放路径 + mediaSet.setMediaConfig(mMediaPlayer); + // 异步加载 + mMediaPlayer.prepareAsync(); + return true; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "playPrepare"); + // 销毁资源 + destroyMedia(); + } + return false; + } + + // ==================== + // = MediaPlayer 操作 = + // ==================== + + /** + * 是否播放中 + * @return {@code true} yes, {@code false} no + */ + public boolean isPlaying() { + if (mMediaPlayer != null) { + return mMediaPlayer.isPlaying(); + } + return false; + } + + /** + * 暂停操作 + */ + public void pause() { + if (mMediaPlayer != null) { + mMediaPlayer.pause(); + } + } + + /** + * 停止操作 - 销毁 MediaPlayer + */ + public void stop() { + // 销毁 MediaPlayer + destroyMedia(); + } + + // ============ + // = 快捷操作 = + // ============ + + /** + * 是否忽略错误类型 + * @param errorWhat onError 方法回调 what + * @return {@code true} yes, {@code false} no + */ + public static boolean isIgnoreWhat(final int errorWhat) { + // 是否忽略 + boolean isIgnore = false; + switch (errorWhat) { + case -38: + case 1: + case 100: + case 700: + case 701: + case 800: + isIgnore = true; + break; + } + return isIgnore; + } + + // ============ + // = 回调事件 = + // ============ + + /** + * 播放出错回调 + * @param mp {@link MediaPlayer} + * @param what 异常 what + * @param extra 异常 extra + * @return {@code true} 处理异常, {@code false} 调用 OnCompletionListener + */ + @Override + public boolean onError(MediaPlayer mp, int what, int extra) { + LogPrintUtils.dTag(TAG, "onError - what: " + what + ", extra: " + extra); + // 触发回调 + if (mMeidaListener != null) { + return mMeidaListener.onError(what, extra); + } + return false; + } + + /** + * 视频大小改变通知 + * @param mp {@link MediaPlayer} + * @param width 宽度 + * @param height 高度 + */ + @Override + public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { + LogPrintUtils.dTag(TAG, "onVideoSizeChanged - width: " + width + ", height: " + height); + mVideoWidth = width; + mVideoHeight = height; + // 触发回调 + if (mMeidaListener != null) { + mMeidaListener.onVideoSizeChanged(width, height); + } + } + + /** + * 使用 mMediaPlayer.prepareAsync() 异步播放准备成功回调 + * @param mp {@link MediaPlayer} + */ + @Override + public void onPrepared(MediaPlayer mp) { + LogPrintUtils.dTag(TAG, "onPrepared"); + // 触发回调 + if (mMeidaListener != null) { + mMeidaListener.onPrepared(); + } + } + + /** + * 视频播放结束回调 + * @param mp {@link MediaPlayer} + */ + @Override + public void onCompletion(MediaPlayer mp) { + LogPrintUtils.dTag(TAG, "onCompletion"); + // 触发回调 + if (mMeidaListener != null) { + mMeidaListener.onCompletion(); + } + } + + /** + * MediaPlayer 缓冲更新回调 + * @param mp {@link MediaPlayer} + * @param percent 缓冲百分比进度 + */ + @Override + public void onBufferingUpdate(MediaPlayer mp, int percent) { + LogPrintUtils.dTag(TAG, "onBufferingUpdate - percent: " + percent); + // 触发回调 + if (mMeidaListener != null) { + mMeidaListener.onBufferingUpdate(percent); + } + } + + /** + * 滑动加载完成回调 + * @param mp {@link MediaPlayer} + */ + @Override + public void onSeekComplete(MediaPlayer mp) { + LogPrintUtils.dTag(TAG, "onSeekComplete"); + // 触发回调 + if (mMeidaListener != null) { + mMeidaListener.onSeekComplete(); + } + } + + // ================ + // = 封装回调事件 = + // ================ + + // MediaPlayer 回调事件 + private MediaListener mMeidaListener; + + /** + * detail: MediaPlayer 回调接口 + * @author Ttt + */ + public interface MediaListener { + + /** + * 使用 mMediaPlayer.prepareAsync() 异步播放准备成功回调 + */ + void onPrepared(); + + /** + * 视频播放结束回调 + */ + void onCompletion(); + + /** + * MediaPlayer 缓冲更新回调 + * @param percent 缓冲百分比进度 + */ + void onBufferingUpdate(int percent); + + /** + * 滑动加载完成回调 + */ + void onSeekComplete(); + + /** + * 播放出错回调 + * @param what 异常 what + * @param extra 异常 extra + * @return {@code true} 处理异常, {@code false} 调用 OnCompletionListener + */ + boolean onError(int what, int extra); + + /** + * 视频大小改变通知 + * @param width 宽度 + * @param height 高度 + */ + void onVideoSizeChanged(int width, int height); + } + + /** + * 设置 MediaPlayer 回调事件 + * @param meidaListener {@link MediaListener} MediaPlayer 回调事件 + * @return {@link DevMediaManager} + */ + public DevMediaManager setMeidaListener(final MediaListener meidaListener) { + this.mMeidaListener = meidaListener; + return this; + } + + /** + * detail: Media 播放设置 + * @author Ttt + */ + public static abstract class MediaSet { + + /** + * 是否循环播放 - 默认不循环 + * @return {@code true} yes, {@code false} no + */ + public boolean isLooping() { + return false; + } + + /** + * 获取播放音量 ( 设置 ) - 默认使用全局统一音量 + * @return 播放音量 + */ + public float getVolume() { + return DevMediaManager.getInstance().getVolume(); + } + + /** + * 设置播放配置 + * @param mediaPlayer {@link MediaPlayer} + * @throws Exception 设置异常 + */ + public abstract void setMediaConfig(MediaPlayer mediaPlayer) throws Exception; + } + + // ================ + // = get/set 方法 = + // ================ + + /** + * 判断 MediaPlayer 是否为 null + * @return {@code true} yes, {@code false} no + */ + public boolean isNullMediaPlayer() { + return mMediaPlayer == null; + } + + /** + * 判断 MediaPlayer 是否不为 null + * @return {@code true} yes, {@code false} no + */ + public boolean isNotNullMediaPlayer() { + return mMediaPlayer != null; + } + + /** + * 获取 MediaPlayer 对象 + * @return {@link MediaPlayer} + */ + public MediaPlayer getMediaPlayer() { + return mMediaPlayer; + } + + /** + * 设置 MediaPlayer 对象 + * @param mediaPlayer {@link MediaPlayer} + * @return {@link DevMediaManager} + */ + public DevMediaManager setMediaPlayer(final MediaPlayer mediaPlayer) { + this.mMediaPlayer = mediaPlayer; + return this; + } + + /** + * 设置日志打印 TAG + * @param tag 日志 TAG + * @return {@link DevMediaManager} + */ + public DevMediaManager setTAG(final String tag) { + TAG = tag; + return this; + } + + /** + * 获取播放音量 + * @return 播放音量 + */ + public float getVolume() { + return mVolume; + } + + /** + * 设置播放音量 + * @param volume 播放音量 + * @return {@link DevMediaManager} + */ + public DevMediaManager setVolume(final float volume) { + this.mVolume = volume; + return this; + } + + // ============ + // = 内部变量 = + // ============ + + // 流类型 + private int mStreamType = AudioManager.STREAM_MUSIC; + // 本地资源 + private int mPlayRawId = -1; + // 播放路径 / 地址 + private String mPlayUri = null; + // 视频宽度 + private int mVideoWidth = 0; + // 视频高度 + private int mVideoHeight = 0; + // 播放音量 + private float mVolume = -1f; + + /** + * 清空播放信息 + */ + private void clearMediaPlayerData() { + mPlayRawId = -1; + mPlayUri = null; + mVideoWidth = 0; + mVideoHeight = 0; + } + + /** + * 获取播放资源 id + * @return 播放资源 id + */ + public int getPlayRawId() { + return mPlayRawId; + } + + /** + * 获取播放地址 + * @return 播放地址 + */ + public String getPlayUri() { + return mPlayUri; + } + + /** + * 获取视频宽度 + * @return 视频宽度 + */ + public int getVideoWidth() { + return mVideoWidth; + } + + /** + * 获取视频高度 + * @return 视频高度 + */ + public int getVideoHeight() { + return mVideoHeight; + } + + /** + * 获取播放时间 + * @return 播放时间 + */ + public int getCurrentPosition() { + if (mMediaPlayer != null) { + return mMediaPlayer.getCurrentPosition(); + } + return 0; + } + + /** + * 获取资源总时间 + * @return 资源总时间 + */ + public int getDuration() { + if (mMediaPlayer != null) { + return mMediaPlayer.getDuration(); + } + return 0; + } + + /** + * 获取播放进度百分比 + * @return 播放进度百分比 + */ + public int getPlayPercent() { + try { + return (getCurrentPosition() * 100) / getDuration(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPlayPercent"); + } + return 0; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/player/DevVideoPlayerControl.java b/lib/DevApp/src/main/java/dev/utils/app/player/DevVideoPlayerControl.java new file mode 100644 index 0000000000..ff3a1ac8ac --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/player/DevVideoPlayerControl.java @@ -0,0 +1,405 @@ +package dev.utils.app.player; + +import android.graphics.PixelFormat; +import android.media.MediaPlayer; +import android.text.TextUtils; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +import dev.utils.LogPrintUtils; + +/** + * detail: 视频播放控制器 + * @author Ttt + */ +public class DevVideoPlayerControl implements SurfaceHolder.Callback, + DevMediaManager.MediaListener { + + // ============ + // = 外部回调 = + // ============ + + // 日志 TAG + private final String TAG = DevVideoPlayerControl.class.getSimpleName(); + // 播放设置 + private DevMediaManager.MediaSet mMediaSet; + + // ======== + // = View = + // ======== + + // 播放预览载体 SurfaceView + private SurfaceView mSurfaceview; + // 画面预览回调 + private SurfaceHolder mSurfaceHolder; + // 判断是否自动播放 + private boolean mAutoPlay; + + /** + * 构造函数 + * @param surfaceview {@link SurfaceView} + */ + public DevVideoPlayerControl(final SurfaceView surfaceview) { + this(surfaceview, false); + } + + /** + * 构造函数 + * @param surfaceview {@link SurfaceView} + * @param autoPlay 是否自动播放 + */ + public DevVideoPlayerControl(final SurfaceView surfaceview, final boolean autoPlay) { + this.mSurfaceview = surfaceview; + this.mAutoPlay = autoPlay; + + // 初始化 DevMediaManager 回调事件类 + DevMediaManager.getInstance().setMeidaListener(this); + } + + // ================ + // = 内部快捷控制 = + // ================ + + /** + * 重置操作 + */ + private void resetOperate() { + // 移除旧的回调 + if (mSurfaceHolder != null) { + mSurfaceHolder.removeCallback(this); + } + // 设置 Holder + mSurfaceHolder = mSurfaceview.getHolder(); + // 移除旧的回调 + if (mSurfaceHolder != null) { + mSurfaceHolder.removeCallback(this); + } + // 添加回调 + mSurfaceHolder.addCallback(this); + } + + // ==================== + // = Surface 回调事件 = + // ==================== + + /** + * Surface 改变通知 + * @param holder {@link SurfaceHolder} + * @param format {@link PixelFormat} 像素格式 + * @param width 宽度 + * @param height 高度 + */ + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + LogPrintUtils.dTag(TAG, "surfaceChanged - format: " + format + ", width: " + width + ", height: " + height); + } + + /** + * Surface 创建 + * @param holder {@link SurfaceHolder} + */ + @Override + public void surfaceCreated(SurfaceHolder holder) { + LogPrintUtils.dTag(TAG, "surfaceCreated"); + try { + // 开始播放 + DevMediaManager.getInstance().playPrepare(mMediaSet); + // = + LogPrintUtils.dTag(TAG, "setDisplay(surfaceHolder) - Success"); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setDisplay(surfaceHolder) - Error"); + } + } + + /** + * Surface 销毁 + * @param holder {@link SurfaceHolder} + */ + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + LogPrintUtils.dTag(TAG, "surfaceDestroyed"); + } + + // ======================== + // = MediaPlayer 回调事件 = + // ======================== + + /** + * 准备完成回调 + */ + @Override + public void onPrepared() { + LogPrintUtils.dTag(TAG, "onPrepared"); + // = + if (mSurfaceview != null) { + // 如果等于 null, 或者不在显示中, 则跳过 + if (mSurfaceHolder.getSurface() == null || !mSurfaceHolder.getSurface().isValid()) + return; + + try { + MediaPlayer mPlayer = DevMediaManager.getInstance().getMediaPlayer(); + mPlayer.setDisplay(mSurfaceHolder); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "onPrepared"); + } + // 判断是否自动播放 + if (mAutoPlay) { + try { // 如果没有设置则直接播放 + DevMediaManager.getInstance().getMediaPlayer().start(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "onPrepared - start"); + } + } + // 触发回调 + if (mMediaListener != null) { + mMediaListener.onPrepared(); + } + } + } + + /** + * 播放完成 / 结束 + */ + @Override + public void onCompletion() { + LogPrintUtils.dTag(TAG, "onCompletion"); + // 触发回调 + if (mMediaListener != null) { + mMediaListener.onCompletion(); + } + } + + /** + * 缓存进度 + * @param percent 缓冲百分比进度 + */ + @Override + public void onBufferingUpdate(int percent) { + LogPrintUtils.dTag(TAG, "onBufferingUpdate: " + percent); + // 触发回调 + if (mMediaListener != null) { + mMediaListener.onBufferingUpdate(percent); + } + } + + /** + * 滑动进度加载成功 + */ + @Override + public void onSeekComplete() { + LogPrintUtils.dTag(TAG, "onSeekComplete"); + // 触发回调 + if (mMediaListener != null) { + mMediaListener.onSeekComplete(); + } + } + + /** + * 播放出错回调 + * @param what 异常 what + * @param extra 异常 extra + * @return {@code true} 处理异常, {@code false} 调用 OnCompletionListener + */ + @Override + public boolean onError(int what, int extra) { + LogPrintUtils.dTag(TAG, "onError - what: " + what + ", extra: " + extra); + // 触发回调 + if (mMediaListener != null) { + return mMediaListener.onError(what, extra); + } + return false; + } + + /** + * 视频大小改变通知 + * @param width 宽度 + * @param height 高度 + */ + @Override + public void onVideoSizeChanged(int width, int height) { + LogPrintUtils.dTag(TAG, "onVideoSizeChanged - width: " + width + ", height: " + height); + // 触发回调 + if (mMediaListener != null) { + mMediaListener.onVideoSizeChanged(width, height); + } + } + + // = + + // 播放事件监听 + private DevMediaManager.MediaListener mMediaListener; + + /** + * 设置播放监听事件 + * @param mediaListener {@link DevMediaManager.MediaListener} + * @return {@link DevVideoPlayerControl} + */ + public DevVideoPlayerControl setMediaListener(final DevMediaManager.MediaListener mediaListener) { + this.mMediaListener = mediaListener; + return this; + } + + // ================ + // = 播放快捷操作 = + // ================ + + /** + * 暂停播放 + */ + public void pausePlayer() { + DevMediaManager.getInstance().pause(); + } + + /** + * 停止播放 + */ + public void stopPlayer() { + DevMediaManager.getInstance().stop(); + } + + /** + * 开始播放 + * @param playUri 播放地址 + */ + public void startPlayer(final String playUri) { + startPlayer(playUri, false); + } + + /** + * 开始播放 + * @param playUri 播放地址 + * @param isLooping 是否循环播放 + */ + public void startPlayer(final String playUri, final boolean isLooping) { + // 设置播放信息 + this.mMediaSet = new DevMediaManager.MediaSet() { + @Override + public boolean isLooping() { + return isLooping; + } + + @Override + public void setMediaConfig(MediaPlayer mediaPlayer) throws Exception { + mediaPlayer.setDataSource(playUri); + } + }; + // 重置操作 + resetOperate(); + } + + /** + * 开始播放 + * @param mediaSet 播放设置 + */ + public void startPlayer(final DevMediaManager.MediaSet mediaSet) { + // 设置播放信息 + this.mMediaSet = mediaSet; + // 重置操作 + resetOperate(); + } + + /** + * 获取 SurfaceView + * @return {@link SurfaceView} + */ + public SurfaceView getSurfaceview() { + return mSurfaceview; + } + + // = + + /** + * 是否播放中 + * @return {@code true} yes, {@code false} no + */ + public boolean isPlaying() { + return DevMediaManager.getInstance().isPlaying(); + } + + /** + * 是否播放中 + * @param uri 播放地址 + * @return {@code true} yes, {@code false} no + */ + public boolean isPlaying(final String uri) { + if (!TextUtils.isEmpty(uri)) { // 需要播放的地址, 必须不等于 null + // 获取之前播放路径 + String playUri = DevMediaManager.getInstance().getPlayUri(); + // 如果不等于 null, 并且播放地址相同 + if (playUri != null && playUri.equals(uri)) { + try { + return DevMediaManager.getInstance().isPlaying(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isPlaying - uri: " + uri); + } + } + } + return isPlaying(); + } + + /** + * 判断是否自动播放 + * @return {@code true} yes, {@code false} no + */ + public boolean isAutoPlay() { + return mAutoPlay; + } + + /** + * 设置自动播放 + * @param autoPlay 是否自动播放 + * @return {@link DevVideoPlayerControl} + */ + public DevVideoPlayerControl setAutoPlay(final boolean autoPlay) { + this.mAutoPlay = autoPlay; + return this; + } + + /** + * 获取播放地址 + * @return 播放地址 + */ + public String getPlayUri() { + return DevMediaManager.getInstance().getPlayUri(); + } + + /** + * 获取视频宽度 + * @return 视频宽度 + */ + public int getVideoWidth() { + return DevMediaManager.getInstance().getVideoWidth(); + } + + /** + * 获取视频高度 + * @return 视频高度 + */ + public int getVideoHeight() { + return DevMediaManager.getInstance().getVideoHeight(); + } + + /** + * 获取播放时间 + * @return 播放时间 + */ + public int getCurrentPosition() { + return DevMediaManager.getInstance().getCurrentPosition(); + } + + /** + * 获取资源总时间 + * @return 资源总时间 + */ + public int getDuration() { + return DevMediaManager.getInstance().getDuration(); + } + + /** + * 获取播放进度百分比 + * @return 播放进度百分比 + */ + public int getPlayPercent() { + return DevMediaManager.getInstance().getPlayPercent(); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/share/IPreference.java b/lib/DevApp/src/main/java/dev/utils/app/share/IPreference.java new file mode 100644 index 0000000000..e9cd87b824 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/share/IPreference.java @@ -0,0 +1,149 @@ +package dev.utils.app.share; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * detail: SharedPreferences 操作接口 + * @author Ttt + */ +public interface IPreference { + + /** + * detail: 存储 / 取出 数据类型 + * @author Ttt + */ + enum DataType { + INTEGER, LONG, BOOLEAN, FLOAT, STRING, STRING_SET + } + + // = + + /** + * 保存数据 + * @param key 保存的 key + * @param value 保存的 value + * @param 泛型 + */ + void put(String key, T value); + + /** + * 保存 Map 集合 ( 只能是 Integer、Long、Boolean、Float、String、Set) + * @param map {@link Map} + * @param 泛型 + */ + void putAll(Map map); + + /** + * 保存 List 集合 + * @param key 保存的 key + * @param list 保存的 value + */ + void putAll(String key, List list); + + /** + * 保存 List 集合, 并且自定义保存顺序 + * @param key 保存的 key + * @param list 保存的 value + * @param comparator 排序 {@link Comparator} + */ + void putAll(String key, List list, Comparator comparator); + + /** + * 根据 key 获取数据 + * @param key 保存的 key + * @param type 数据类型 + * @param 泛型 + * @return 存储的数据 + */ + T get(String key, DataType type); + + /** + * 获取全部数据 + * @return 存储的数据 + */ + Map getAll(); + + /** + * 获取 List 集合 + * @param key 保存的 key + * @return 存储的数据 + */ + List getAll(String key); + + /** + * 移除数据 + * @param key 保存的 key + */ + void remove(String key); + + /** + * 移除集合的数据 + * @param keys 保存的 key 集合 + */ + void removeAll(List keys); + + /** + * 移除数组的数据 + * @param keys 保存的 key 数组 + */ + void removeAll(String[] keys); + + /** + * 是否存在 key + * @param key 保存的 key + * @return {@code true} yes, {@code false} no + */ + boolean contains(String key); + + /** + * 清除全部数据 + */ + void clear(); + + // = + + /** + * 获取 int 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + int getInt(String key); + + /** + * 获取 float 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + float getFloat(String key); + + /** + * 获取 long 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + long getLong(String key); + + /** + * 获取 boolean 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + boolean getBoolean(String key); + + /** + * 获取 String 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + String getString(String key); + + /** + * 获取 Set 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + Set getSet(String key); +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/share/IPreferenceHolder.java b/lib/DevApp/src/main/java/dev/utils/app/share/IPreferenceHolder.java new file mode 100644 index 0000000000..42985a54f0 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/share/IPreferenceHolder.java @@ -0,0 +1,73 @@ +package dev.utils.app.share; + +import android.content.Context; + +import java.util.HashMap; + +/** + * detail: IPreference 持有类, 内部返回实现类 + * @author Ttt + */ +class IPreferenceHolder { + + // HashMap 保存持有对象 + private static final HashMap sHashMaps = new HashMap<>(); + + /** + * 获取 IPreference (SharedPreferences) 操作接口 + * @param context {@link Context} + * @return {@link IPreference} + */ + public static IPreference getPreference(final Context context) { + // 判断是否存在对应的持有类 + IPreference ipref = sHashMaps.get(null); + // 判断是否存在 + if (ipref != null) { + return ipref; + } + // 初始化并保存 + ipref = new PreferenceImpl(context); + sHashMaps.put(null, ipref); + return ipref; + } + + /** + * 获取 IPreference (SharedPreferences) 操作接口 + * @param context {@link Context} + * @param fileName 文件名 + * @return {@link IPreference} + */ + public static IPreference getPreference(final Context context, final String fileName) { + // 判断是否存在对应的持有类 + IPreference ipref = sHashMaps.get(fileName); + // 判断是否存在 + if (ipref != null) { + return ipref; + } + // 初始化并保存 + ipref = new PreferenceImpl(context, fileName); + sHashMaps.put(fileName, ipref); + return ipref; + } + + /** + * 获取 IPreference (SharedPreferences) 操作接口 + * @param context {@link Context} + * @param fileName 文件名 + * @param mode SharedPreferences 操作模式 + * @return {@link IPreference} + */ + public static IPreference getPreference(final Context context, final String fileName, final int mode) { + String key = fileName + "_" + mode; + // 判断是否存在对应的持有类 + IPreference ipref = sHashMaps.get(key); + // 判断是否存在 + if (ipref != null) { + return ipref; + } + // 初始化并保存 + ipref = new PreferenceImpl(context, fileName, mode); + sHashMaps.put(key, ipref); + return ipref; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/share/PreferenceImpl.java b/lib/DevApp/src/main/java/dev/utils/app/share/PreferenceImpl.java new file mode 100644 index 0000000000..7c7a83a546 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/share/PreferenceImpl.java @@ -0,0 +1,330 @@ +package dev.utils.app.share; + +import android.content.Context; +import android.content.SharedPreferences; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * detail: SharedPreferences 操作接口具体实现类 + * @author Ttt + *
+ *     1.apply 没有返回值而 commit 返回 boolean 表明修改是否提交成功
+ *     2.apply 是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而 commit 是同步的提交到硬件磁盘
+ *     3.apply 方法不会提示任何失败的提示 apply 的效率高一些, 如果没有必要确认是否提交成功建议使用 apply
+ * 
+ */ +final class PreferenceImpl implements IPreference { + + // 文件名 + private static final String NAME = "SPConfig"; + // SharedPreferences 对象 + private SharedPreferences mPreferences; + + // ============ + // = 构造函数 = + // ============ + + /** + * 构造函数 + * @param context {@link Context} + */ + public PreferenceImpl(final Context context) { + mPreferences = context.getSharedPreferences(NAME, Context.MODE_PRIVATE); + } + + /** + * 构造函数 + * @param context {@link Context} + * @param fileName 文件名 + */ + public PreferenceImpl(final Context context, final String fileName) { + mPreferences = context.getSharedPreferences(fileName, Context.MODE_PRIVATE); + } + + /** + * 构造函数 + * @param context {@link Context} + * @param fileName 文件名 + * @param mode SharedPreferences 操作模式 + */ + public PreferenceImpl(final Context context, final String fileName, final int mode) { + mPreferences = context.getSharedPreferences(fileName, mode); + } + + // ============ + // = 内部方法 = + // ============ + + /** + * 保存数据 + * @param editor {@link SharedPreferences.Editor} + * @param key 保存的 key + * @param object 保存的 value + */ + private void put(final SharedPreferences.Editor editor, final String key, final Object object) { + // key 不为 null 时再存入, 否则不存储 + if (key != null && object != null) { + if (object instanceof Integer) { + editor.putInt(key, (Integer) object); + } else if (object instanceof Long) { + editor.putLong(key, (Long) object); + } else if (object instanceof Boolean) { + editor.putBoolean(key, (Boolean) object); + } else if (object instanceof Float) { + editor.putFloat(key, (Float) object); + } else if (object instanceof Set) { + editor.putStringSet(key, (Set) object); + } else if (object instanceof String) { + editor.putString(key, String.valueOf(object)); + } + } + } + + /** + * 根据 key 和 数据类型 取出数据 + * @param key 保存的 key + * @param type 数据类型 {@link DataType} + * @return 指定 key 存储的数据 ( 传入的 type 类型 ) + */ + private Object getValue(final String key, final DataType type) { + switch (type) { + case INTEGER: + return mPreferences.getInt(key, -1); + case FLOAT: + return mPreferences.getFloat(key, -1f); + case BOOLEAN: + return mPreferences.getBoolean(key, false); + case LONG: + return mPreferences.getLong(key, -1L); + case STRING: + return mPreferences.getString(key, null); + case STRING_SET: + return mPreferences.getStringSet(key, null); + default: + return null; + } + } + + /** + * detail: 默认比较器, 当存储 List 集合中的 String 类型数据时, 没有指定比较器, 就使用默认比较器 + * @author Ttt + */ + class ComparatorImpl implements Comparator { + @Override + public int compare(String lhs, String rhs) { + return lhs.compareTo(rhs); + } + } + + // ================ + // = 接口实现方法 = + // ================ + + /** + * 保存数据 + * @param key 保存的 key + * @param value 保存的 value + * @param 泛型 + */ + @Override + public void put(final String key, final T value) { + SharedPreferences.Editor edit = mPreferences.edit(); + put(edit, key, value); + edit.apply(); + } + + /** + * 保存 Map 集合 ( 只能是 Integer、Long、Boolean、Float、String、Set) + * @param map {@link Map} + * @param 泛型 + */ + @Override + public void putAll(final Map map) { + SharedPreferences.Editor edit = mPreferences.edit(); + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + put(edit, key, value); + } + edit.apply(); + } + + /** + * 保存 List 集合 + * @param key 保存的 key + * @param list 保存的 value + */ + @Override + public void putAll(final String key, final List list) { + putAll(key, list, new ComparatorImpl()); + } + + /** + * 保存 List 集合, 并且自定义保存顺序 + * @param key 保存的 key + * @param list 保存的 value + * @param comparator 排序 {@link Comparator} + */ + @Override + public void putAll(final String key, final List list, final Comparator comparator) { + Set set = new TreeSet<>(comparator); + for (String value : list) { + set.add(value); + } + mPreferences.edit().putStringSet(key, set).apply(); + } + + /** + * 根据 key 获取数据 + * @param key 保存的 key + * @param type 数据类型 + * @param 泛型 + * @return 存储的数据 + */ + @Override + public T get(final String key, final DataType type) { + return (T) getValue(key, type); + } + + /** + * 获取全部数据 + * @return 存储的数据 + */ + @Override + public Map getAll() { + return mPreferences.getAll(); + } + + /** + * 获取 List 集合 + * @param key 保存的 key + * @return 存储的数据 + */ + @Override + public List getAll(final String key) { + List list = new ArrayList<>(); + Set set = get(key, DataType.STRING_SET); + for (String value : set) { + list.add(value); + } + return list; + } + + /** + * 移除数据 + * @param key 保存的 key + */ + @Override + public void remove(final String key) { + mPreferences.edit().remove(key).apply(); + } + + /** + * 移除集合的数据 + * @param keys 保存的 key 集合 + */ + @Override + public void removeAll(final List keys) { + SharedPreferences.Editor edit = mPreferences.edit(); + for (String k : keys) { + edit.remove(k); + } + edit.apply(); + } + + /** + * 移除数组的数据 + * @param keys 保存的 key 数组 + */ + @Override + public void removeAll(final String[] keys) { + removeAll(Arrays.asList(keys)); + } + + /** + * 是否存在 key + * @param key 保存的 key + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean contains(final String key) { + return mPreferences.contains(key); + } + + /** + * 清除全部数据 + */ + @Override + public void clear() { + mPreferences.edit().clear().apply(); + } + + // = + + /** + * 获取 int 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + @Override + public int getInt(final String key) { + return get(key, DataType.INTEGER); + } + + /** + * 获取 float 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + @Override + public float getFloat(final String key) { + return get(key, DataType.FLOAT); + } + + /** + * 获取 long 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + @Override + public long getLong(final String key) { + return get(key, DataType.LONG); + } + + /** + * 获取 boolean 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + @Override + public boolean getBoolean(final String key) { + return get(key, DataType.BOOLEAN); + } + + /** + * 获取 String 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + @Override + public String getString(final String key) { + return get(key, DataType.STRING); + } + + /** + * 获取 Set 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + @Override + public Set getSet(final String key) { + return get(key, DataType.STRING_SET); + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/share/SPUtils.java b/lib/DevApp/src/main/java/dev/utils/app/share/SPUtils.java similarity index 76% rename from DevLibUtils/src/main/java/dev/utils/app/share/SPUtils.java rename to lib/DevApp/src/main/java/dev/utils/app/share/SPUtils.java index 7293f102a6..c422a1f7b6 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/share/SPUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/app/share/SPUtils.java @@ -2,10 +2,10 @@ /** * detail: SharedPreferences 工具类 - * Created by Ttt + * @author Ttt */ public final class SPUtils extends IPreferenceHolder { - private SPUtils(){ + private SPUtils() { } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/share/SharedUtils.java b/lib/DevApp/src/main/java/dev/utils/app/share/SharedUtils.java new file mode 100644 index 0000000000..5039b08f97 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/share/SharedUtils.java @@ -0,0 +1,196 @@ +package dev.utils.app.share; + +import android.content.Context; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * detail: SPUtils 工具类 ( 单独使用 ) + * @author Ttt + */ +public final class SharedUtils { + + private SharedUtils() { + } + + // 全局 Context + private static Context sContext; + + /** + * 初始化操作 ( 内部已调用 ) + * @param context {@link Context} + */ + public static void init(final Context context) { + if (sContext == null && context != null) { + // 初始化全局 Context + sContext = context.getApplicationContext(); + } + } + + // = + + /** + * 保存数据 + * @param key 保存的 key + * @param value 保存的 value + * @param 泛型 + */ + public static void put(final String key, final T value) { + SPUtils.getPreference(sContext).put(key, value); + } + + /** + * 保存 Map 集合 ( 只能是 Integer、Long、Boolean、Float、String、Set) + * @param map {@link Map} + * @param 泛型 + */ + public static void putAll(final Map map) { + SPUtils.getPreference(sContext).putAll(map); + } + + /** + * 保存 List 集合 + * @param key 保存的 key + * @param list 保存的 value + */ + public static void putAll(final String key, final List list) { + SPUtils.getPreference(sContext).putAll(key, list); + } + + /** + * 保存 List 集合, 并且自定义保存顺序 + * @param key 保存的 key + * @param list 保存的 value + * @param comparator 排序 {@link Comparator} + */ + public static void putAll(final String key, final List list, final Comparator comparator) { + SPUtils.getPreference(sContext).putAll(key, list, comparator); + } + + /** + * 根据 key 获取数据 + * @param key 保存的 key + * @param type 数据类型 + * @param 泛型 + * @return 存储的数据 + */ + public static T get(final String key, final IPreference.DataType type) { + return SPUtils.getPreference(sContext).get(key, type); + } + + /** + * 获取全部数据 + * @return 存储的数据 + */ + public static Map getAll() { + return SPUtils.getPreference(sContext).getAll(); + } + + /** + * 获取 List 集合 + * @param key 保存的 key + * @return 存储的数据 + */ + public static List getAll(final String key) { + return SPUtils.getPreference(sContext).getAll(key); + } + + /** + * 移除数据 + * @param key 保存的 key + */ + public static void remove(final String key) { + SPUtils.getPreference(sContext).remove(key); + } + + /** + * 移除集合的数据 + * @param keys 保存的 key 集合 + */ + public static void removeAll(final List keys) { + SPUtils.getPreference(sContext).removeAll(keys); + } + + /** + * 移除数组的数据 + * @param keys 保存的 key 数组 + */ + public static void removeAll(final String[] keys) { + SPUtils.getPreference(sContext).removeAll(keys); + } + + /** + * 是否存在 key + * @param key 保存的 key + * @return {@code true} yes, {@code false} no + */ + public static boolean contains(final String key) { + return SPUtils.getPreference(sContext).contains(key); + } + + /** + * 清除全部数据 + */ + public static void clear() { + SPUtils.getPreference(sContext).clear(); + } + + // = + + /** + * 获取 int 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + public static int getInt(final String key) { + return SPUtils.getPreference(sContext).getInt(key); + } + + /** + * 获取 float 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + public static float getFloat(final String key) { + return SPUtils.getPreference(sContext).getFloat(key); + } + + /** + * 获取 long 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + public static long getLong(final String key) { + return SPUtils.getPreference(sContext).getLong(key); + } + + /** + * 获取 boolean 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + public static boolean getBoolean(final String key) { + return SPUtils.getPreference(sContext).getBoolean(key); + } + + /** + * 获取 String 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + public static String getString(final String key) { + return SPUtils.getPreference(sContext).getString(key); + } + + /** + * 获取 Set 类型的数据 + * @param key 保存的 key + * @return 存储的数据 + */ + public static Set getSet(final String key) { + return SPUtils.getPreference(sContext).getSet(key); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/toast/ToastTintUtils.java b/lib/DevApp/src/main/java/dev/utils/app/toast/ToastTintUtils.java new file mode 100644 index 0000000000..53a84dc0fc --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/toast/ToastTintUtils.java @@ -0,0 +1,1575 @@ +package dev.utils.app.toast; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.NinePatchDrawable; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import java.lang.reflect.Field; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.R; + +/** + * detail: 自定义 View 着色美化 Toast 工具类 + * @author Ttt + *
+ *     支持子线程弹出 Toast, 可通过开关配置
+ *     内部解决 Android 7.1.1 崩溃问题
+ *     但无处理 部分 ROM 如魅族、小米、三星等关闭应用通知, 无法显示 Toast 问题
+ * 
+ */ +public final class ToastTintUtils { + + private ToastTintUtils() { + } + + // 日志 TAG + private static final String TAG = ToastTintUtils.class.getSimpleName(); + // Toast 判断过滤 + private static ToastTintUtils.Filter sToastFilter = null; + // 内部持有单个 Toast + private static Toast sToast = null; + // 判断是否使用 Handler + private static boolean sIsHandler = true; + // 内部 Handler + private static final Handler sHandler = new Handler(Looper.getMainLooper()); + // Null 值 - null 提示值 + private static String sNullText = null; + + // ============ + // = 部分配置 = + // ============ + + // 判断是否使用配置 + private static boolean sUseConfig = true; + // Toast 的重心、X、Y 轴偏移 + private static int sGravity, sX, sY; + // 水平边距、垂直边距 + private static float sHorizontalMargin, sVerticalMargin; + + // ============ + // = 样式相关 = + // ============ + + // 默认样式 + private static ToastTintUtils.Style sDefaultStyle = new DefaultStyle(); + // Normal 样式 + private static ToastTintUtils.Style sNormalStyle = new NormalStyle(); + // Info 样式 + private static ToastTintUtils.Style sInfoStyle = new InfoStyle(); + // Warning 样式 + private static ToastTintUtils.Style sWarningStyle = new WarningStyle(); + // Error 样式 + private static ToastTintUtils.Style sErrorStyle = new ErrorStyle(); + // Success 样式 + private static ToastTintUtils.Style sSuccessStyle = new SuccessStyle(); + // = + // info icon + private static Drawable sInfoDrawable = null; + // warning icon + private static Drawable sWarningDrawable = null; + // error icon + private static Drawable sErrorDrawable = null; + // Success icon + private static Drawable sSuccessDrawable = null; + + /** + * 重置默认参数 + */ + public static void reset() { + sIsHandler = true; + sUseConfig = true; + sNullText = null; + sGravity = sX = sY = 0; + sHorizontalMargin = sVerticalMargin = 0.0f; + } + + /** + * 设置 Toast 过滤器 + * @param toastFilter {@link ToastUtils.Filter} + */ + public static void setToastFilter(final ToastTintUtils.Filter toastFilter) { + ToastTintUtils.sToastFilter = toastFilter; + } + + /** + * 设置是否使用 Handler 显示 Toast + * @param isHandler {@code true} 使用, {@code false} 不使用 + */ + public static void setIsHandler(final boolean isHandler) { + ToastTintUtils.sIsHandler = isHandler; + } + + /** + * 设置 Text 为 null 的文本 + * @param nullText 显示内容为 null 时, 使用的提示值 + */ + public static void setNullText(final String nullText) { + ToastTintUtils.sNullText = nullText; + } + + /** + * 设置是否使用配置 + * @param useConfig {@code true} 使用, {@code false} 不使用 + */ + public static void setUseConfig(final boolean useConfig) { + ToastTintUtils.sUseConfig = useConfig; + } + + /** + * 设置 Toast 显示在屏幕上的位置 + * @param gravity 重心 + * @param xOffset X 轴偏移 + * @param yOffset Y 轴偏移 + */ + public static void setGravity(final int gravity, final int xOffset, final int yOffset) { + ToastTintUtils.sGravity = gravity; + ToastTintUtils.sX = xOffset; + ToastTintUtils.sY = yOffset; + } + + /** + * 设置边距 + * @param horizontalMargin 水平边距 + * @param verticalMargin 垂直边距 + */ + public static void setMargin(final float horizontalMargin, final float verticalMargin) { + ToastTintUtils.sHorizontalMargin = horizontalMargin; + ToastTintUtils.sVerticalMargin = verticalMargin; + } + + // = + + /** + * 获取默认样式 + * @return {@link Style} 默认样式 + */ + public static Style getDefaultStyle() { + return sDefaultStyle; + } + + /** + * 获取 Normal 样式 + * @return {@link Style} Normal 样式 + */ + public static Style getNormalStyle() { + return sNormalStyle; + } + + /** + * 获取 Info 样式 + * @return {@link Style} Info 样式 + */ + public static Style getInfoStyle() { + return sInfoStyle; + } + + /** + * 获取 Warning 样式 + * @return {@link Style} Warning 样式 + */ + public static Style getWarningStyle() { + return sWarningStyle; + } + + /** + * 获取 Error 样式 + * @return {@link Style} Error 样式 + */ + public static Style getErrorStyle() { + return sErrorStyle; + } + + /** + * 获取 Success 样式 + * @return {@link Style} Success 样式 + */ + public static Style getSuccessStyle() { + return sSuccessStyle; + } + + // = + + /** + * 设置 Normal 样式 + * @param normalStyle Normal 样式 + */ + public static void setNormalStyle(final Style normalStyle) { + ToastTintUtils.sNormalStyle = normalStyle; + } + + /** + * 设置 Info 样式 + * @param infoStyle Info 样式 + */ + public static void setInfoStyle(final Style infoStyle) { + ToastTintUtils.sInfoStyle = infoStyle; + } + + /** + * 设置 Warning 样式 + * @param warningStyle Warning 样式 + */ + public static void setWarningStyle(final Style warningStyle) { + ToastTintUtils.sWarningStyle = warningStyle; + } + + /** + * 设置 Error 样式 + * @param errorStyle Error 样式 + */ + public static void setErrorStyle(final Style errorStyle) { + ToastTintUtils.sErrorStyle = errorStyle; + } + + /** + * 设置 Success 样式 + * @param successStyle Success 样式 + */ + public static void setSuccessStyle(final Style successStyle) { + ToastTintUtils.sSuccessStyle = successStyle; + } + + /** + * 获取 Info 样式 icon + * @return {@link Drawable} Info 样式 icon + */ + public static Drawable getInfoDrawable() { + if (sInfoDrawable != null) { + return sInfoDrawable; + } + sInfoDrawable = getDrawable(DevUtils.getContext(), R.drawable.dev_toast_icon_info_white); + return sInfoDrawable; + } + + /** + * 获取 Warning 样式 icon + * @return {@link Drawable} Warning 样式 icon + */ + public static Drawable getWarningDrawable() { + if (sWarningDrawable != null) { + return sWarningDrawable; + } + sWarningDrawable = getDrawable(DevUtils.getContext(), R.drawable.dev_toast_icon_warning_white); + return sWarningDrawable; + } + + /** + * 获取 Error 样式 icon + * @return {@link Drawable} Error 样式 icon + */ + public static Drawable getErrorDrawable() { + if (sErrorDrawable != null) { + return sErrorDrawable; + } + sErrorDrawable = getDrawable(DevUtils.getContext(), R.drawable.dev_toast_icon_error_white); + return sErrorDrawable; + } + + /** + * 获取 Success 样式 icon + * @return {@link Drawable} Success 样式 icon + */ + public static Drawable getSuccessDrawable() { + if (sSuccessDrawable != null) { + return sSuccessDrawable; + } + sSuccessDrawable = getDrawable(DevUtils.getContext(), R.drawable.dev_toast_icon_success_white); + return sSuccessDrawable; + } + + // ===================== + // = 显示 normal Toast = + // ===================== + + /** + * normal 样式 Toast + * @param text Toast 提示文本 + */ + public static void normal(final String text) { + custom(true, null, sNormalStyle, text, Toast.LENGTH_SHORT, null); + } + + /** + * normal 样式 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void normal(final String text, final int duration) { + custom(true, null, sNormalStyle, text, duration, null); + } + + /** + * normal 样式 Toast + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void normal(final String text, final Drawable icon) { + custom(true, null, sNormalStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** + * normal 样式 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void normal(final String text, final int duration, final Drawable icon) { + custom(true, null, sNormalStyle, text, duration, icon); + } + + // = + + /** + * normal 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + */ + public static void normal(final boolean isSingle, final String text) { + custom(isSingle, null, sNormalStyle, text, Toast.LENGTH_SHORT, null); + } + + /** + * normal 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void normal(final boolean isSingle, final String text, final int duration) { + custom(isSingle, null, sNormalStyle, text, duration, null); + } + + /** + * normal 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void normal(final boolean isSingle, final String text, final Drawable icon) { + custom(isSingle, null, sNormalStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** + * normal 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void normal(final boolean isSingle, final String text, final int duration, final Drawable icon) { + custom(isSingle, null, sNormalStyle, text, duration, icon); + } + + // =================== + // = 显示 info Toast = + // =================== + + /** + * info 样式 Toast + * @param text Toast 提示文本 + */ + public static void info(final String text) { + custom(true, null, sInfoStyle, text, Toast.LENGTH_SHORT, getInfoDrawable()); + } + + /** + * info 样式 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void info(final String text, final int duration) { + custom(true, null, sInfoStyle, text, duration, getInfoDrawable()); + } + + /** + * info 样式 Toast + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void info(final String text, final Drawable icon) { + custom(true, null, sInfoStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** + * info 样式 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void info(final String text, final int duration, final Drawable icon) { + custom(true, null, sInfoStyle, text, duration, icon); + } + + // = + + /** + * info 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + */ + public static void info(final boolean isSingle, final String text) { + custom(isSingle, null, sInfoStyle, text, Toast.LENGTH_SHORT, getInfoDrawable()); + } + + /** + * info 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void info(final boolean isSingle, final String text, final int duration) { + custom(isSingle, null, sInfoStyle, text, duration, getInfoDrawable()); + } + + /** + * info 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void info(final boolean isSingle, final String text, final Drawable icon) { + custom(isSingle, null, sInfoStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** + * info 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void info(final boolean isSingle, final String text, final int duration, final Drawable icon) { + custom(isSingle, null, sInfoStyle, text, duration, icon); + } + + // ====================== + // = 显示 warning Toast = + // ====================== + + /** + * warning 样式 Toast + * @param text Toast 提示文本 + */ + public static void warning(final String text) { + custom(true, null, sWarningStyle, text, Toast.LENGTH_SHORT, getWarningDrawable()); + } + + /** + * warning 样式 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void warning(final String text, final int duration) { + custom(true, null, sWarningStyle, text, duration, getWarningDrawable()); + } + + /** + * warning 样式 Toast + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void warning(final String text, final Drawable icon) { + custom(true, null, sWarningStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** + * warning 样式 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void warning(final String text, final int duration, final Drawable icon) { + custom(true, null, sWarningStyle, text, duration, icon); + } + + // = + + /** + * warning 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + */ + public static void warning(final boolean isSingle, final String text) { + custom(isSingle, null, sWarningStyle, text, Toast.LENGTH_SHORT, getWarningDrawable()); + } + + /** + * warning 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void warning(final boolean isSingle, final String text, final int duration) { + custom(isSingle, null, sWarningStyle, text, duration, getWarningDrawable()); + } + + /** + * warning 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void warning(final boolean isSingle, final String text, final Drawable icon) { + custom(isSingle, null, sWarningStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** + * warning 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void warning(final boolean isSingle, final String text, final int duration, final Drawable icon) { + custom(isSingle, null, sWarningStyle, text, duration, icon); + } + + // ==================== + // = 显示 error Toast = + // ==================== + + /** + * error 样式 Toast + * @param text Toast 提示文本 + */ + public static void error(final String text) { + custom(true, null, sErrorStyle, text, Toast.LENGTH_SHORT, getErrorDrawable()); + } + + /** + * error 样式 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void error(final String text, final int duration) { + custom(true, null, sErrorStyle, text, duration, getErrorDrawable()); + } + + /** + * error 样式 Toast + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void error(final String text, final Drawable icon) { + custom(true, null, sErrorStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** + * error 样式 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void error(final String text, final int duration, final Drawable icon) { + custom(true, null, sErrorStyle, text, duration, icon); + } + + // = + + /** + * error 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + */ + public static void error(final boolean isSingle, final String text) { + custom(isSingle, null, sErrorStyle, text, Toast.LENGTH_SHORT, getErrorDrawable()); + } + + /** + * error 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void error(final boolean isSingle, final String text, final int duration) { + custom(isSingle, null, sErrorStyle, text, duration, getErrorDrawable()); + } + + /** + * error 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void error(final boolean isSingle, final String text, final Drawable icon) { + custom(isSingle, null, sErrorStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** + * error 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void error(final boolean isSingle, final String text, final int duration, final Drawable icon) { + custom(isSingle, null, sErrorStyle, text, duration, icon); + } + + // ====================== + // = 显示 success Toast = + // ====================== + + /** + * success 样式 Toast + * @param text Toast 提示文本 + */ + public static void success(final String text) { + custom(true, null, sSuccessStyle, text, Toast.LENGTH_SHORT, getSuccessDrawable()); + } + + /** + * success 样式 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void success(final String text, final int duration) { + custom(true, null, sSuccessStyle, text, duration, getSuccessDrawable()); + } + + /** + * success 样式 Toast + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void success(final String text, final Drawable icon) { + custom(true, null, sSuccessStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** + * success 样式 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void success(final String text, final int duration, final Drawable icon) { + custom(true, null, sSuccessStyle, text, duration, icon); + } + + // = + + /** + * success 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + */ + public static void success(final boolean isSingle, final String text) { + custom(isSingle, null, sSuccessStyle, text, Toast.LENGTH_SHORT, getSuccessDrawable()); + } + + /** + * success 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void success(final boolean isSingle, final String text, final int duration) { + custom(isSingle, null, sSuccessStyle, text, duration, getSuccessDrawable()); + } + + /** + * success 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void success(final boolean isSingle, final String text, final Drawable icon) { + custom(isSingle, null, sSuccessStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** + * success 样式 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void success(final boolean isSingle, final String text, final int duration, final Drawable icon) { + custom(isSingle, null, sSuccessStyle, text, duration, icon); + } + + // ===================== + // = 显示 custom Toast = + // ===================== + + /** + * custom Toast + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + */ + public static void custom(final ToastTintUtils.Style style, final String text) { + custom(true, null, style, text, Toast.LENGTH_SHORT, null); + } + + /** + * custom Toast + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void custom(final ToastTintUtils.Style style, final String text, final int duration) { + custom(true, null, style, text, duration, null); + } + + /** + * custom Toast + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void custom(final ToastTintUtils.Style style, final String text, final Drawable icon) { + custom(true, null, style, text, Toast.LENGTH_SHORT, icon); + } + + /** + * custom Toast + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void custom(final ToastTintUtils.Style style, final String text, final int duration, final Drawable icon) { + custom(true, null, style, text, duration, icon); + } + + // = + + /** + * custom Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + */ + public static void custom(final boolean isSingle, final ToastTintUtils.Style style, final String text) { + custom(isSingle, null, style, text, Toast.LENGTH_SHORT, null); + } + + /** + * custom Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void custom(final boolean isSingle, final ToastTintUtils.Style style, final String text, final int duration) { + custom(isSingle, null, style, text, duration, null); + } + + /** + * custom Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void custom(final boolean isSingle, final ToastTintUtils.Style style, final String text, final Drawable icon) { + custom(isSingle, null, style, text, Toast.LENGTH_SHORT, icon); + } + + /** + * custom Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void custom(final boolean isSingle, final ToastTintUtils.Style style, final String text, final int duration, final Drawable icon) { + custom(isSingle, null, style, text, duration, icon); + } + + // = + + /** + * custom Toast + * @param context {@link Context} + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + */ + public static void custom(final Context context, final ToastTintUtils.Style style, final String text) { + custom(true, context, style, text, Toast.LENGTH_SHORT, null); + } + + /** + * custom Toast + * @param context {@link Context} + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void custom(final Context context, final ToastTintUtils.Style style, final String text, final int duration) { + custom(true, context, style, text, duration, null); + } + + /** + * custom Toast + * @param context {@link Context} + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void custom(final Context context, final ToastTintUtils.Style style, final String text, final Drawable icon) { + custom(true, context, style, text, Toast.LENGTH_SHORT, icon); + } + + /** + * custom Toast + * @param context {@link Context} + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void custom(final Context context, final ToastTintUtils.Style style, final String text, final int duration, final Drawable icon) { + custom(true, context, style, text, duration, icon); + } + + // = + + /** + * custom Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + */ + public static void custom(final boolean isSingle, final Context context, final ToastTintUtils.Style style, final String text) { + custom(isSingle, context, style, text, Toast.LENGTH_SHORT, null); + } + + /** + * custom Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void custom(final boolean isSingle, final Context context, final ToastTintUtils.Style style, final String text, final int duration) { + custom(isSingle, context, style, text, duration, null); + } + + /** + * custom Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + */ + public static void custom(final boolean isSingle, final Context context, final ToastTintUtils.Style style, final String text, final Drawable icon) { + custom(isSingle, context, style, text, Toast.LENGTH_SHORT, icon); + } + + /** + * custom Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param icon Toast icon Drawable + */ + public static void custom(final boolean isSingle, final Context context, final ToastTintUtils.Style style, final String text, final int duration, + final Drawable icon) { + // 获取 View + View view = inflaterView(context, style, text, icon); + // 显示 Toast + showToastView(isSingle, context, view, duration); + } + + // ============== + // = 内部 Toast = + // ============== + + /** + * 显示 View Toast 方法 + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + private static void showToastView(final boolean isSingle, final Context context, final View view, final int duration) { + if (view == null) return; + if (sIsHandler) { + sHandler.post(new Runnable() { + @Override + public void run() { + try { + Toast toast = newToastView(isSingle, context, view, duration); + if (toast != null) { + toast.show(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "showToastView"); + } + } + }); + } else { + try { + Toast toast = newToastView(isSingle, context, view, duration); + if (toast != null) { + toast.show(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "showToastView"); + } + } + } + + /** + * 获取一个新的 View Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @return {@link Toast} + */ + private static Toast newToastView(final boolean isSingle, Context context, final View view, final int duration) { + if (context == null) { + context = DevUtils.getContext(); + } + // 防止 Context 为 null + if (context == null) { + return null; + } else if (view == null) { // 防止显示的 View 为 null + return null; + } + // 判断是否显示唯一, 单独共用一个 + if (isSingle) { + try { + // 关闭旧的 Toast + if (sToast != null) { + sToast.cancel(); + sToast = null; + } + // 解决 MIUI 会显示应用名称问题 + sToast = new Toast(context); + sToast.setView(view); + sToast.setDuration(duration); + // 判断是否使用配置 + if (sUseConfig) { + // 设置属性配置 + if (sGravity != 0) { + sToast.setGravity(sGravity, sX, sY); + } + sToast.setMargin(sHorizontalMargin, sVerticalMargin); + } + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(sToast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastView"); + } + return sToast; + } else { + Toast toast = null; + try { + // 解决 MIUI 会显示应用名称问题 + toast = new Toast(context); + toast.setView(view); + toast.setDuration(duration); + // 判断是否使用配置 + if (sUseConfig) { + // 设置属性配置 + if (sGravity != 0) { + toast.setGravity(sGravity, sX, sY); + } + toast.setMargin(sHorizontalMargin, sVerticalMargin); + } + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(toast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastView"); + } + return toast; + } + } + + /** + * 实例化 Layout View + * @param context {@link Context} + * @param style Toast 样式 {@link ToastTintUtils.Style} + * @param text Toast 提示文本 + * @param icon Toast icon Drawable + * @return {@link View} inflate dev_toast_layout + */ + private static View inflaterView(Context context, final ToastTintUtils.Style style, final String text, Drawable icon) { + if (context == null) { + context = DevUtils.getContext(); + } + // 如果样式为 null, 则不处理 + if (style == null) { + return null; + } + // 提示文本 + String toastText = text; + // 判断是否过滤 + if (!sPriToastFilter.filter(toastText)) { + return null; + } + // 处理内容 + toastText = sPriToastFilter.handlerContent(toastText); + // 设置为 null, 便于提示排查 + if (TextUtils.isEmpty(toastText)) { + toastText = sNullText; + // 如果还是为 null, 则不处理 + if (TextUtils.isEmpty(toastText)) { + return null; + } + } + if (context != null) { + try { + // 引入 View + final View toastLayout = LayoutInflater.from(context).inflate(dev.utils.R.layout.dev_toast_layout, null); + // 初始化 View + final ImageView toastIcon = toastLayout.findViewById(dev.utils.R.id.vid_dtl_toast_igview); + final TextView toastTextView = toastLayout.findViewById(dev.utils.R.id.vid_dtl_toast_tv); + + // ================= + // = TextView 相关 = + // ================= + + // 设置文案 + toastTextView.setText(toastText); + // 设置字体颜色 + if (style.getTextColor() != 0) { + toastTextView.setTextColor(style.getTextColor()); + } + // 设置字体大小 + if (style.getTextSize() != 0f) { + toastTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, style.getTextSize()); + } + // 设置最大行数 + if (style.getMaxLines() >= 1) { + toastTextView.setMaxLines(style.getMaxLines()); + } + // 设置 Ellipsize 效果 + if (style.getEllipsize() != null) { + toastTextView.setEllipsize(style.getEllipsize()); + } + // 设置字体样式 + if (style.getTypeface() != null) { + toastTextView.setTypeface(style.getTypeface()); + } + + // ================== + // = ImageView 相关 = + // ================== + + // 判断是否使用图标 + if (icon != null) { + // 判断是否渲染图标 + if (style.isTintIcon() && style.getTintIconColor() != 0) { + icon = tintIcon(icon, style.getTintIconColor()); + } + // 设置 ImageView 图片 + setBackground(toastIcon, icon); + } else { + // 隐藏图标 + toastIcon.setVisibility(View.GONE); + } + + // ================== + // = 背景 View 相关 = + // ================== + + // 背景图片 + Drawable drawableFrame = style.getBackground(); + // 判断是否为 null + if (drawableFrame == null) { + drawableFrame = getDrawable(context, dev.utils.R.drawable.dev_toast_frame); + // 判断是否需要着色 + if (style.getBackgroundTintColor() != 0) { // 根据背景色进行渲染透明图片 + drawableFrame = tint9PatchDrawableFrame(context, style.getBackgroundTintColor()); + } + } + // 设置 View 背景 + setBackground(toastLayout, drawableFrame); + // 返回 View + return toastLayout; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "inflaterView"); + } + } + return null; + } + + // ============ + // = 内部方法 = + // ============ + + /** + * 设置背景 + * @param view {@link View} + * @param drawable 背景 {@link Drawable} + */ + private static void setBackground(final View view, final Drawable drawable) { + if (view != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) + view.setBackground(drawable); + else + view.setBackgroundDrawable(drawable); + } + } + + /** + * 获取 Drawable + * @param context {@link Context} + * @param id R.drawable.id + * @return {@link Drawable} + */ + private static Drawable getDrawable(final Context context, @DrawableRes final int id) { + if (context == null) { + return null; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + return context.getDrawable(id); + else + return context.getResources().getDrawable(id); + } + + /** + * 图片着色 + * @param drawable {@link Drawable} + * @param tintColor R.color.id + * @return {@link Drawable} + */ + private static Drawable tintIcon(final Drawable drawable, @ColorInt final int tintColor) { + if (drawable != null) { + try { + drawable.setColorFilter(tintColor, PorterDuff.Mode.SRC_IN); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "tintIcon"); + } + } + return drawable; + } + + /** + * .9 图片着色 + * @param context {@link Context} + * @param tintColor R.color.id + * @return {@link Drawable} + */ + private static Drawable tint9PatchDrawableFrame(final Context context, @ColorInt final int tintColor) { + try { + final NinePatchDrawable toastDrawable = (NinePatchDrawable) getDrawable(context, dev.utils.R.drawable.dev_toast_frame); + return tintIcon(toastDrawable, tintColor); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "tint9PatchDrawableFrame"); + } + return null; + } + + // =============================== + // = 解决 Android 7.1.1 崩溃问题 = + // =============================== + + /** + * 反射 Hook Toast 设置 Handler + * @param toast {@link Toast} + */ + private static void reflectToastHandler(final Toast toast) { + if (toast == null) return; + // 反射设置 Toat Handler 解决 Android 7.1.1 Toast 崩溃问题 + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) { + try { + Field field_tn = Toast.class.getDeclaredField("mTN"); + field_tn.setAccessible(true); + + Object mTN = field_tn.get(toast); + Field field_handler = field_tn.getType().getDeclaredField("mHandler"); + field_handler.setAccessible(true); + + Handler handler = (Handler) field_handler.get(mTN); + field_handler.set(mTN, new SafeHandler(handler)); + } catch (Exception ignored) { + } + } + } + + /** + * detail: Toast 安全显示 Handler + * @author Ttt + */ + private static final class SafeHandler extends Handler { + + private Handler mHandler; + + SafeHandler(Handler handler) { + mHandler = handler; + } + + @Override + public void handleMessage(Message msg) { + mHandler.handleMessage(msg); + } + + @Override + public void dispatchMessage(Message msg) { + try { + mHandler.dispatchMessage(msg); + } catch (Exception ignored) { + } + } + } + + // ============ + // = 样式相关 = + // ============ + + /** + * detail: Toast 自定义 View 着色等相关 样式配置 + * @author Ttt + */ + public interface Style { + + /** + * 获取文本颜色 + * @return 文本颜色 + */ + int getTextColor(); + + /** + * 获取字体大小 + * @return 字体大小 + */ + float getTextSize(); + + /** + * 获取背景着色颜色 + * @return 背景着色颜色 + */ + int getBackgroundTintColor(); + + /** + * 获取背景图片 + * @return 背景图片 + */ + Drawable getBackground(); + + /** + * 获取最大行数 + * @return 最大行数 + */ + int getMaxLines(); + + /** + * 获取 Ellipsize 效果 + * @return Ellipsize 效果 + */ + TextUtils.TruncateAt getEllipsize(); + + /** + * 获取字体样式 + * @return 字体样式 + */ + Typeface getTypeface(); + + /** + * 获取图标着色颜色 + * @return 图标着色颜色 + */ + int getTintIconColor(); + + /** + * 是否渲染图标 - getTintIconColor() 着色渲染 + * @return {@code true} yes, {@code false} no + */ + boolean isTintIcon(); + } + + // = + + /** + * detail: 默认样式 + * @author Ttt + */ + public static class DefaultStyle implements Style { + + /** + * 获取获取文本颜色 + * @return 文本颜色 + */ + @Override + public int getTextColor() { + return Color.WHITE; + } + + /** + * 获取字体大小 + * @return 字体大小 + */ + @Override + public float getTextSize() { + return 16f; + } + + /** + * 获取背景着色颜色 + * @return 背景着色颜色 + */ + @Override + public int getBackgroundTintColor() { + return 0; + } + + /** + * 获取背景图片 + * @return 背景图片 + */ + @Override + public Drawable getBackground() { + return null; + } + + /** + * 获取最大行数 + * @return 最大行数 + */ + @Override + public int getMaxLines() { + return 0; + } + + /** + * 获取 Ellipsize 效果 + * @return Ellipsize 效果 + */ + @Override + public TextUtils.TruncateAt getEllipsize() { + return null; + } + + /** + * 获取字体样式 + * @return 字体样式 + */ + @Override + public Typeface getTypeface() { + // return Typeface.create("sans-serif-condensed", Typeface.NORMAL); + return null; + } + + /** + * 获取图标着色颜色 + * @return 图标着色颜色 + */ + @Override + public int getTintIconColor() { + return Color.WHITE; + } + + /** + * 是否渲染图标 - getTintIconColor() 着色渲染 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isTintIcon() { + return false; + } + } + + /** + * detail: Normal 样式 - 灰色 + * @author Ttt + */ + public static class NormalStyle extends DefaultStyle { + + /** + * 获取背景着色颜色 + * @return 背景着色颜色 + */ + @Override + public int getBackgroundTintColor() { + return Color.parseColor("#353A3E"); + } + + /** + * 是否渲染图标 - getTintIconColor() 着色渲染 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isTintIcon() { + return true; + } + } + + /** + * detail: Info 样式 - 海洋蓝 + * @author Ttt + */ + public static class InfoStyle extends DefaultStyle { + + /** + * 获取背景着色颜色 + * @return 背景着色颜色 + */ + @Override + public int getBackgroundTintColor() { + return Color.parseColor("#3F51B5"); + } + + /** + * 是否渲染图标 - getTintIconColor() 着色渲染 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isTintIcon() { + return true; + } + } + + /** + * detail: Warning 样式 - 橙色 + * @author Ttt + */ + public static class WarningStyle extends DefaultStyle { + + /** + * 获取背景着色颜色 + * @return 背景着色颜色 + */ + @Override + public int getBackgroundTintColor() { + return Color.parseColor("#FFA900"); + } + + /** + * 是否渲染图标 - getTintIconColor() 着色渲染 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isTintIcon() { + return true; + } + } + + /** + * detail: Error 样式 - 红色 + * @author Ttt + */ + public static class ErrorStyle extends DefaultStyle { + + /** + * 获取背景着色颜色 + * @return 背景着色颜色 + */ + @Override + public int getBackgroundTintColor() { + return Color.parseColor("#D50000"); + } + + /** + * 是否渲染图标 - getTintIconColor() 着色渲染 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isTintIcon() { + return true; + } + } + + /** + * detail: Success 样式 - 绿色 + * @author Ttt + */ + public static class SuccessStyle extends DefaultStyle { + + /** + * 获取背景着色颜色 + * @return 背景着色颜色 + */ + @Override + public int getBackgroundTintColor() { + return Color.parseColor("#388E3C"); + } + + /** + * 是否渲染图标 - getTintIconColor() 着色渲染 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isTintIcon() { + return true; + } + } + + // ============ + // = 其他接口 = + // ============ + + /** + * detail: Toast 过滤器 + * @author Ttt + */ + public interface Filter { + + /** + * 判断是否显示 + * @param content Toast 显示文案 + * @return {@code true} 接着执行, {@code false} 过滤不处理 + */ + boolean filter(String content); + + /** + * 获取 Toast 显示的文案 + * @param content Toast 显示文案 + * @return 处理后的内容 + */ + String handlerContent(String content); + } + + // ================================== + // = ToastTintUtils.Filter 实现方法 = + // ================================== + + /** + * 内部 Toast Filter 实现对象 + */ + private static final ToastTintUtils.Filter sPriToastFilter = new ToastTintUtils.Filter() { + + /** + * 判断是否显示 + * @param content Toast 显示文案 + * @return {@code true} 接着执行, {@code false} 过滤不处理 + */ + @Override + public boolean filter(String content) { + if (sToastFilter != null) { + return sToastFilter.filter(content); + } + return true; + } + + /** + * 获取 Toast 显示的文案 + * @param content Toast 显示文案 + * @return 处理后的内容 + */ + @Override + public String handlerContent(String content) { + if (sToastFilter != null) { + return sToastFilter.handlerContent(content); + } + return content; + } + }; +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/toast/ToastUtils.java b/lib/DevApp/src/main/java/dev/utils/app/toast/ToastUtils.java new file mode 100644 index 0000000000..322d0e5866 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/toast/ToastUtils.java @@ -0,0 +1,846 @@ +package dev.utils.app.toast; + +import android.content.Context; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.support.annotation.StringRes; +import android.text.TextUtils; +import android.view.View; +import android.widget.Toast; + +import java.lang.reflect.Field; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Simple Toast 工具类 ( 简单的 Toast 工具类, 支持子线程弹出 Toast) + * @author Ttt + *
+ *     支持子线程弹出 Toast, 可通过开关配置
+ *     内部解决 Android 7.1.1 崩溃问题
+ *     但无处理 部分 ROM 如魅族、小米、三星等关闭应用通知, 无法显示 Toast 问题
+ * 
+ */ +public final class ToastUtils { + + private ToastUtils() { + } + + // 日志 TAG + private static final String TAG = ToastUtils.class.getSimpleName(); + // Toast 判断过滤 + private static ToastUtils.Filter sToastFilter = null; + // 内部持有单个 Toast + private static Toast sToast = null; + // 判断是否使用 Handler + private static boolean sIsHandler = true; + // 内部 Handler + private static final Handler sHandler = new Handler(Looper.getMainLooper()); + // Null 值 - null 提示值 + private static String sNullText = null; + + // ============ + // = 部分配置 = + // ============ + + // 判断是否使用配置 + private static boolean sUseConfig = true; + // Toast 的重心、X、Y 轴偏移 + private static int sGravity, sX, sY; + // 水平边距、垂直边距 + private static float sHorizontalMargin, sVerticalMargin; + + /** + * 重置默认参数 + */ + public static void reset() { + sIsHandler = true; + sUseConfig = true; + sNullText = null; + sGravity = sX = sY = 0; + sHorizontalMargin = sVerticalMargin = 0.0f; + } + + /** + * 设置 Toast 过滤器 + * @param toastFilter {@link ToastUtils.Filter} + */ + public static void setToastFilter(final ToastUtils.Filter toastFilter) { + ToastUtils.sToastFilter = toastFilter; + } + + /** + * 设置是否使用 Handler 显示 Toast + * @param isHandler {@code true} 使用, {@code false} 不使用 + */ + public static void setIsHandler(final boolean isHandler) { + ToastUtils.sIsHandler = isHandler; + } + + /** + * 设置 Text 为 null 的文本 + * @param nullText 显示内容为 null 时, 使用的提示值 + */ + public static void setNullText(final String nullText) { + ToastUtils.sNullText = nullText; + } + + /** + * 设置是否使用配置 + * @param useConfig {@code true} 使用, {@code false} 不使用 + */ + public static void setUseConfig(final boolean useConfig) { + ToastUtils.sUseConfig = useConfig; + } + + /** + * 设置 Toast 显示在屏幕上的位置 + * @param gravity 重心 + * @param xOffset X 轴偏移 + * @param yOffset Y 轴偏移 + */ + public static void setGravity(final int gravity, final int xOffset, final int yOffset) { + ToastUtils.sGravity = gravity; + ToastUtils.sX = xOffset; + ToastUtils.sY = yOffset; + } + + /** + * 设置边距 + * @param horizontalMargin 水平边距 + * @param verticalMargin 垂直边距 + */ + public static void setMargin(final float horizontalMargin, final float verticalMargin) { + ToastUtils.sHorizontalMargin = horizontalMargin; + ToastUtils.sVerticalMargin = verticalMargin; + } + + // ================== + // = 统一显示 Toast = + // ================== + + // ====================== + // = Toast.LENGTH_SHORT = + // ====================== + + /** + * 显示 LENGTH_SHORT Toast + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + public static void showShort(final String text, final Object... objs) { + showShort(null, text, objs); + } + + /** + * 显示 LENGTH_SHORT Toast + * @param context {@link Context} + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + public static void showShort(final Context context, final String text, final Object... objs) { + handlerToastStr(true, context, text, Toast.LENGTH_SHORT, objs); + } + + // = + + /** + * 显示 LENGTH_SHORT Toast + * @param resId R.string.id + * @param objs 格式化参数 + */ + public static void showShort(@StringRes final int resId, final Object... objs) { + showShort(null, resId, objs); + } + + /** + * 显示 LENGTH_SHORT Toast + * @param context {@link Context} + * @param resId R.string.id + * @param objs 格式化参数 + */ + public static void showShort(final Context context, @StringRes final int resId, final Object... objs) { + handlerToastRes(true, context, resId, Toast.LENGTH_SHORT, objs); + } + + // ===================== + // = Toast.LENGTH_LONG = + // ===================== + + /** + * 显示 LENGTH_LONG Toast + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + public static void showLong(final String text, final Object... objs) { + showLong(null, text, objs); + } + + /** + * 显示 LENGTH_LONG Toast + * @param context {@link Context} + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + public static void showLong(final Context context, final String text, final Object... objs) { + handlerToastStr(true, context, text, Toast.LENGTH_LONG, objs); + } + + // = + + /** + * 显示 LENGTH_LONG Toast + * @param resId R.string.id + * @param objs 格式化参数 + */ + public static void showLong(@StringRes final int resId, final Object... objs) { + showLong(null, resId, objs); + } + + /** + * 显示 LENGTH_LONG Toast + * @param context {@link Context} + * @param resId R.string.id + * @param objs 格式化参数 + */ + public static void showLong(final Context context, @StringRes final int resId, final Object... objs) { + handlerToastRes(true, context, resId, Toast.LENGTH_LONG, objs); + } + + // ============== + // = Toast 方法 = + // ============== + + /** + * 显示 Toast + * @param resId R.string.id + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToast(@StringRes final int resId, final int duration) { + showToast(null, resId, duration); + } + + /** + * 显示 Toast + * @param context {@link Context} + * @param resId R.string.id + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToast(final Context context, @StringRes final int resId, final int duration) { + handlerToastRes(true, context, resId, duration); + } + + /** + * 显示 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToast(final String text, final int duration) { + priShowToastText(true, null, text, duration); + } + + /** + * 显示 Toast + * @param context {@link Context} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToast(final Context context, final String text, final int duration) { + priShowToastText(true, context, text, duration); + } + + // ==================== + // = 非统一显示 Toast = + // ==================== + + // ====================== + // = Toast.LENGTH_SHORT = + // ====================== + + /** + * 显示 new LENGTH_SHORT Toast + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + public static void showShortNew(final String text, final Object... objs) { + showShortNew(null, text, objs); + } + + /** + * 显示 new LENGTH_SHORT Toast + * @param context {@link Context} + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + public static void showShortNew(final Context context, final String text, final Object... objs) { + handlerToastStr(false, context, text, Toast.LENGTH_SHORT, objs); + } + + // = + + /** + * 显示 new LENGTH_SHORT Toast + * @param resId R.string.id + * @param objs 格式化参数 + */ + public static void showShortNew(@StringRes final int resId, final Object... objs) { + showShortNew(null, resId, objs); + } + + /** + * 显示 new LENGTH_SHORT Toast + * @param context {@link Context} + * @param resId R.string.id + * @param objs 格式化参数 + */ + public static void showShortNew(final Context context, @StringRes final int resId, final Object... objs) { + handlerToastRes(false, context, resId, Toast.LENGTH_SHORT, objs); + } + + // ===================== + // = Toast.LENGTH_LONG = + // ===================== + + /** + * 显示 new LENGTH_LONG Toast + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + public static void showLongNew(final String text, final Object... objs) { + showLongNew(null, text, objs); + } + + /** + * 显示 new LENGTH_LONG Toast + * @param context {@link Context} + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + public static void showLongNew(final Context context, final String text, final Object... objs) { + handlerToastStr(false, context, text, Toast.LENGTH_LONG, objs); + } + + // = + + /** + * 显示 new LENGTH_LONG Toast + * @param resId R.string.id + * @param objs 格式化参数 + */ + public static void showLongNew(@StringRes final int resId, final Object... objs) { + showLongNew(null, resId, objs); + } + + /** + * 显示 new LENGTH_LONG Toast + * @param context {@link Context} + * @param resId R.string.id + * @param objs 格式化参数 + */ + public static void showLongNew(final Context context, @StringRes final int resId, final Object... objs) { + handlerToastRes(false, context, resId, Toast.LENGTH_LONG, objs); + } + + // ============== + // = Toast 方法 = + // ============== + + /** + * 显示新的 Toast + * @param resId R.string.id + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToastNew(@StringRes final int resId, final int duration) { + showToastNew(null, resId, duration); + } + + /** + * 显示新的 Toast + * @param context {@link Context} + * @param resId R.string.id + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToastNew(final Context context, @StringRes final int resId, final int duration) { + handlerToastRes(false, context, resId, duration); + } + + /** + * 显示新的 Toast + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToastNew(final String text, final int duration) { + priShowToastText(false, null, text, duration); + } + + /** + * 显示新的 Toast + * @param context {@link Context} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToastNew(final Context context, final String text, final int duration) { + priShowToastText(false, context, text, duration); + } + + // ============== + // = 显示 Toast = + // ============== + + /** + * 内部私有方法, 最终显示 Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + private static void priShowToastText(final boolean isSingle, final Context context, final String text, final int duration) { + if (sIsHandler) { + sHandler.post(new Runnable() { + @Override + public void run() { + try { + Toast toast = newToastText(isSingle, context, text, duration); + if (toast != null) { + toast.show(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "priShowToastText"); + } + } + }); + } else { + try { + Toast toast = newToastText(isSingle, context, text, duration); + if (toast != null) { + toast.show(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "priShowToastText"); + } + } + } + + /** + * 获取一个新的 Text Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @return {@link Toast} + */ + public static Toast newToastText(final boolean isSingle, Context context, final String text, final int duration) { + if (context == null) { + context = DevUtils.getContext(); + } + // 提示文本 + String toastText = text; + // 判断是否过滤 + if (!sPriToastFilter.filter(toastText)) { + return null; + } + // 处理内容 + toastText = sPriToastFilter.handlerContent(toastText); + // 设置为 null, 便于提示排查 + if (TextUtils.isEmpty(toastText)) { + toastText = sNullText; + // 如果还是为 null, 则不处理 + if (TextUtils.isEmpty(toastText)) { + return null; + } + } + // 判断是否显示唯一, 单独共用一个 + if (isSingle) { + try { + // 关闭旧的 Toast + if (sToast != null) { + sToast.cancel(); + sToast = null; + } + // 解决 MIUI 会显示应用名称问题 + sToast = Toast.makeText(context, "", duration); + sToast.setText(toastText); + // 判断是否使用配置 + if (sUseConfig) { + // 设置属性配置 + if (sGravity != 0) { + sToast.setGravity(sGravity, sX, sY); + } + sToast.setMargin(sHorizontalMargin, sVerticalMargin); + } + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(sToast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastText"); + } + return sToast; + } else { + Toast toast = null; + try { + // 解决 MIUI 会显示应用名称问题 + toast = Toast.makeText(context, "", duration); + toast.setText(toastText); + // 判断是否使用配置 + if (sUseConfig) { + // 设置属性配置 + if (sGravity != 0) { + toast.setGravity(sGravity, sX, sY); + } + toast.setMargin(sHorizontalMargin, sVerticalMargin); + } + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(toast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastText"); + } + return toast; + } + } + + // =================== + // = 显示 View Toast = + // =================== + + /** + * 显示 View Toast 方法 + * @param view Toast 显示的 View + */ + public static void showToastView(final View view) { + showToastView(true, null, view, Toast.LENGTH_SHORT); + } + + /** + * 显示 View Toast 方法 + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToastView(final View view, final int duration) { + showToastView(true, null, view, duration); + } + + /** + * 显示 View Toast 方法 + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param view Toast 显示的 View + */ + public static void showToastView(final boolean isSingle, final View view) { + showToastView(isSingle, null, view, Toast.LENGTH_SHORT); + } + + /** + * 显示 View Toast 方法 + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToastView(final boolean isSingle, final View view, final int duration) { + showToastView(isSingle, null, view, duration); + } + + /** + * 显示 View Toast 方法 + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void showToastView(final boolean isSingle, final Context context, final View view, final int duration) { + if (view == null) return; + if (sIsHandler) { + sHandler.post(new Runnable() { + @Override + public void run() { + try { + Toast toast = newToastView(isSingle, context, view, duration); + if (toast != null) { + toast.show(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "showToastView"); + } + } + }); + } else { + try { + Toast toast = newToastView(isSingle, context, view, duration); + if (toast != null) { + toast.show(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "showToastView"); + } + } + } + + /** + * 获取一个新的 View Toast + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @return {@link Toast} + */ + public static Toast newToastView(final boolean isSingle, Context context, final View view, final int duration) { + if (context == null) { + context = DevUtils.getContext(); + } + // 判断是否过滤 + if (!sPriToastFilter.filter(view)) { + return null; + } + // 防止 Context 为 null + if (context == null) { + return null; + } else if (view == null) { // 防止显示的 View 为 null + return null; + } + // 判断是否显示唯一, 单独共用一个 + if (isSingle) { + try { + // 关闭旧的 Toast + if (sToast != null) { + sToast.cancel(); + sToast = null; + } + // 解决 MIUI 会显示应用名称问题 + sToast = new Toast(context); + sToast.setView(view); + sToast.setDuration(duration); + // 判断是否使用配置 + if (sUseConfig) { + // 设置属性配置 + if (sGravity != 0) { + sToast.setGravity(sGravity, sX, sY); + } + sToast.setMargin(sHorizontalMargin, sVerticalMargin); + } + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(sToast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastView"); + } + return sToast; + } else { + Toast toast = null; + try { + // 解决 MIUI 会显示应用名称问题 + toast = new Toast(context); + toast.setView(view); + toast.setDuration(duration); + // 判断是否使用配置 + if (sUseConfig) { + // 设置属性配置 + if (sGravity != 0) { + toast.setGravity(sGravity, sX, sY); + } + toast.setMargin(sHorizontalMargin, sVerticalMargin); + } + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(toast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastView"); + } + return toast; + } + } + + // ================ + // = 内部处理方法 = + // ================ + + /** + * 处理 R.string 资源 Toast 的格式化 + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param resId R.string.id + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param objs 格式化参数 + */ + private static void handlerToastRes(final boolean isSingle, Context context, @StringRes final int resId, final int duration, final Object... objs) { + if (context == null) { + context = DevUtils.getContext(); + } + if (context != null) { + String text = null; + try { + if (objs != null && objs.length != 0) { + text = context.getString(resId, objs); + } else { + text = context.getString(resId); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "handlerToastRes"); + } + priShowToastText(isSingle, context, text, duration); + } + } + + /** + * 处理字符串 Toast 的格式化 + * @param isSingle 是否单例 Toast ( 全局共用 Toast) + * @param context {@link Context} + * @param text Toast 提示文本 + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @param objs 格式化参数 + */ + private static void handlerToastStr(final boolean isSingle, Context context, final String text, final int duration, final Object... objs) { + if (context == null) { + context = DevUtils.getContext(); + } + // 防止 Context 为 null + if (context != null) { + // 表示需要格式化字符串, 只是为了减少 format 步骤, 增加判断, 为 null 不影响 + if (objs != null && objs.length != 0) { + if (text != null) { // String.format() 中的 objs 可以为 null, 但是 text 不能为 null + try { + priShowToastText(isSingle, context, String.format(text, objs), duration); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "handlerToastStr"); + priShowToastText(isSingle, context, e.getMessage(), duration); + } + } else { + priShowToastText(isSingle, context, text, duration); + } + } else { + priShowToastText(isSingle, context, text, duration); + } + } + } + + // =============================== + // = 解决 Android 7.1.1 崩溃问题 = + // =============================== + + /** + * 反射 Hook Toast 设置 Handler + * @param toast {@link Toast} + */ + private static void reflectToastHandler(final Toast toast) { + if (toast == null) return; + // 反射设置 Toat Handler 解决 Android 7.1.1 Toast 崩溃问题 + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) { + try { + Field field_tn = Toast.class.getDeclaredField("mTN"); + field_tn.setAccessible(true); + + Object mTN = field_tn.get(toast); + Field field_handler = field_tn.getType().getDeclaredField("mHandler"); + field_handler.setAccessible(true); + + Handler handler = (Handler) field_handler.get(mTN); + field_handler.set(mTN, new SafeHandler(handler)); + } catch (Exception ignored) { + } + } + } + + /** + * detail: Toast 安全显示 Handler + * @author Ttt + */ + private static final class SafeHandler extends Handler { + + private Handler mHandler; + + SafeHandler(Handler handler) { + mHandler = handler; + } + + @Override + public void handleMessage(Message msg) { + mHandler.handleMessage(msg); + } + + @Override + public void dispatchMessage(Message msg) { + try { + mHandler.dispatchMessage(msg); + } catch (Exception ignored) { + } + } + } + + // ============ + // = 其他接口 = + // ============ + + /** + * detail: Toast 过滤器 + * @author Ttt + */ + public interface Filter { + + /** + * 判断是否显示 + * @param view Toast 显示的 View + * @return {@code true} 接着执行, {@code false} 过滤不处理 + */ + boolean filter(View view); + + /** + * 判断是否显示 + * @param content Toast 显示文案 + * @return {@code true} 接着执行, {@code false} 过滤不处理 + */ + boolean filter(String content); + + /** + * 获取 Toast 显示的文案 + * @param content Toast 显示文案 + * @return 处理后的内容 + */ + String handlerContent(String content); + } + + // ============================== + // = ToastUtils.Filter 实现方法 = + // ============================== + + /** + * 内部 Toast Filter 实现对象 + */ + private static final ToastUtils.Filter sPriToastFilter = new Filter() { + + /** + * 判断是否显示 + * @param view Toast 显示的 View + * @return {@code true} 接着执行, {@code false} 过滤不处理 + */ + @Override + public boolean filter(View view) { + if (sToastFilter != null) { + return sToastFilter.filter(view); + } + return (view != null); + } + + /** + * 判断是否显示 + * @param content Toast 显示文案 + * @return {@code true} 接着执行, {@code false} 过滤不处理 + */ + @Override + public boolean filter(String content) { + if (sToastFilter != null) { + return sToastFilter.filter(content); + } + return true; + } + + /** + * 获取 Toast 显示的文案 + * @param content Toast 显示文案 + * @return 处理后的内容 + */ + @Override + public String handlerContent(String content) { + if (sToastFilter != null) { + return sToastFilter.handlerContent(content); + } + return content; + } + }; +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/DefaultToastStyle.java b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/DefaultToastStyle.java new file mode 100644 index 0000000000..257918b7af --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/DefaultToastStyle.java @@ -0,0 +1,156 @@ +package dev.utils.app.toast.toaster; + +import android.graphics.Color; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorInt; +import android.text.TextUtils; + +/** + * detail: Toast 默认样式 + * @author Ttt + */ +public class DefaultToastStyle implements IToast.Style { + + /** + * 获取 Toast 的重心 + * @return Toast 的重心 + */ + @Override + public int getGravity() { + return 0; + } + + /** + * 获取 X 轴偏移 + * @return X 轴偏移 + */ + @Override + public int getXOffset() { + return 0; + } + + /** + * 获取 Y 轴偏移 + * @return Y 轴偏移 + */ + @Override + public int getYOffset() { + return 0; + } + + /** + * 获取水平边距 + * @return 水平边距 + */ + @Override + public int getHorizontalMargin() { + return 0; + } + + /** + * 获取垂直边距 + * @return 垂直边距 + */ + @Override + public int getVerticalMargin() { + return 0; + } + + /** + * 获取 Toast Z 轴坐标阴影 + * @return Toast Z 轴坐标阴影 + */ + @Override + public int getZ() { + return 0; + } + + /** + * 获取圆角大小 + * @return 圆角大小 + */ + @Override + public float getCornerRadius() { + return 5f; + } + + /** + * 获取背景着色颜色 + * @return 背景着色颜色 + */ + @ColorInt + @Override + public int getBackgroundTintColor() { + return 0xB2000000; + } + + /** + * 获取背景图片 + * @return 背景图片 + */ + @Override + public Drawable getBackground() { + return null; + } + + // ================= + // = TextView 相关 = + // ================= + + /** + * 获取文本颜色 + * @return 文本颜色 + */ + @ColorInt + @Override + public int getTextColor() { + return Color.WHITE; + } + + /** + * 获取字体大小 + * @return 字体大小 + */ + @Override + public float getTextSize() { + return 16f; + } + + /** + * 获取最大行数 + * @return 最大行数 + */ + @Override + public int getMaxLines() { + return 0; + } + + /** + * 获取 Ellipsize 效果 + * @return Ellipsize 效果 + */ + @Override + public TextUtils.TruncateAt getEllipsize() { + return null; + } + + /** + * 获取字体样式 + * @return 字体样式 + */ + @Override + public Typeface getTypeface() { + // return Typeface.create("sans-serif-condensed", Typeface.NORMAL); + return null; + } + + /** + * 获取 TextView padding 边距 - new int[] { left, top, right, bottom } + * @return TextView padding 边距 + */ + @Override + public int[] getPadding() { + return new int[]{25, 10, 25, 10}; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/DevToast.java b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/DevToast.java new file mode 100644 index 0000000000..608e04c9b4 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/DevToast.java @@ -0,0 +1,177 @@ +package dev.utils.app.toast.toaster; + +import android.app.Application; +import android.support.annotation.LayoutRes; +import android.support.annotation.StringRes; +import android.view.View; +import android.widget.Toast; + +/** + * detail: Toast 工具类 ( 支持子线程弹出 Toast, 处理无通知权限 ) + * @author Ttt + *
+ *     支持子线程弹出 Toast, 可通过开关配置
+ *     内部解决 Android 7.1.1 崩溃问题
+ *     已处理 部分 ROM 如魅族、小米、三星等关闭应用通知, 无法显示 Toast 问题
+ *     

+ * 缺点: 同时间只能显示一个 Toast + *
+ */ +public final class DevToast { + + private DevToast() { + } + + // 包下 IToastImpl 类持有对象 + private static final IToast.Operate sToast = new IToastImpl(); + + /** + * 重置默认参数 + */ + public static void reset() { + sToast.reset(); + } + + /** + * 设置是否使用 Handler 显示 Toast + * @param isHandler {@code true} 使用, {@code false} 不使用 + */ + public static void setIsHandler(final boolean isHandler) { + sToast.setIsHandler(isHandler); + } + + /** + * 设置 Text 为 null 的文本 + * @param nullText 显示内容为 null 时, 使用的提示值 + */ + public static void setNullText(final String nullText) { + sToast.setNullText(nullText); + } + + /** + * 设置 Toast 文案长度转换 显示时间 + * @param textLengthConvertDuration Toast 文案长度转换界限 + */ + public static void setTextLength(final int textLengthConvertDuration) { + sToast.setTextLength(textLengthConvertDuration); + } + + // = + + /** + * Application 初始化调用 ( 内部已调用 ) + * @param application {@link Application} + */ + public static void init(final Application application) { + sToast.init(application); + } + + // ============ + // = 配置方法 = + // ============ + + /** + * 使用单次 Toast 样式配置 + * @param toastStyle Toast 样式 + * @return {@link IToast.Operate} + */ + public static IToast.Operate style(final IToast.Style toastStyle) { + return sToast.style(toastStyle); + } + + /** + * 使用默认 Toast 样式 + * @return {@link IToast.Operate} + */ + public static IToast.Operate defaultStyle() { + return sToast.defaultStyle(); + } + + /** + * 获取 Toast 样式配置 + * @return Toast 样式配置 + */ + public static IToast.Style getToastStyle() { + return sToast.getToastStyle(); + } + + /** + * 初始化 Toast 样式配置 + * @param toastStyle Toast 样式配置 + */ + public static void initStyle(final IToast.Style toastStyle) { + sToast.initStyle(toastStyle); + } + + /** + * 初始化 Toast 过滤器 + * @param toastFilter Toast 过滤器 + */ + public static void initToastFilter(final IToast.Filter toastFilter) { + sToast.initToastFilter(toastFilter); + } + + /** + * 设置 Toast 显示的 View + * @param view Toast 显示的 View + */ + public static void setView(final View view) { + sToast.setView(view); + } + + /** + * 设置 Toast 显示的 View + * @param layoutId R.layout.id + */ + public static void setView(@LayoutRes final int layoutId) { + sToast.setView(layoutId); + } + + // ============ + // = 操作方法 = + // ============ + + /** + * 显示 Toast + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + public static void show(final String text, final Object... objs) { + sToast.show(text, objs); + } + + /** + * 显示 R.string.id Toast + * @param resId R.string.id + * @param objs 格式化参数 + */ + public static void show(@StringRes final int resId, final Object... objs) { + sToast.show(resId, objs); + } + + /** + * 通过 View 显示 Toast + * @param view Toast 显示的 View + */ + public static void show(final View view) { + sToast.show(view); + } + + /** + * 通过 View 显示 Toast + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + public static void show(final View view, final int duration) { + sToast.show(view, duration); + } + + // = + + /** + * 取消当前显示的 Toast + */ + public static void cancel() { + sToast.cancel(); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/IToast.java b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/IToast.java new file mode 100644 index 0000000000..9e84f80afc --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/IToast.java @@ -0,0 +1,281 @@ +package dev.utils.app.toast.toaster; + +import android.app.Application; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorInt; +import android.support.annotation.LayoutRes; +import android.support.annotation.StringRes; +import android.text.TextUtils; +import android.view.View; +import android.widget.Toast; + +/** + * detail: Toast 对外提供接口方法 + * @author Ttt + */ +public final class IToast { + + private IToast() { + } + + /** + * detail: Toast 对外公开操作方法 + * @author Ttt + */ + public interface Operate { + + /** + * 重置默认参数 + */ + void reset(); + + /** + * 设置是否使用 Handler 显示 Toast + * @param isHandler {@code true} 使用, {@code false} 不使用 + */ + void setIsHandler(boolean isHandler); + + /** + * 设置 Text 为 null 的文本 + * @param nullText 显示内容为 null 时, 使用的提示值 + */ + void setNullText(String nullText); + + /** + * 设置 Toast 文案长度转换 显示时间 + * @param textLengthConvertDuration Toast 文案长度转换界限 + */ + void setTextLength(int textLengthConvertDuration); + + // = + + /** + * Application 初始化调用 + * @param application {@link Application} + */ + void init(Application application); + + // ============ + // = 配置方法 = + // ============ + + /** + * 使用单次 Toast 样式配置 + * @param toastStyle Toast 样式 + * @return {@link IToast.Operate} + */ + IToast.Operate style(IToast.Style toastStyle); + + /** + * 使用默认 Toast 样式 + * @return {@link IToast.Operate} + */ + IToast.Operate defaultStyle(); + + /** + * 获取 Toast 样式配置 + * @return Toast 样式配置 + */ + IToast.Style getToastStyle(); + + /** + * 初始化 Toast 样式配置 + * @param toastStyle Toast 样式配置 + */ + void initStyle(IToast.Style toastStyle); + + /** + * 初始化 Toast 过滤器 + * @param toastFilter Toast 过滤器 + */ + void initToastFilter(IToast.Filter toastFilter); + + /** + * 设置 Toast 显示的 View + * @param view Toast 显示的 View + */ + void setView(View view); + + /** + * 设置 Toast 显示的 View + * @param layoutId R.layout.id + */ + void setView(@LayoutRes int layoutId); + + // ============ + // = 操作方法 = + // ============ + + /** + * 显示 Toast + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + void show(String text, Object... objs); + + /** + * 显示 R.string.id Toast + * @param resId R.string.id + * @param objs 格式化参数 + */ + void show(@StringRes int resId, Object... objs); + + // = + + /** + * 通过 View 显示 Toast + * @param view Toast 显示的 View + */ + void show(View view); + + /** + * 通过 View 显示 Toast + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + void show(View view, int duration); + + // = + + /** + * 取消当前显示的 Toast + */ + void cancel(); + } + + // ============ + // = 其他接口 = + // ============ + + /** + * detail: Toast 样式配置 + * @author Ttt + */ + public interface Style { + + /** + * 获取 Toast 的重心 + * @return Toast 的重心 + */ + int getGravity(); + + /** + * 获取 X 轴偏移 + * @return X 轴偏移 + */ + int getXOffset(); + + /** + * 获取 Y 轴偏移 + * @return Y 轴偏移 + */ + int getYOffset(); + + /** + * 获取水平边距 + * @return 水平边距 + */ + int getHorizontalMargin(); + + /** + * 获取垂直边距 + * @return 垂直边距 + */ + int getVerticalMargin(); + + /** + * 获取 Toast Z 轴坐标阴影 + * @return Toast Z 轴坐标阴影 + */ + int getZ(); + + /** + * 获取圆角大小 + * @return 圆角大小 + */ + float getCornerRadius(); + + /** + * 获取背景着色颜色 + * @return 背景着色颜色 + */ + @ColorInt + int getBackgroundTintColor(); + + /** + * 获取背景图片 + * @return 背景图片 + */ + Drawable getBackground(); + + // ================= + // = TextView 相关 = + // ================= + + /** + * 获取文本颜色 + * @return 文本颜色 + */ + @ColorInt + int getTextColor(); + + /** + * 获取字体大小 + * @return 字体大小 + */ + float getTextSize(); + + /** + * 获取最大行数 + * @return 最大行数 + */ + int getMaxLines(); + + /** + * 获取 Ellipsize 效果 + * @return Ellipsize 效果 + */ + TextUtils.TruncateAt getEllipsize(); + + /** + * 获取字体样式 + * @return 字体样式 + */ + Typeface getTypeface(); + + /** + * 获取 TextView padding 边距 - new int[] { left, top, right, bottom } + * @return TextView padding 边距 + */ + int[] getPadding(); + } + + /** + * detail: Toast 过滤器 + * @author Ttt + */ + public interface Filter { + + /** + * 判断是否显示 + * @param view Toast 显示的 View + * @return {@code true} 接着执行, {@code false} 过滤不处理 + */ + boolean filter(View view); + + /** + * 判断是否显示 + * @param content Toast 显示文案 + * @return {@code true} 接着执行, {@code false} 过滤不处理 + */ + boolean filter(String content); + + /** + * 获取 Toast 显示的文案 + * @param content Toast 显示文案 + * @return 处理后的内容 + */ + String handlerContent(String content); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/IToastImpl.java b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/IToastImpl.java new file mode 100644 index 0000000000..6d0beac42e --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/IToastImpl.java @@ -0,0 +1,571 @@ +package dev.utils.app.toast.toaster; + +import android.app.Application; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.LayoutRes; +import android.support.annotation.StringRes; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Toast 接口实现方法 ( 处理方法 ) + * @author Ttt + */ +final class IToastImpl implements IToast.Operate, IToast.Filter { + + // 日志 TAG + private final String TAG = IToastImpl.class.getSimpleName(); + // Application + private Application mApplication; + // 内部保存配置 Toast + private ToastFactory.BaseToast mConfigToast = null; + // 当前显示的 Toast + private ToastFactory.BaseToast mToast = null; + // Toast 样式信息 + private IToast.Style mToastStyle = null; + // Toast 判断过滤 + private IToast.Filter mToastFilter = null; + // Toast 默认样式 + private final IToast.Style mDefaultStyle = new DefaultToastStyle(); + // 每个线程的 Toast 样式 + private final ThreadLocal LOCAL_TOAST_STYLES = new ThreadLocal<>(); + // 判断是否使用 Handler + private boolean mIsHandler = true; + // 内部 Handler + private final Handler mHandler = new Handler(Looper.getMainLooper()); + // Null 值 - null 提示值 + private String mNullText = null; + // Toast 文案长度转换 显示时间 + private int mTextLengthConvertDuration = 15; + + /** + * 重置默认参数 + */ + @Override + public void reset() { + // 重新初始化 + init(mApplication); + } + + /** + * 设置是否使用 Handler 显示 Toast + * @param isHandler {@code true} 使用, {@code false} 不使用 + */ + @Override + public void setIsHandler(final boolean isHandler) { + this.mIsHandler = isHandler; + } + + /** + * 设置 Text 为 null 的文本 + * @param nullText 显示内容为 null 时, 使用的提示值 + */ + @Override + public void setNullText(final String nullText) { + this.mNullText = nullText; + } + + /** + * 设置 Toast 文案长度转换 显示时间 + * @param textLengthConvertDuration Toast 文案长度转换界限 + */ + @Override + public void setTextLength(final int textLengthConvertDuration) { + this.mTextLengthConvertDuration = textLengthConvertDuration; + } + + // ======================== + // = Application 中初始化 = + // ======================== + + /** + * Application 初始化调用 + * @param application {@link Application} + */ + @Override + public void init(final Application application) { + if (application != null) { + this.mApplication = application; + // 初始化默认参数 + mIsHandler = true; + mNullText = null; + // 初始化 Toast + mConfigToast = new ToastFactory.BaseToast(mApplication); + mConfigToast.setView(createView()); + // 初始化默认样式 + getToastStyle(); + } + } + + // ================================== + // = 实现 IToast 接口, 对外公开方法 = + // ================================== + + /** + * 使用单次 Toast 样式配置 + * @param toastStyle Toast 样式 + * @return {@link IToast.Operate} + */ + @Override + public IToast.Operate style(final IToast.Style toastStyle) { + if (toastStyle != null) { + LOCAL_TOAST_STYLES.set(toastStyle); + } + return this; + } + + /** + * 使用默认 Toast 样式 + * @return {@link IToast.Operate} + */ + @Override + public IToast.Operate defaultStyle() { + return style(mDefaultStyle); + } + + /** + * 获取 Toast 样式配置 + * @return Toast 样式配置 + */ + @Override + public IToast.Style getToastStyle() { + if (mToastStyle == null) { + mToastStyle = mDefaultStyle; + } + return mToastStyle; + } + + /** + * 初始化 Toast 样式配置 + * @param toastStyle Toast 样式配置 + */ + @Override + public void initStyle(final IToast.Style toastStyle) { + mToastStyle = toastStyle; + // 防止样式为 null + getToastStyle(); + } + + /** + * 初始化 Toast 过滤器 + * @param toastFilter Toast 过滤器 + */ + @Override + public void initToastFilter(final IToast.Filter toastFilter) { + mToastFilter = toastFilter; + } + + /** + * 设置 Toast 显示的 View + * @param view Toast 显示的 View + */ + @Override + public void setView(final View view) { + if (mConfigToast != null && view != null) { + mConfigToast.setView(view); + // 如果是 null, 则抛出异常 + if (mConfigToast.isEmptyMessageView()) { + // 如果设置的布局没有包含一个 TextView 则抛出异常, 必须要包含一个 TextView 作为 Message View + throw new IllegalArgumentException("The layout must contain a TextView"); + } + } + } + + /** + * 设置 Toast 显示的 View + * @param layoutId R.layout.id + */ + @Override + public void setView(@LayoutRes final int layoutId) { + if (mConfigToast != null) { + try { + setView(View.inflate(mConfigToast.getView().getContext().getApplicationContext(), layoutId, null)); + } catch (Exception e) { + } + // 如果是 null, 则抛出异常 + if (mConfigToast.isEmptyMessageView()) { + // 如果设置的布局没有包含一个 TextView 则抛出异常, 必须要包含一个 TextView 作为 Message View + throw new IllegalArgumentException("The layout must contain a TextView"); + } + } + } + + // ============ + // = 操作方法 = + // ============ + + /** + * 显示 Toast + * @param text Toast 提示文本 + * @param objs 格式化参数 + */ + @Override + public void show(final String text, final Object... objs) { + String context = Utils.getFormatString(text, objs); + if (filter(context)) { + priShowToastText(handlerContent(context)); + } + } + + /** + * 显示 R.string.id Toast + * @param resId R.string.id + * @param objs 格式化参数 + */ + @Override + public void show(@StringRes final int resId, final Object... objs) { + String context = Utils.getFormatRes(resId, objs); + if (filter(context)) { + // 获取处理的内容 + priShowToastText(handlerContent(context)); + } + } + + /** + * 通过 View 显示 Toast + * @param view Toast 显示的 View + */ + @Override + public void show(final View view) { + if (filter(view)) { + priShowToastView(view, Toast.LENGTH_SHORT); + } + } + + /** + * 通过 View 显示 Toast + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + @Override + public void show(final View view, final int duration) { + if (filter(view)) { + priShowToastView(view, duration); + } + } + + // = + + /** + * 取消当前显示的 Toast + */ + @Override + public void cancel() { + if (mToast != null) { + try { + mToast.cancel(); + } catch (Exception e) { + } + } + } + + // ========================== + // = IToast.Filter 实现方法 = + // ========================== + + /** + * 判断是否显示 + * @param view Toast 显示的 View + * @return {@code true} 接着执行, {@code false} 过滤不处理 + */ + @Override + public boolean filter(View view) { + if (mToastFilter != null) { + return mToastFilter.filter(view); + } + return true; + } + + /** + * 判断是否显示 + * @param content Toast 显示文案 + * @return {@code true} 接着执行, {@code false} 过滤不处理 + */ + @Override + public boolean filter(String content) { + if (mToastFilter != null) { + return mToastFilter.filter(content); + } + return true; + } + + /** + * 获取 Toast 显示的文案 + * @param content Toast 显示文案 + * @return 处理后的内容 + */ + @Override + public String handlerContent(String content) { + if (mToastFilter != null) { + return mToastFilter.handlerContent(content); + } + return content; + } + + // ============ + // = 内部处理 = + // ============ + + /** + * 返回对应线程的 Toast 样式信息 + * @return Toast 样式 + */ + private IToast.Style getThreadToastStyle() { + // 获取当前线程的线程的 Toast 样式 + IToast.Style toastStyle = LOCAL_TOAST_STYLES.get(); + // 如果等于 null, 则返回默认配置信息 + if (toastStyle == null) { + return getToastStyle(); + } else { + LOCAL_TOAST_STYLES.remove(); + } + // 如果存在当前线程的配置信息, 则返回 + return toastStyle; + } + + /** + * 默认创建 View + * @return {@link TextView} + */ + private TextView createView() { + TextView textView = new TextView(mApplication); + textView.setId(android.R.id.message); + textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + return textView; + } + + /** + * 内部私有方法, 最终显示 Toast + * @param text Toast 提示文本 + */ + private void priShowToastText(final String text) { + // 获取样式 + final IToast.Style style = getThreadToastStyle(); + if (mIsHandler) { + mHandler.post(new Runnable() { + @Override + public void run() { + try { + Toast toast = newToastText(style, text); + if (toast != null) { + toast.show(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "priShowToastText"); + } + } + }); + } else { + try { + Toast toast = newToastText(style, text); + if (toast != null) { + toast.show(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "priShowToastText"); + } + } + } + + /** + * 获取一个新的 Text Toast + * @param style Toast 样式 {@link IToast.Style} + * @param text Toast 提示文本 + * @return {@link Toast} + */ + private Toast newToastText(final IToast.Style style, final String text) { + if (style == null) return null; + // 提示文本 + String toastText = text; + // 设置为 null, 便于提示排查 + if (TextUtils.isEmpty(toastText)) { + toastText = mNullText; + // 如果还是为 null, 则不处理 + if (TextUtils.isEmpty(toastText)) { + return null; + } + } + try { + // 关闭旧的 Toast + if (mToast != null) { + mToast.cancel(); + mToast = null; + } + // 如果不存在 TextView, 直接跳过 + if (mConfigToast.isEmptyMessageView()) { + return null; + } + View view = mConfigToast.getView(); + // 获取 Toast TextView + TextView toastTextView = mConfigToast.getMessageView(); + // 设置文案 + toastTextView.setText(toastText); + // 设置字体颜色 + if (style.getTextColor() != 0) { + toastTextView.setTextColor(style.getTextColor()); + } + // 设置字体大小 + if (style.getTextSize() != 0f) { + toastTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, style.getTextSize()); + } + // 设置最大行数 + if (style.getMaxLines() >= 1) { + toastTextView.setMaxLines(style.getMaxLines()); + } + // 设置 Ellipsize 效果 + if (style.getEllipsize() != null) { + toastTextView.setEllipsize(style.getEllipsize()); + } + // 设置字体样式 + if (style.getTypeface() != null) { + toastTextView.setTypeface(style.getTypeface()); + } + // 设置 Z 轴阴影 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // 设置 Z 轴阴影 + toastTextView.setZ(style.getZ()); + } + // 设置边距 + if (style.getPadding() != null && style.getPadding().length == 4) { + int[] padding = style.getPadding(); + toastTextView.setPadding(padding[0], padding[1], padding[2], padding[3]); + } + + // 获取背景图片 + Drawable backgroundDrawable = style.getBackground(); + // 如果等于 null + if (backgroundDrawable != null) { + // 设置背景 + Utils.setBackground(view, backgroundDrawable); + } else { + if (style.getBackgroundTintColor() != 0) { + GradientDrawable drawable = new GradientDrawable(); + // 设置背景色 + drawable.setColor(style.getBackgroundTintColor()); + // 设置圆角大小 + drawable.setCornerRadius(style.getCornerRadius()); + // 设置背景 + Utils.setBackground(view, drawable); + } + } + + // 创建 Toast + mToast = ToastFactory.create(DevUtils.getContext()); + mToast.setView(view); + // 设置属性配置 + if (style.getGravity() != 0) { + // 设置 Toast 的重心、X、Y 轴偏移 + mToast.setGravity(style.getGravity(), style.getXOffset(), style.getYOffset()); + } + // 设置边距 + mToast.setMargin(style.getHorizontalMargin(), style.getVerticalMargin()); + // 设置显示时间 + mToast.setDuration((toastText.length() < mTextLengthConvertDuration) ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastText"); + } + return mToast; + } + + /** + * 显示 View Toast 方法 + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + */ + private void priShowToastView(final View view, final int duration) { + if (view == null) return; + // 获取样式 + final IToast.Style style = getThreadToastStyle(); + // = + if (mIsHandler) { + mHandler.post(new Runnable() { + @Override + public void run() { + try { + Toast toast = newToastView(style, view, duration); + if (toast != null) { + toast.show(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "priShowToastView"); + } + } + }); + } else { + try { + Toast toast = newToastView(style, view, duration); + if (toast != null) { + toast.show(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "priShowToastView"); + } + } + } + + /** + * 获取一个新的 View Toast + * @param style Toast 样式 {@link IToast.Style} + * @param view Toast 显示的 View + * @param duration Toast 显示时长 {@link Toast#LENGTH_SHORT}、{@link Toast#LENGTH_LONG} + * @return {@link Toast} + */ + private Toast newToastView(final IToast.Style style, final View view, final int duration) { + if (style == null) { + return null; + } else if (view == null) { // 防止显示的 View 为 null + return null; + } + try { + // 关闭旧的 Toast + if (mToast != null) { + mToast.cancel(); + mToast = null; + } + // 获取背景图片 + Drawable backgroundDrawable = style.getBackground(); + // 如果等于 null + if (backgroundDrawable != null) { + // 设置背景 + Utils.setBackground(view, backgroundDrawable); + } else { + if (style.getBackgroundTintColor() != 0) { + GradientDrawable drawable = new GradientDrawable(); + // 设置背景色 + drawable.setColor(style.getBackgroundTintColor()); + // 设置圆角大小 + drawable.setCornerRadius(style.getCornerRadius()); + // 设置背景 + Utils.setBackground(view, drawable); + } + } + + // 创建 Toast + mToast = ToastFactory.create(DevUtils.getContext()); + mToast.setView(view); + // 设置属性配置 + if (style.getGravity() != 0) { + // 设置 Toast 的重心、X、Y 轴偏移 + mToast.setGravity(style.getGravity(), style.getXOffset(), style.getYOffset()); + } + // 设置边距 + mToast.setMargin(style.getHorizontalMargin(), style.getVerticalMargin()); + // 设置显示时间 + mToast.setDuration(duration); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastView"); + } + return mToast; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/ToastFactory.java b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/ToastFactory.java new file mode 100644 index 0000000000..6add7a7cca --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/ToastFactory.java @@ -0,0 +1,354 @@ +package dev.utils.app.toast.toaster; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.PixelFormat; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.TextView; +import android.widget.Toast; + +import java.lang.reflect.Field; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Toast 工厂模式 + * @author Ttt + */ +final class ToastFactory { + + private ToastFactory() { + } + + // 日志 TAG + private static final String TAG = ToastFactory.class.getSimpleName(); + + /** + * detail: Toast 基类 + * @author Ttt + */ + static class BaseToast extends Toast { + + // Toast 消息 View + private TextView mMessageView; + + /** + * 构造函数 + * @param context {@link Context} + */ + public BaseToast(Context context) { + super(context); + } + + @Override + public final void setView(View view) { + super.setView(view); + if (view instanceof TextView) { + mMessageView = (TextView) view; + return; + } else if (view.findViewById(android.R.id.message) instanceof TextView) { + mMessageView = view.findViewById(android.R.id.message); + return; + } else if (view instanceof ViewGroup) { + if ((mMessageView = findTextView((ViewGroup) view)) != null) return; + } + } + + @Override + public final void setText(CharSequence s) { + if (mMessageView != null) { + mMessageView.setText(s); + } + } + + /** + * 递归获取 ViewGroup 中的 TextView 对象 + * @param group {@link ViewGroup} + * @return {@link TextView} + */ + private TextView findTextView(final ViewGroup group) { + for (int i = 0; i < group.getChildCount(); i++) { + View view = group.getChildAt(i); + if ((view instanceof TextView)) { + return (TextView) view; + } else if (view instanceof ViewGroup) { + TextView textView = findTextView((ViewGroup) view); + if (textView != null) return textView; + } + } + return null; + } + + /** + * 判断是否 null 的 Message View + * @return {@code true} yes, {@code false} no + */ + public final boolean isEmptyMessageView() { + return mMessageView == null; + } + + /** + * 获取 TextView + * @return {@link TextView} + */ + public TextView getMessageView() { + return mMessageView; + } + +// // = +// +// /** +// * 获取系统 Toast View +// * @param context {@link Context} +// * @return {@link View} +// */ +// public static final View getToastSystemView(Context context) { +// return Toast.makeText(context, "", Toast.LENGTH_SHORT).getView(); +// } +// +// /** +// * 设置系统 Toast View +// * @param context {@link Context} +// * @return {@link BaseToast} +// */ +// public final BaseToast setToastSystemView(Context context) { +// setView(getToastSystemView(context)); +// return this; +// } + } + + /** + * detail: 解决 Android 7.1 Toast 崩溃问题 + * @author Ttt + */ + static final class SafeToast extends BaseToast { + + /** + * 构造函数 + * @param context {@link Context} + */ + public SafeToast(Context context) { + super(context); + // 反射设置 Toat Handler 解决 Android 7.1.1 Toast 崩溃问题 + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) { + try { + Field field_tn = Toast.class.getDeclaredField("mTN"); + field_tn.setAccessible(true); + + Object mTN = field_tn.get(this); + Field field_handler = field_tn.getType().getDeclaredField("mHandler"); + field_handler.setAccessible(true); + + Handler handler = (Handler) field_handler.get(mTN); + field_handler.set(mTN, new SafeHandler(handler)); + } catch (Exception ignored) { + } + } + } + + /** + * detail: Toast 安全显示 Handler + * @author Ttt + */ + static final class SafeHandler extends Handler { + + private Handler mHandler; + + SafeHandler(Handler handler) { + mHandler = handler; + } + + @Override + public void handleMessage(Message msg) { + mHandler.handleMessage(msg); + } + + @Override + public void dispatchMessage(Message msg) { + try { + mHandler.dispatchMessage(msg); + } catch (Exception ignored) { + } + } + } + } + + /** + * detail: 通知栏显示 Toast + * @author Ttt + */ + static final class NotificationToast extends BaseToast { + + // Toast Window 显示辅助类 + private final ToastHelper mToastHelper; + + /** + * 构造函数 + * @param context {@link Context} + */ + public NotificationToast(Context context) { + super(context); + // 初始化操作 + mToastHelper = new ToastHelper(this); + } + + @Override + public void show() { + // 显示 Toast + mToastHelper.show(); + } + + @Override + public void cancel() { + // 取消显示 + mToastHelper.cancel(); + } + } + + // = + + /** + * 创建 Toast + * @param context {@link Context} + * @return {@link BaseToast} + */ + public static BaseToast create(final Context context) { + if (Utils.isNotificationEnabled(context)) { + return new SafeToast(context); + } + return new NotificationToast(context); + } + + // = + + /** + * detail: Toast Window 显示辅助类 + * @author Ttt + *
+     *     参考 Toast.TN 实现方式
+     * 
+ */ + static final class ToastHelper extends Handler { + + // 当前 Toast 对象 + private final Toast mToast; + // 判断是否显示中 + private boolean mShow; + + ToastHelper(Toast toast) { + super(Looper.getMainLooper()); + mToast = toast; + } + + @Override + public void handleMessage(Message msg) { + cancel(); + } + + /*** + * 显示 Toast 弹窗 + */ + void show() { + if (!mShow) { + try { + if (mToast == null) { + return; + } + // 获取 View + View view = mToast.getView(); + // 防止 View 为 null + if (view == null) { + return; + } + // 获取 Context + Context context = view.getContext().getApplicationContext(); + if (context == null) { + context = view.getContext(); + } + // 获取包名 + String packageName = context.getPackageName(); + + // = + + WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + // 设置参数 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) { + params.type = WindowManager.LayoutParams.TYPE_TOAST; + } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) { + params.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + } else { + params.type = WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW + 37; + } + + params.height = WindowManager.LayoutParams.WRAP_CONTENT; + params.width = WindowManager.LayoutParams.WRAP_CONTENT; + params.format = PixelFormat.TRANSLUCENT; + params.windowAnimations = android.R.style.Animation_Toast; + params.setTitle(Toast.class.getSimpleName()); + params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + + // Toast 的重心 + int gravity = mToast.getGravity(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + Configuration config = context.getResources().getConfiguration(); + gravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection()); + } + if (gravity != 0) { + params.gravity = gravity; + // 判断是否铺满整个方向 + if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { + params.horizontalWeight = 1.0f; + } + if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) { + params.verticalWeight = 1.0f; + } + } + // 设置 X、Y 轴偏移 + params.x = mToast.getXOffset(); + params.y = mToast.getYOffset(); + // 设置水平边距、垂直边距 + params.verticalMargin = mToast.getVerticalMargin(); + params.horizontalMargin = mToast.getHorizontalMargin(); + // 设置包名 + params.packageName = packageName; + + // View 对象不能重复添加, 否则会抛出异常 + Utils.getWindowManager(DevUtils.getTopActivity()).addView(mToast.getView(), params); + // 当前已经显示 + mShow = true; + // 添加一个移除 Toast 的任务 + sendEmptyMessageDelayed(0, mToast.getDuration() == Toast.LENGTH_LONG ? 3500 : 2000); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "ToastHelper - show"); + } + } + } + + /** + * 取消 Toast 弹窗 + */ + void cancel() { + // 移除之前移除 Toast 的任务 + removeMessages(0); + // 如果显示中, 则移除 View + if (mShow) { + try { + Utils.getWindowManager(DevUtils.getTopActivity()).removeView(mToast.getView()); + } catch (Exception ignored) { + } + // 当前没有显示 + mShow = false; + } + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/Utils.java b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/Utils.java new file mode 100644 index 0000000000..4f28296636 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/Utils.java @@ -0,0 +1,208 @@ +package dev.utils.app.toast.toaster; + +import android.app.Activity; +import android.app.AppOpsManager; +import android.app.NotificationManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.NinePatchDrawable; +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; +import android.view.View; +import android.view.WindowManager; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Toast 内部工具类 + * @author Ttt + */ +final class Utils { + + private Utils() { + } + + // 日志 TAG + private static final String TAG = Utils.class.getSimpleName(); + + /** + * 获取格式化字符串 + * @param format 待格式化字符串 + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getFormatString(final String format, final Object... args) { + try { + if (args != null && args.length != 0) { + return String.format(format, args); + } else { + return format; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getFormatString"); + } + return null; + } + + /** + * 获取 R.string 资源的格式化字符串 + * @param resId R.string.id + * @param objs 格式化参数 + * @return 格式化后的字符串 + */ + public static String getFormatRes(@StringRes final int resId, final Object... objs) { + try { + if (objs != null && objs.length != 0) { + return DevUtils.getContext().getString(resId, objs); + } else { + return DevUtils.getContext().getString(resId); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getFormatRes"); + } + return null; + } + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + * 检查通知栏权限是否开启 + * 参考 SupportCompat 包中的: NotificationManagerCompat.from(context).areNotificationsEnabled(); + * @param context {@link Context} + * @return {@code true} yes, {@code false} no + */ + public static boolean isNotificationEnabled(final Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).areNotificationsEnabled(); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + try { + AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + ApplicationInfo appInfo = context.getApplicationInfo(); + String pkg = context.getApplicationContext().getPackageName(); + int uid = appInfo.uid; + + Class appOpsClass = Class.forName(AppOpsManager.class.getName()); + Method checkOpNoThrowMethod = appOpsClass.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class); + Field opPostNotificationValue = appOpsClass.getDeclaredField("OP_POST_NOTIFICATION"); + int value = (Integer) opPostNotificationValue.get(Integer.class); + return (Integer) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg) == 0; + } catch (Throwable ignored) { + return true; + } + } else { + return true; + } + } + + /** + * 获取一个对象的独一无二的标记 + * @param object 对象 + * @return 对象唯一标记 + */ + public static String getObjectTag(final Object object) { + if (object == null) return null; + // 对象所在的包名 + 对象的内存地址 + return object.getClass().getName() + Integer.toHexString(object.hashCode()); + } + + /** + * 获取一个 WindowManager 对象 + * @param activity {@link Activity} + * @return {@link WindowManager} + */ + public static WindowManager getWindowManager(final Activity activity) { + // 如果使用的 WindowManager 对象不是当前 Activity 创建的, 则会抛出异常 + // android.view.WindowManager$BadTokenException: Unable to add window - token null is not for an application + if (activity != null) { + try { + return ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWindowManager"); + } + } + return null; + } + + // = + + /** + * 设置背景 + * @param view {@link View} + * @param drawable 背景 {@link Drawable} + */ + public static void setBackground(final View view, final Drawable drawable) { + if (view != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) + view.setBackground(drawable); + else + view.setBackgroundDrawable(drawable); + } + } + + /** + * 获取 Drawable + * @param context {@link Context} + * @param id R.drawable.id + * @return {@link Drawable} + */ + public static Drawable getDrawable(final Context context, @DrawableRes final int id) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + return context.getDrawable(id); + else + return context.getResources().getDrawable(id); + } + + /** + * 图片着色 + * @param drawable {@link Drawable} + * @param tintColor R.color.id + * @return {@link Drawable} + */ + public static Drawable tintIcon(final Drawable drawable, @ColorInt final int tintColor) { + if (drawable != null) { + try { + drawable.setColorFilter(tintColor, PorterDuff.Mode.SRC_IN); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "tintIcon"); + } + } + return drawable; + } + + /** + * .9 图片着色 + * @param context {@link Context} + * @param tintColor R.color.id + * @return {@link Drawable} + */ + public static Drawable tint9PatchDrawableFrame(final Context context, @ColorInt final int tintColor) { + try { + final NinePatchDrawable toastDrawable = (NinePatchDrawable) getDrawable(context, dev.utils.R.drawable.dev_toast_frame); + return tintIcon(toastDrawable, tintColor); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "tint9PatchDrawableFrame"); + } + return null; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiHotUtils.java b/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiHotUtils.java similarity index 51% rename from DevLibUtils/src/main/java/dev/utils/app/wifi/WifiHotUtils.java rename to lib/DevApp/src/main/java/dev/utils/app/wifi/WifiHotUtils.java index f0385382ff..8e2342623c 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiHotUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiHotUtils.java @@ -14,49 +14,70 @@ import java.io.FileReader; import java.lang.reflect.Method; +import dev.DevUtils; import dev.utils.LogPrintUtils; /** * detail: Wifi 热点工具类 - * Created by Ttt - * - * - * - * - * - * - * Android 8.0 开始, 热点操作方法, 已经变更 - * https://blog.csdn.net/bukker/article/details/78649504 - * android 7.1 系统以上不支持自动开启热点,需要手动开启热点 - * https://www.jianshu.com/p/9dbb02c3e21f + * @author Ttt + *
+ *     所需权限
+ *     
+ *     
+ *     
+ *     
+ *     
+ *     
+ * 
*/ -public class WifiHotUtils { +public final class WifiHotUtils { - // 日志TAG - private final static String TAG = WifiHotUtils.class.getSimpleName(); - // 对应的上下文 + // 日志 TAG + private static final String TAG = WifiHotUtils.class.getSimpleName(); + // Context private Context mContext; - /** 定义WifiManager对象 */ + // WifiManager 对象 private WifiManager mWifiManager; - // 热点 Wifi 配置 - private WifiConfiguration apWifiConfig; + // Wifi 热点配置 + private WifiConfiguration mAPWifiConfig; - /** 构造器(只能进行初始化WifiManager操作,其他靠方法定义) */ - public WifiHotUtils(Context context) { + /** + * 构造函数 + */ + public WifiHotUtils() { + this(DevUtils.getContext()); + } + + /** + * 构造函数 + * @param context {@link Context} + */ + public WifiHotUtils(final Context context) { this.mContext = context; - // 初始化WifiManager对象 + // 初始化 WifiManager 对象 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); } - // == Wifi 操作 == + // ============= + // = Wifi 操作 = + // ============= + + /** + * 创建 wifi 热点配置 ( 支持 无密码 /WPA2 PSK) + * @param ssid wifi ssid + * @return {@link WifiConfiguration} 热点配置 + */ + public static WifiConfiguration createWifiConfigToAp(final String ssid) { + return createWifiConfigToAp(ssid, null); + } /** - * 创建Wifi配置信息(无其他操作,单独返回WifiConfig) => Wifi热点 (就支持 无密码/WPA2 PSK) - * @param ssid - * @param pwd 密码需要大于等于8位 - * @return + * 创建 wifi 热点配置 ( 支持 无密码 /WPA2 PSK) + * @param ssid wifi ssid + * @param pwd 密码 ( 需要大于等于 8 位 ) + * @return {@link WifiConfiguration} */ - public static WifiConfiguration createWifiConfigToAp(String ssid, String pwd) { + public static WifiConfiguration createWifiConfigToAp(final String ssid, final String pwd) { try { // 创建一个新的网络配置 WifiConfiguration wifiConfig = new WifiConfiguration(); @@ -66,7 +87,7 @@ public static WifiConfiguration createWifiConfigToAp(String ssid, String pwd) { wifiConfig.allowedPairwiseCiphers.clear(); wifiConfig.allowedProtocols.clear(); wifiConfig.priority = 0; - /** 设置连接的SSID */ + // 设置连接的 SSID wifiConfig.SSID = ssid; // 判断密码 if (TextUtils.isEmpty(pwd)) { @@ -85,27 +106,27 @@ public static WifiConfiguration createWifiConfigToAp(String ssid, String pwd) { wifiConfig.status = WifiConfiguration.Status.ENABLED; } return wifiConfig; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "createWifiConfigToAp"); } return null; } /** - * 开启Wifi热点 - * @param wifiConfig wifi配置 + * 开启 wifi 热点 + * @param wifiConfig wifi 配置 */ - public void stratWifiAp(WifiConfiguration wifiConfig) { - this.apWifiConfig = wifiConfig; + public void stratWifiAp(final WifiConfiguration wifiConfig) { + this.mAPWifiConfig = wifiConfig; // 大于 8.0 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 关闭热点 - if (mReservation != null){ + if (mReservation != null) { mReservation.close(); } // 清空信息 - apWifiSSID = apWifiPwd = null; - // Android 8.0 是基于应用开启的, 必须使用固定生成的配置进行发送连接, 无法进行控制(App 关闭后, 热点自动关闭) + mAPWifiSSID = mAPWifiPwd = null; + // Android 8.0 是基于应用开启的, 必须使用固定生成的配置进行创建, 无法进行控制 (APP 关闭后, 热点自动关闭 ) // 必须有定位权限 mWifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() { @Override @@ -115,13 +136,13 @@ public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) { mReservation = reservation; // 获取配置信息 WifiConfiguration wifiConfiguration = reservation.getWifiConfiguration(); - apWifiSSID = wifiConfiguration.SSID; - apWifiPwd = wifiConfiguration.preSharedKey; + mAPWifiSSID = wifiConfiguration.SSID; + mAPWifiPwd = wifiConfiguration.preSharedKey; // 打印信息 - LogPrintUtils.dTag(TAG, "Android 8.0 wifi Ap ssid: " + apWifiSSID + ", pwd: " + apWifiPwd); + LogPrintUtils.dTag(TAG, "Android 8.0 onStarted wifiAp ssid: " + mAPWifiSSID + ", pwd: " + mAPWifiPwd); // 触发回调 - if (wifiAPListener != null){ - wifiAPListener.onStarted(wifiConfiguration); + if (mWifiAPListener != null) { + mWifiAPListener.onStarted(wifiConfiguration); } } @@ -131,8 +152,8 @@ public void onStopped() { // 打印信息 LogPrintUtils.dTag(TAG, "Android 8.0 onStopped wifiAp"); // 触发回调 - if (wifiAPListener != null){ - wifiAPListener.onStopped(); + if (mWifiAPListener != null) { + mWifiAPListener.onStopped(); } } @@ -140,28 +161,29 @@ public void onStopped() { public void onFailed(int reason) { super.onFailed(reason); // 打印信息 - LogPrintUtils.eTag(TAG, "Android 8.0 onFailed wifiAp, reason : " + reason); + LogPrintUtils.eTag(TAG, "Android 8.0 onFailed wifiAp, reason: " + reason); // 触发回调 - if (wifiAPListener != null){ - wifiAPListener.onFailed(reason); + if (mWifiAPListener != null) { + mWifiAPListener.onFailed(reason); } } }, null); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1){ // android 7.1 系统以上不支持自动开启热点,需要手动开启热点 - // 先设置wifi热点信息, 这样跳转前保存热点信息, 开启热点则是对应设置的信息 + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { // android 7.1 系统以上不支持自动开启热点, 需要手动开启热点 + // 先设置 wifi 热点信息, 这样跳转前保存热点信息, 开启热点则是对应设置的信息 boolean setResult = setWifiApConfiguration(wifiConfig); // 打印日志 - LogPrintUtils.dTag(TAG, "设置Wifi 热点信息是否成功: " + setResult); - // https://www.cnblogs.com/bluestorm/p/3665555.html + LogPrintUtils.dTag(TAG, "设置 wifi 热点信息是否成功: " + setResult); + // 跳转到便携式热点设置页面 Intent intent = new Intent(); intent.setAction(Intent.ACTION_MAIN); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setComponent(new ComponentName("com.android.settings", "com.android.settings.TetherSettings")); mContext.startActivity(intent); } else { try { - // 获取设置Wifi热点方法 + // 获取设置 wifi 热点方法 Method method = mWifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class); - // 开启Wifi热点 + // 开启 wifi 热点 method.invoke(mWifiManager, wifiConfig, true); } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "stratWifiAp"); @@ -170,21 +192,21 @@ public void onFailed(int reason) { } /** - * 关闭Wifi热点 + * 关闭 wifi 热点 */ - public void closeWifiAp(){ + public void closeWifiAp() { // 大于 8.0 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 关闭热点 - if (mReservation != null){ + if (mReservation != null) { mReservation.close(); } // 清空信息 - apWifiSSID = apWifiPwd = null; + mAPWifiSSID = mAPWifiPwd = null; return; } try { - // 获取设置Wifi热点方法 + // 获取设置 wifi 热点方法 Method method = mWifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class); // 创建一个新的网络配置 WifiConfiguration wifiConfig = new WifiConfiguration(); @@ -194,11 +216,11 @@ public void closeWifiAp(){ wifiConfig.allowedPairwiseCiphers.clear(); wifiConfig.allowedProtocols.clear(); wifiConfig.priority = 0; - // 设置Wifi SSID + // 设置 wifi ssid wifiConfig.SSID = "CloseWifiAp"; // formatSSID(ssid,true); - // 设置Wifi密码 + // 设置 wifi 密码 wifiConfig.preSharedKey = "CloseWifiAp"; - // 设置Wifi属性 + // 设置 wifi 属性 wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN); wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA); @@ -207,34 +229,47 @@ public void closeWifiAp(){ wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); - // 开启Wifi热点 + // 开启 wifi 热点 method.invoke(mWifiManager, wifiConfig, false); } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "closeWifiAp"); } } - // ========================== 手机热点功能 ========================== - /** Wifi热点正在关闭 -- WifiManager.WIFI_AP_STATE_DISABLING */ + // ================ + // = 手机热点功能 = + // ================ + + /** + * wifi 热点正在关闭 - WifiManager.WIFI_AP_STATE_DISABLING + */ public static final int WIFI_AP_STATE_DISABLING = 10; - /** Wifi热点已关闭 -- WifiManager.WIFI_AP_STATE_DISABLED */ + /** + * wifi 热点已关闭 - WifiManager.WIFI_AP_STATE_DISABLED + */ public static final int WIFI_AP_STATE_DISABLED = 11; - /** Wifi热点正在打开 -- WifiManager.WIFI_AP_STATE_ENABLING */ + /** + * wifi 热点正在打开 - WifiManager.WIFI_AP_STATE_ENABLING + */ public static final int WIFI_AP_STATE_ENABLING = 12; - /** Wifi热点已打开 -- WifiManager.WIFI_AP_STATE_ENABLED */ + /** + * wifi 热点已打开 - WifiManager.WIFI_AP_STATE_ENABLED + */ public static final int WIFI_AP_STATE_ENABLED = 13; - /** Wifi热点状态未知 -- WifiManager.WIFI_AP_STATE_FAILED */ + /** + * wifi 热点状态未知 - WifiManager.WIFI_AP_STATE_FAILED + */ public static final int WIFI_AP_STATE_FAILED = 14; /** - * 获取Wifi热点状态 - * @return + * 获取 wifi 热点状态 + * @return wifi 热点状态 */ public int getWifiApState() { try { // 反射获取方法 Method method = mWifiManager.getClass().getMethod("getWifiApState"); - // 调用方法,获取状态 + // 调用方法, 获取状态 int wifiApState = (Integer) method.invoke(mWifiManager); // 打印状态 LogPrintUtils.dTag(TAG, "WifiApState: " + wifiApState); @@ -246,12 +281,12 @@ public int getWifiApState() { } /** - * 获取Wifi热点配置信息 - * @return + * 获取 wifi 热点配置信息 + * @return {@link WifiConfiguration} 热点配置 */ - public WifiConfiguration getWifiApConfiguration(){ + public WifiConfiguration getWifiApConfiguration() { try { - // 获取Wifi热点方法 + // 获取 wifi 热点方法 Method method = mWifiManager.getClass().getMethod("getWifiApConfiguration"); // 获取配置 WifiConfiguration wifiApConfig = (WifiConfiguration) method.invoke(mWifiManager); @@ -264,17 +299,17 @@ public WifiConfiguration getWifiApConfiguration(){ } /** - * 设置Wifi热点配置信息 - * @param apWifiConfig - * @return 是否成功 + * 设置 wifi 热点配置信息 + * @param apWifiConfig wifi 热点配置 + * @return {@code true} success, {@code false} fail */ - public boolean setWifiApConfiguration(WifiConfiguration apWifiConfig){ + public boolean setWifiApConfiguration(final WifiConfiguration apWifiConfig) { try { - // 获取设置Wifi热点方法 + // 获取设置 wifi 热点方法 Method method = mWifiManager.getClass().getMethod("setWifiApConfiguration", WifiConfiguration.class); - // 开启Wifi热点 + // 开启 wifi 热点 return (boolean) method.invoke(mWifiManager, apWifiConfig); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setWifiApConfiguration"); } return false; @@ -283,75 +318,77 @@ public boolean setWifiApConfiguration(WifiConfiguration apWifiConfig){ // = /** - * 判断是否打开Wifi热点 - * @return + * 判断是否打开 wifi 热点 + * @return {@code true} yes, {@code false} no */ - public boolean isOpenWifiAp(){ - // 判断是否开启热点(默认未打开) + public boolean isOpenWifiAp() { + // 判断是否开启热点 ( 默认未打开 ) boolean isOpen = false; - // 获取当前Wifi热点状态 + // 获取当前 wifi 热点状态 int wifiApState = getWifiApState(); - switch(wifiApState){ - case WIFI_AP_STATE_DISABLING: // Wifi热点正在关闭 + switch (wifiApState) { + case WIFI_AP_STATE_DISABLING: // wifi 热点正在关闭 break; - case WIFI_AP_STATE_DISABLED: // Wifi热点已关闭 + case WIFI_AP_STATE_DISABLED: // wifi 热点已关闭 break; - case WIFI_AP_STATE_ENABLING: // Wifi热点正在打开 + case WIFI_AP_STATE_ENABLING: // wifi 热点正在打开 break; - case WIFI_AP_STATE_ENABLED: // Wifi热点已打开 + case WIFI_AP_STATE_ENABLED: // wifi 热点已打开 isOpen = true; break; - case WIFI_AP_STATE_FAILED: // Wifi热点状态未知 + case WIFI_AP_STATE_FAILED: // wifi 热点状态未知 break; } return isOpen; } /** - * 关闭Wifi热点(判断当前状态) + * 关闭 wifi 热点 ( 判断当前状态 ) * @param isExecute 是否执行关闭 - * @return 之前是否打开热点 + * @return {@code true} success, {@code false} fail */ - public boolean closeWifiApCheck(boolean isExecute){ - // 判断是否开启热点(默认是) + public boolean closeWifiApCheck(final boolean isExecute) { + // 判断是否开启热点 ( 默认是 ) boolean isOpen = true; - // 获取当前Wifi热点状态 + // 判断是否执行关闭 + boolean isExecuteClose = isExecute; + // 获取当前 wifi 热点状态 int wifiApState = getWifiApState(); - switch(wifiApState){ - case WIFI_AP_STATE_DISABLING: // Wifi热点正在关闭 - isExecute = false; + switch (wifiApState) { + case WIFI_AP_STATE_DISABLING: // wifi 热点正在关闭 + isExecuteClose = false; break; - case WIFI_AP_STATE_DISABLED: // Wifi热点已关闭 + case WIFI_AP_STATE_DISABLED: // wifi 热点已关闭 isOpen = false; break; - case WIFI_AP_STATE_ENABLING: // Wifi热点正在打开 + case WIFI_AP_STATE_ENABLING: // wifi 热点正在打开 break; - case WIFI_AP_STATE_ENABLED: // Wifi热点已打开 + case WIFI_AP_STATE_ENABLED: // wifi 热点已打开 break; - case WIFI_AP_STATE_FAILED: // Wifi热点状态未知 + case WIFI_AP_STATE_FAILED: // wifi 热点状态未知 break; } - // 如果属于开启,则进行关闭 - if(isOpen && isExecute){ + // 如果属于开启, 则进行关闭 + if (isOpen && isExecuteClose) { closeWifiAp(); } return isOpen; } /** - * 是否有连接热点 - * @return + * 是否有设备连接热点 + * @return {@code true} yes, {@code false} no */ - public boolean isConnectHot(){ + public boolean isConnectHot() { try { BufferedReader br = new BufferedReader(new FileReader("/proc/net/arp")); String line; while ((line = br.readLine()) != null) { String[] splitted = line.split(" +"); if (splitted != null && splitted.length >= 4) { - String ipAdr = splitted[0]; // Ip地址 - // 防止地址为null,并且需要以.拆分 存在4个长度 255.255.255.255 - if(ipAdr != null && ipAdr.split("\\.").length >= 3){ + String ipAddress = splitted[0]; // IP 地址 + // 防止地址为 null, 并且需要以. 拆分存在 4 个长度 255.255.255.255 + if (ipAddress != null && ipAddress.split("\\.").length >= 3) { return true; } } @@ -363,15 +400,15 @@ public boolean isConnectHot(){ } /** - * 获取热点主机ip地址 - * @return + * 获取热点主机 IP 地址 + * @return 热点主机 IP 地址 */ - public String getHotspotServiceIp(){ + public String getHotspotServiceIp() { try { // 获取网关信息 - DhcpInfo dhcpinfo = mWifiManager.getDhcpInfo(); + DhcpInfo dhcpInfo = mWifiManager.getDhcpInfo(); // 获取服务器地址 - return intToString(dhcpinfo.serverAddress); + return intToString(dhcpInfo.serverAddress); } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getHotspotServiceIp"); } @@ -379,20 +416,20 @@ public String getHotspotServiceIp(){ } /** - * 获取连接上的子网关热点IP(一个) - * @return + * 获取连接上的子网关热点 IP ( 一个 ) + * @return 连接上的子网关热点 IP */ - public String getHotspotAllotIp(){ + public String getHotspotAllotIp() { try { BufferedReader br = new BufferedReader(new FileReader("/proc/net/arp")); String line; while ((line = br.readLine()) != null) { String[] splitted = line.split(" +"); if (splitted != null && splitted.length >= 4) { - String ipAdr = splitted[0]; // Ip地址 - // 防止地址为null,并且需要以.拆分 存在4个长度 255.255.255.255 - if(ipAdr != null && ipAdr.split("\\.").length >= 3){ - return ipAdr; + String ipAddress = splitted[0]; // IP 地址 + // 防止地址为 null, 并且需要以. 拆分存在 4 个长度 255.255.255.255 + if (ipAddress != null && ipAddress.split("\\.").length >= 3) { + return ipAddress; } } } @@ -403,101 +440,131 @@ public String getHotspotAllotIp(){ } /** - * 获取热点拼接后的ip网关掩码 - * @param df 默认网关掩码 - * @param ipAdr ip地址 - * @return + * 获取连接的热点信息 + * @return 连接的热点信息 */ - public String getHotspotSplitIpMask(String df, String ipAdr){ - // 网关掩码 - String hsMask = df; - // 获取网关掩码 - if(ipAdr != null){ - try { - int length = ipAdr.lastIndexOf("."); - // 进行裁剪 - hsMask = ipAdr.substring(0,length) + ".255"; - } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getHotspotSplitIpMask"); + public String getConnectHotspotMsg() { + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader("/proc/net/arp")); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + builder.append(line); + } + return builder.toString(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getHotspotAllotIp"); + } finally { + if (br != null) { + try { + br.close(); + } catch (Exception e) { + } } } - return hsMask; + return null; } // /** -// * 获取连接的热点信息(暂时不用) -// * @return +// * 获取连接的热点信息 +// * @return 连接的热点信息 // */ -// private void getConnectedHotMsg() { +// private String getConnectHotspotMsg() { // try { // BufferedReader br = new BufferedReader(new FileReader("/proc/net/arp")); // String line; // while ((line = br.readLine()) != null) { // String[] splitted = line.split(" +"); // if (splitted != null && splitted.length >= 4) { -// String ip = splitted[0]; // Ip地址 +// String ip = splitted[0]; // IP 地址 // String mac = splitted[3]; // Mac 地址 // } // } // } catch (Exception e) { -// LogPrintUtils.eTag(TAG, e, "getConnectedHotMsg"); +// LogPrintUtils.eTag(TAG, e, "getConnectHotspotMsg"); // } +// return null; // } /** - * 转换ip地址 - * @param data 需要转换的数据 - * @return + * 获取热点拼接后的 IP 网关掩码 + * @param defaultGateway 默认网关掩码 + * @param ipAddress IP 地址 + * @return 网关掩码 + */ + public String getHotspotSplitIpMask(final String defaultGateway, final String ipAddress) { + // 网关掩码 + String hsMask = defaultGateway; + // 获取网关掩码 + if (ipAddress != null) { + try { + int length = ipAddress.lastIndexOf("."); + // 进行裁剪 + hsMask = ipAddress.substring(0, length) + ".255"; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getHotspotSplitIpMask"); + } + } + return hsMask; + } + + /** + * 转换 IP 地址 + * @param data 待转换的数据 + * @return 转换后的 IP 地址 */ - private String intToString(int data) { - StringBuffer sb = new StringBuffer(); + private String intToString(final int data) { + StringBuilder builder = new StringBuilder(); int b = (data >> 0) & 0xff; - sb.append(b + "."); + builder.append(b + "."); b = (data >> 8) & 0xff; - sb.append(b + "."); + builder.append(b + "."); b = (data >> 16) & 0xff; - sb.append(b + "."); + builder.append(b + "."); b = (data >> 24) & 0xff; - sb.append(b); - return sb.toString(); + builder.append(b); + return builder.toString(); } - // == Android 8.0相关 == + // ==================== + // = Android 8.0 相关 = + // ==================== - // Wifi ssid - private String apWifiSSID; + // wifi ssid + private String mAPWifiSSID; // wifi 密码 - private String apWifiPwd; + private String mAPWifiPwd; // wifi 热点对象 private WifiManager.LocalOnlyHotspotReservation mReservation; /** - * 获取Wifi 热点名 - * @return + * 获取 wifi 热点名 + * @return wifi ssid */ public String getApWifiSSID() { // 大于 8.0 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ - return apWifiSSID; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return mAPWifiSSID; } else { - if (apWifiConfig != null){ - return apWifiConfig.SSID; + if (mAPWifiConfig != null) { + return mAPWifiConfig.SSID; } } return null; } /** - * 获取Wifi 热点密码 - * @return + * 获取 wifi 热点密码 + * @return wifi 热点密码 */ public String getApWifiPwd() { // 大于 8.0 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ - return apWifiPwd; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return mAPWifiPwd; } else { - if (apWifiConfig != null){ - return apWifiConfig.preSharedKey; + if (mAPWifiConfig != null) { + return mAPWifiConfig.preSharedKey; } } return null; @@ -505,23 +572,41 @@ public String getApWifiPwd() { // = - private onWifiAPListener wifiAPListener; + // wifi 热点监听 + private OnWifiAPListener mWifiAPListener; + /** + * 设置 wifi 热点监听事件 + * @param wifiAPListener {@link OnWifiAPListener} + * @return {@link WifiHotUtils} + */ @RequiresApi(api = Build.VERSION_CODES.O) - public void setOnWifiAPListener(onWifiAPListener wifiAPListener) { - this.wifiAPListener = wifiAPListener; + public WifiHotUtils setOnWifiAPListener(final OnWifiAPListener wifiAPListener) { + this.mWifiAPListener = wifiAPListener; + return this; } /** - * detail: Android Wifi监听 - * Created by Ttt + * detail: Android Wifi 热点监听 + * @author Ttt */ - public interface onWifiAPListener { + public interface OnWifiAPListener { + /** + * 开启热点回调 + * @param wifiConfig 热点配置 + */ void onStarted(WifiConfiguration wifiConfig); + /** + * 关闭热点回调 + */ void onStopped(); + /** + * 失败回调 + * @param reason 失败原因 ( 错误码 ) + */ void onFailed(int reason); } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiUtils.java b/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiUtils.java new file mode 100644 index 0000000000..fe59e22b9e --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiUtils.java @@ -0,0 +1,1093 @@ +package dev.utils.app.wifi; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.KeyMgmt; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.support.annotation.RequiresPermission; +import android.text.TextUtils; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Wifi 工具类 + * @author Ttt + *
+ *     所需权限
+ *     
+ *     
+ *     
+ * 
+ */ +public final class WifiUtils { + + // 日志 TAG + private static final String TAG = WifiUtils.class.getSimpleName(); + // WifiManager 对象 + private WifiManager mWifiManager; + + // ======== + // = 常量 = + // ======== + + // 没有密码 + public static final int NOPWD = 0; + // wep 加密方式 + public static final int WEP = 1; + // wpa 加密方式 + public static final int WPA = 2; + + /** + * 构造函数 + */ + public WifiUtils() { + this(DevUtils.getContext()); + } + + /** + * 构造函数 + * @param context {@link Context} + */ + public WifiUtils(final Context context) { + // 初始化 WifiManager 对象 + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + } + + /** + * 获取 WifiManager + * @return {@link WifiManager} + */ + public WifiManager getWifiManager() { + return this.mWifiManager; + } + + // =========================== + // = Wifi 开关、连接状态获取 = + // =========================== + + /** + * 判断是否打开 wifi + * @return {@code true} yes, {@code false} no + */ + public boolean isOpenWifi() { + return mWifiManager.isWifiEnabled(); + } + + /** + * 打开 wifi + */ + @SuppressLint("MissingPermission") + public void openWifi() { + // 如果没有打开 wifi, 才进行打开 + if (!isOpenWifi()) { + try { + mWifiManager.setWifiEnabled(true); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "openWifi"); + } + } + } + + /** + * 关闭 wifi + */ + @SuppressLint("MissingPermission") + public void closeWifi() { + // 如果已经打开了 wifi, 才进行关闭 + if (isOpenWifi()) { + try { + mWifiManager.setWifiEnabled(false); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "closeWifi"); + } + } + } + + /** + * 自动切换 wifi 开关状态 + *
+     *     如果打开了, 则关闭
+     *     如果关闭了, 则打开
+     * 
+ */ + @SuppressLint("MissingPermission") + public void toggleWifiEnabled() { + try { + mWifiManager.setWifiEnabled(!isOpenWifi()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "toggleWifiEnabled"); + } + } + + /** + * 获取当前 wifi 连接状态 + * @return wifi 连接状态 + */ + @SuppressLint("MissingPermission") + public int getWifiState() { + // WifiManager.WIFI_STATE_ENABLED: // 已打开 + // WifiManager.WIFI_STATE_ENABLING: // 正在打开 + // WifiManager.WIFI_STATE_DISABLED: // 已关闭 + // WifiManager.WIFI_STATE_DISABLING: // 正在关闭 + // WifiManager.WIFI_STATE_UNKNOWN: // 未知 + try { + return mWifiManager.getWifiState(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWifiState"); + } + return WifiManager.WIFI_STATE_UNKNOWN; + } + + // ============ + // = get 操作 = + // ============ + + /** + * 开始扫描 wifi + * @return {@code true} 操作成功, {@code false} 操作失败 + */ + @SuppressLint("MissingPermission") + public boolean startScan() { + try { + return mWifiManager.startScan(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startScan"); + } + return false; + } + + /** + * 获取已配置 ( 连接过 ) 的 wifi 配置 + * @return {@link List} 已配置 ( 连接过 ) 的 wifi 配置 + */ + @SuppressLint("MissingPermission") + public List getConfiguration() { + try { + return mWifiManager.getConfiguredNetworks(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getConfiguration"); + } + return null; + } + + /** + * 获取附近的 wifi 列表 + * @return {@link List} 附近的 wifi 列表 + */ + @SuppressLint("MissingPermission") + public List getWifiList() { + try { + return mWifiManager.getScanResults(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWifiList"); + } + return null; + } + + /** + * 获取连接的 WifiInfo + * @return {@link WifiInfo} + */ + @SuppressLint("MissingPermission") + public WifiInfo getWifiInfo() { + try { + return mWifiManager.getConnectionInfo(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWifiInfo"); + } + return null; + } + + /** + * 获取 MAC 地址 + * @param wifiInfo {@link WifiInfo} + * @return MAC 地址 + */ + public static String getMacAddress(final WifiInfo wifiInfo) { + if (wifiInfo == null) return null; + return wifiInfo.getMacAddress(); + } + + /** + * 获取连接的 BSSID + * @param wifiInfo {@link WifiInfo} + * @return BSSID + */ + public static String getBSSID(final WifiInfo wifiInfo) { + if (wifiInfo == null) return null; + return wifiInfo.getBSSID(); + } + + /** + * 获取 IP 地址 + * @param wifiInfo {@link WifiInfo} + * @return IP 地址 + */ + public static int getIPAddress(final WifiInfo wifiInfo) { + if (wifiInfo == null) return -1; + return wifiInfo.getIpAddress(); + } + + /** + * 获取连接的 Network Id + * @param wifiInfo {@link WifiInfo} + * @return Network Id + */ + public static int getNetworkId(final WifiInfo wifiInfo) { + if (wifiInfo == null) return -1; + return wifiInfo.getNetworkId(); + } + + /** + * 获取 wifi SSID + * @param wifiInfo {@link WifiInfo} + * @return wifi SSID + */ + public static String getSSID(final WifiInfo wifiInfo) { + if (wifiInfo == null) return null; + try { + // 获取 SSID, 并进行处理 + return formatSSID(wifiInfo.getSSID(), false); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSSID"); + } + return null; + } + + /** + * 获取当前连接的 wifi SSID + * @return wifi SSID + */ + @SuppressLint("MissingPermission") + public static String getSSID() { + try { + // 初始化 WifiManager 对象 + WifiManager mWifiManager = (WifiManager) DevUtils.getContext().getApplicationContext().getSystemService(Context.WIFI_SERVICE); + // 获取当前连接的 wifi + WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); + // 获取 wifi SSID + return formatSSID(wifiInfo.getSSID(), false); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getSSID"); + } + return null; + } + + // = + + /** + * 判断是否存在 \"ssid\", 存在则裁剪返回 + * @param ssid 待处理的 SSID + * @return 处理后的 SSID + */ + public static String formatSSID(final String ssid) { + if (ssid == null) return null; + // 自动去掉 "" + if (ssid != null && ssid.startsWith("\"") && ssid.endsWith("\"")) { + try { + // 裁剪连接的 ssid, 并返回 + return ssid.substring(1, ssid.length() - 1); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "formatSSID"); + } + } + return ssid; + } + + /** + * 格式化处理 SSID + * @param ssid 待处理的 SSID + * @param isAppend {@code true} 添加引号, {@code false} 删除引号 + * @return 处理后的 SSID + */ + public static String formatSSID(final String ssid, final boolean isAppend) { + if (ssid == null) return null; + if (isAppend) { + return "\"" + ssid + "\""; + } else { + return formatSSID(ssid); + } + } + + /** + * 获取处理后的密码 + * @param pwd 待处理的密码 + * @param isJudge 是否需要判断 + * @return 处理后的密码 + */ + public static String getPassword(final String pwd, final boolean isJudge) { + if (pwd == null) return null; + if (isJudge && isHexWepKey(pwd)) { + return pwd; + } else { + return "\"" + pwd + "\""; + } + } + + /** + * 判断是否 wep 加密 + * @param wepKey 加密类型 + * @return {@code true} yes, {@code false} no + */ + public static boolean isHexWepKey(final String wepKey) { + if (wepKey == null) return false; + // WEP-40, WEP-104, and some vendors using 256-bit WEP (WEP-232?) + int len = wepKey.length(); + if (len != 10 && len != 26 && len != 58) { + return false; + } + return isHex(wepKey); + } + + /** + * 判断是否十六进制数据 + * @param data 待检验数据 + * @return {@code true} yes, {@code false} no + */ + private static boolean isHex(final String data) { + if (data == null) return false; + // 获取数据长度 + int len = data.length(); + if (len > 0) { + for (int i = len - 1; i >= 0; i--) { + char c = data.charAt(i); + if (!(c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f')) { + return false; + } + } + return true; + } + return false; + } + + // ============ + // = 快捷操作 = + // ============ + + /** + * 获取加密类型 + * @param typeStr 加密类型 + * @return 加密类型 {@link WifiUtils#NOPWD}、{@link WifiUtils#WPA}、{@link WifiUtils#WEP} + */ + public static int getWifiType(final String typeStr) { + if (typeStr == null) return NOPWD; + if (typeStr.contains("WPA")) { + return WPA; + } else if (typeStr.contains("WEP")) { + return WEP; + } + // 默认没有密码 + return NOPWD; + } + + /** + * 获取加密类型 + * @param typeInt 加密类型 + * @return 加密类型 {@link WifiUtils#NOPWD}、{@link WifiUtils#WPA}、{@link WifiUtils#WEP} + */ + public static int getWifiTypeInt(final String typeInt) { + if (typeInt == null) return NOPWD; + if (typeInt.equals("2")) { + return WPA; + } else if (typeInt.equals("1")) { + return WEP; + } + // 默认没有密码 + return NOPWD; + } + + /** + * 获取加密类型 + * @param type 加密类型 + * @return 加密类型 + */ + public static String getWifiType(final int type) { + switch (type) { + case WPA: + return "2"; + case WEP: + return "1"; + case NOPWD: + return "0"; + } + return "0"; + } + + /** + * 获取加密类型 + * @param type 加密类型 + * @return 加密类型 + */ + public static String getWifiTypeStr(final int type) { + switch (type) { + case WPA: + return "WPA"; + case WEP: + return "WEP"; + default: + return ""; + } + } + + /** + * 判断是否连接为 null - unknown ssid + * @param ssid wifi ssid + * @return {@code true} yes, {@code false} no + */ + public static boolean isConnNull(final String ssid) { + if (ssid == null) return true; + return ssid.indexOf("unknown") != -1; // + } + + /** + * 获取连接的 wifi 热点 SSID + * @return wifi 热点 SSID + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public static String isConnectAphot() { + try { + // 连接管理 + ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); + // 版本兼容处理 + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + // 连接状态 + NetworkInfo.State nState = cManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState(); + if ((nState == NetworkInfo.State.CONNECTED)) { + // 获取连接的 ssid + return getSSID(); + } + } else { + // 获取当前活跃的网络 ( 连接的网络信息 ) + Network network = cManager.getActiveNetwork(); + if (network != null) { + NetworkCapabilities networkCapabilities = cManager.getNetworkCapabilities(network); + // 判断是否连接 wifi + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + // 获取连接的 ssid + return getSSID(); + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isConnectAphot"); + } + return null; + } + + // ================= + // = Wifi 配置操作 = + // ================= + + // 默认没有密码 + public static final int SECURITY_NONE = 0; + // WEP 加密方式 + public static final int SECURITY_WEP = 1; + // PSK 加密方式 + public static final int SECURITY_PSK = 2; + // EAP 加密方式 + public static final int SECURITY_EAP = 3; + + /** + * 获取 wifi 加密类型 + * @param wifiConfig wifi 配置信息 + * @return wifi 加密类型 + */ + public static int getSecurity(final WifiConfiguration wifiConfig) { + if (wifiConfig == null) return SECURITY_NONE; + if (wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { + return SECURITY_PSK; + } + if (wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_EAP) + || wifiConfig.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { + return SECURITY_EAP; + } + return (wifiConfig.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE; + } + + /** + * 判断 wifi 加密类型, 是否为加密类型 + * @param wifiConfig wifi 配置信息 + * @return {@code true} yes, {@code false} no + */ + public static boolean isExistsPwd(final WifiConfiguration wifiConfig) { + if (wifiConfig == null) return false; + int wifiSecurity = getSecurity(wifiConfig); + // 判断是否加密 + return (wifiSecurity != SECURITY_NONE); + } + + /** + * 获取指定的 ssid 网络配置 ( 需连接保存过, 才存在 ) + * @param ssid wifi ssid + * @return {@link WifiConfiguration} + */ + public WifiConfiguration isExists(final String ssid) { + if (ssid == null) return null; + // 获取 wifi 连接过的配置信息 + List listWifiConfigs = getConfiguration(); + // 防止为 null + if (listWifiConfigs == null) return null; + // 遍历判断是否存在 + for (int i = 0, len = listWifiConfigs.size(); i < len; i++) { + WifiConfiguration wifiConfig = listWifiConfigs.get(i); + if (wifiConfig != null) { + if (wifiConfig.SSID.equals("\"" + ssid + "\"")) { + return wifiConfig; + } + } + } + return null; + } + + /** + * 获取指定的 network id 网络配置 ( 需连接保存过, 才存在 ) + * @param networkId network id + * @return {@link WifiConfiguration} + */ + public WifiConfiguration isExists(final int networkId) { + // 获取 wifi 连接过的配置信息 + List listWifiConfigs = getConfiguration(); + // 防止为 null + if (listWifiConfigs == null) return null; + // 遍历判断是否存在 + for (int i = 0, len = listWifiConfigs.size(); i < len; i++) { + WifiConfiguration wConfig = listWifiConfigs.get(i); + if (wConfig != null) { + if (wConfig.networkId == networkId) { + return wConfig; + } + } + } + return null; + } + + // ============ + // = 配置操作 = + // ============ + + /** + * 删除指定的 wifi(SSID) 配置信息 + * @param ssid wifi ssid + * @return {@code true} success, {@code false} fail + */ + @SuppressLint("MissingPermission") + public static boolean delWifiConfig(final String ssid) { + if (ssid == null) return false; + try { + // 初始化 WifiManager 对象 + WifiManager mWifiManager = (WifiManager) DevUtils.getContext().getApplicationContext().getSystemService(Context.WIFI_SERVICE); + // 获取 wifi 连接过的配置信息 + List listWifiConfigs = mWifiManager.getConfiguredNetworks(); + // 防止为 null + if (listWifiConfigs != null) { + // 遍历判断是否存在 + for (int i = 0, len = listWifiConfigs.size(); i < len; i++) { + WifiConfiguration wConfig = listWifiConfigs.get(i); + if (wConfig != null) { + if (wConfig.SSID.equals("\"" + ssid + "\"")) { + // 删除操作 + mWifiManager.removeNetwork(wConfig.networkId); + } + } + } + // 保存操作 + mWifiManager.saveConfiguration(); + return true; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "delWifiConfig"); + } + return false; + } + + // = + + /** + * 快速连接 wifi ( 不使用静态 IP 方式 ) + * @param ssid wifi ssid + * @param pwd wifi 密码 + * @param type wifi 加密类型 + * @return {@link WifiConfiguration} + */ + public WifiConfiguration quickConnWifi(final String ssid, final String pwd, final int type) { + return quickConnWifi(ssid, pwd, type, false, null); + } + + /** + * 快速连接 wifi + * @param ssid wifi ssid + * @param pwd wifi 密码 + * @param type wifi 加密类型 + * @param isStatic 是否使用静态 IP 连接 + * @param ip 静态 IP 地址 + * @return {@link WifiConfiguration} + */ + @SuppressLint("MissingPermission") + public WifiConfiguration quickConnWifi(final String ssid, final String pwd, final int type, final boolean isStatic, final String ip) { + // 步骤: + // 1. 创建 wifi 静态 IP 连接配置 + // 2. 创建正常 wifi 连接配置 + // 3. 查询准备连接的 wifi SSID 是否存在配置文件, 准备进行删除 + // 4. 查询当前连接的 wifi SSID 准备进行断开 + // 5. 同步进行断开, 删除操作, 并且进行保存 + // 6. 调用连接方法 + // 7. 返回连接的配置信息 + // = + try { + // 正常的 wifi 连接配置 + WifiConfiguration connWifiConfig = null; + // 如果需要通过静态 IP 方式连接, 则进行设置 + if (isStatic && !TextUtils.isEmpty(ip)) { + // 创建 wifi 静态 IP 连接配置 + WifiConfiguration staticWifiConfig = setStaticWifiConfig(createWifiConfig(ssid, pwd, type, true), ip); + // 如果静态 IP 方式, 配置失败, 则初始化正常连接的 wifi 配置 + if (staticWifiConfig == null) { + // 创建正常的配置信息 + connWifiConfig = createWifiConfig(ssid, pwd, type, true); + // = + LogPrintUtils.dTag(TAG, "属于正常方式连接 (DHCP)"); + } else { + // 设置静态信息 + connWifiConfig = staticWifiConfig; + // = + LogPrintUtils.dTag(TAG, "属于静态 IP 方式连接"); + } + } else { + // 创建正常的配置信息 + connWifiConfig = createWifiConfig(ssid, pwd, type, true); + // = + LogPrintUtils.dTag(TAG, "属于正常方式连接 (DHCP)"); + } + // 判断当前准备连接的 wifi, 是否存在配置文件 + WifiConfiguration preWifiConfig = this.isExists(ssid); + // = + if (preWifiConfig != null) { + // 存在则删除 + boolean isRemove = mWifiManager.removeNetwork(preWifiConfig.networkId); + // 打印结果 + LogPrintUtils.dTag(TAG, "删除旧的配置信息 - " + preWifiConfig.SSID + ", isRemove: " + isRemove); + // 保存配置 + mWifiManager.saveConfiguration(); + } + // = + // 连接网络 + int nId = mWifiManager.addNetwork(connWifiConfig); + if (nId != -1) { + try { + // 获取当前连接的 wifi 对象 + WifiInfo wifiInfo = getWifiInfo(); + // 获取连接的 id + int networdId = wifiInfo.getNetworkId(); + // 禁用网络 + boolean isDisable = mWifiManager.disableNetwork(networdId); + // 断开之前的连接 + boolean isDisConnect = mWifiManager.disconnect(); + // 打印断开连接结果 + LogPrintUtils.dTag(TAG, "isDisConnect: " + isDisConnect + ", isDisable: " + isDisable); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "quickConnWifi - 关闭连接出错: " + nId); + } + // 开始连接 + boolean isResult = mWifiManager.enableNetwork(nId, true); + // = + if (!isResult) { + isResult = mWifiManager.enableNetwork(nId, true); + } + // 打印结果 + LogPrintUtils.dTag(TAG, "addNetwork(enableNetwork) - result: " + isResult); + } else { + // 尝试不带引号 SSID 连接 + connWifiConfig.SSID = formatSSID(connWifiConfig.SSID, false); + // 连接网络 + nId = mWifiManager.addNetwork(connWifiConfig); + if (nId != -1) { + try { + // 获取当前连接的 wifi 对象 + WifiInfo wifiInfo = getWifiInfo(); + // 获取连接的 id + int networdId = wifiInfo.getNetworkId(); + // 禁用网络 + boolean isDisable = mWifiManager.disableNetwork(networdId); + // 断开之前的连接 + boolean isDisConnect = mWifiManager.disconnect(); + // 打印断开连接结果 + LogPrintUtils.dTag(TAG, "isDisConnect: " + isDisConnect + ", isDisable: " + isDisable); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "quickConnWifi - 关闭连接出错: " + nId); + } + // 开始连接 + boolean isResult = mWifiManager.enableNetwork(nId, true); + // = + if (!isResult) { + isResult = mWifiManager.enableNetwork(nId, true); + } + // 打印结果 + LogPrintUtils.dTag(TAG, "addNetwork(enableNetwork) - result: " + isResult); + } + } + // 保存 id + connWifiConfig.networkId = nId; + // 连接的 networkId + LogPrintUtils.dTag(TAG, "连接的 SSID - networkId: " + nId); + // 返回连接的信息 + return connWifiConfig; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, "quickConnWifi", e); + } + return null; + } + + /** + * 创建 wifi 配置信息 + * @param ssid wifi ssid + * @param pwd wifi 密码 + * @param type wifi 加密类型 + * @param isHandler 是否处理双引号 + * @return {@link WifiConfiguration} + */ + public static WifiConfiguration createWifiConfig(final String ssid, final String pwd, final int type, final boolean isHandler) { + try { + // 创建一个新的网络配置 + WifiConfiguration wifiConfig = new WifiConfiguration(); + wifiConfig.allowedAuthAlgorithms.clear(); + wifiConfig.allowedGroupCiphers.clear(); + wifiConfig.allowedKeyManagement.clear(); + wifiConfig.allowedPairwiseCiphers.clear(); + wifiConfig.allowedProtocols.clear(); + wifiConfig.priority = 0; + // 设置连接的 SSID + if (isHandler) { + wifiConfig.SSID = formatSSID(ssid, true); + } else { + wifiConfig.SSID = ssid; + } + switch (type) { + case WifiUtils.NOPWD: // 不存在密码 + wifiConfig.hiddenSSID = true; + wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE); + break; + case WifiUtils.WEP: // WEP 加密方式 + wifiConfig.hiddenSSID = true; + if (isHandler) { + if (isHexWepKey(pwd)) { + wifiConfig.wepKeys[0] = pwd; + } else { + wifiConfig.wepKeys[0] = "\"" + pwd + "\""; + } + } else { + wifiConfig.wepKeys[0] = pwd; + } + wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE); + wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); + // wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); + wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); + wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40); + wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104); + wifiConfig.wepTxKeyIndex = 0; + break; + case WifiUtils.WPA: // WPA 加密方式 + if (isHandler) { + wifiConfig.preSharedKey = "\"" + pwd + "\""; + } else { + wifiConfig.preSharedKey = pwd; + } + wifiConfig.hiddenSSID = true; + wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); + wifiConfig.allowedKeyManagement.set(KeyMgmt.WPA_PSK); +// wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA); + wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); + wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); + wifiConfig.status = WifiConfiguration.Status.ENABLED; + break; + } + return wifiConfig; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createWifiConfig"); + } + return null; + } + + // ============ + // = 连接操作 = + // ============ + + /** + * 移除 wifi 配置信息 + * @param wifiConfig wifi 配置信息 + * @return {@code true} success, {@code false} fail + */ + @SuppressLint("MissingPermission") + public boolean removeWifiConfig(final WifiConfiguration wifiConfig) { + // 如果等于 null 则直接返回 + if (wifiConfig == null) return false; + try { + // 删除配置 + boolean isResult = mWifiManager.removeNetwork(wifiConfig.networkId); + // 保存操作 + mWifiManager.saveConfiguration(); + // 返回删除结果 + return isResult; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, "removeWifiConfig", e); + } + return false; + } + + /** + * 断开指定 networkId 的网络 + * @param networkId network id + */ + @SuppressLint("MissingPermission") + public void disconnectWifi(final int networkId) { + try { + mWifiManager.disableNetwork(networkId); + mWifiManager.disconnect(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, "disconnectWifi", e); + } + } + + // =========================== + // = 设置静态 IP、域名等信息 = + // =========================== + + /** + * 设置静态 wifi 配置信息 + * @param wifiConfig wifi 配置信息 + * @param ip 静态 IP + * @return {@link WifiConfiguration} + */ + private WifiConfiguration setStaticWifiConfig(final WifiConfiguration wifiConfig, final String ip) { + String gateway = null; + String dns; + if (ip != null) { + try { + InetAddress intetAddress = InetAddress.getByName(ip); + int intIp = inetAddressToInt(intetAddress); + dns = (intIp & 0xFF) + "." + ((intIp >> 8) & 0xFF) + "." + ((intIp >> 16) & 0xFF) + ".1"; + gateway = dns; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setStaticWifiConfig"); + return null; + } + } + // 暂时不需要设置 DNS, 所以 DNS 参数传入 null + return setStaticWifiConfig(wifiConfig, ip, gateway, null, 24); + } + + /** + * 设置静态 wifi 配置信息 + * @param wifiConfig wifi 配置信息 + * @param ip 静态 IP + * @param gateway 网关 + * @param dns DNS + * @param networkPrefixLength 网络前缀长度 + * @return {@link WifiConfiguration} + */ + private WifiConfiguration setStaticWifiConfig(final WifiConfiguration wifiConfig, final String ip, final String gateway, final String dns, final int networkPrefixLength) { + try { + if (ip == null || gateway == null) { + return null; + } + // 设置 InetAddress + InetAddress intetAddress = InetAddress.getByName(ip); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH) { // 旧的版本, 5.0 之前 + // 设置 IP 分配方式, 静态 IP + setEnumField(wifiConfig, "STATIC", "ipAssignment"); + // 设置不用代理 + setEnumField(wifiConfig, "NONE", "proxySettings"); + // 设置 IP 地址 + setIpAddress(intetAddress, networkPrefixLength, wifiConfig); + // 设置网关 + setGateway(InetAddress.getByName(gateway), wifiConfig); + if (dns != null) { // 判断是否需要设置域名 + // 设置 DNS + setDNS(InetAddress.getByName(dns), wifiConfig); + } + } else { // 5.0 新版本改变到其他地方 + Object object = getDeclaredField(wifiConfig, "mIpConfiguration"); + // 设置 IP 分配方式, 静态 IP + setEnumField(object, "STATIC", "ipAssignment"); + // 设置不用代理 + setEnumField(object, "NONE", "proxySettings"); + // 设置 IP 地址、网关、DNS + setStaticIpConfig(ip, gateway, dns, networkPrefixLength, object); + } + return wifiConfig; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setStaticWifiConfig"); + } + return null; + } + + /** + * 转换 IP 地址 + * @param inetAddr {@link InetAddress} + * @return IPv4 地址 + * @throws Exception 不属于 IPv4 地址 + */ + private int inetAddressToInt(final InetAddress inetAddr) throws Exception { + byte[] data = inetAddr.getAddress(); + if (data.length != 4) { + throw new IllegalArgumentException("Not an IPv4 address"); + } + return ((data[3] & 0xff) << 24) | ((data[2] & 0xff) << 16) | ((data[1] & 0xff) << 8) | (data[0] & 0xff); + } + + /** + * 设置 DNS + * @param dns DNS + * @param wifiConfig wifi 配置信息 + * @throws Exception 设置失败, 抛出异常 + */ + private void setDNS(final InetAddress dns, final WifiConfiguration wifiConfig) throws Exception { + Object linkProperties = getField(wifiConfig, "linkProperties"); + if (linkProperties == null) + throw new NullPointerException(); + + List mDnses = (ArrayList) getDeclaredField(linkProperties, "mDnses"); + mDnses.clear(); // or add a new dns address, here I just want to replace DNS1 + mDnses.add(dns); + } + + /** + * 设置网关 + * @param gateway 网关 + * @param wifiConfig wifi 配置信息 + * @throws Exception 设置失败, 抛出异常 + */ + private void setGateway(final InetAddress gateway, final WifiConfiguration wifiConfig) throws Exception { + Object linkProperties = getField(wifiConfig, "linkProperties"); + if (linkProperties == null) + throw new NullPointerException(); + + Class routeInfoClass = Class.forName("android.net.RouteInfo"); + Constructor routeInfoConstructor = routeInfoClass.getConstructor(InetAddress.class); + Object routeInfo = routeInfoConstructor.newInstance(gateway); + ArrayList mRoutes = (ArrayList) getDeclaredField(linkProperties, "mRoutes"); + mRoutes.clear(); + mRoutes.add(routeInfo); + } + + /** + * 设置 IP 地址 + * @param address IP 地址 + * @param prefixLength 网络前缀长度 + * @param wifiConfig wifi 配置信息 + * @throws Exception 设置失败, 抛出异常 + */ + private void setIpAddress(final InetAddress address, final int prefixLength, final WifiConfiguration wifiConfig) throws Exception { + Object linkProperties = getField(wifiConfig, "linkProperties"); + if (linkProperties == null) + throw new NullPointerException(); + + Class laClass = Class.forName("android.net.LinkAddress"); + Constructor laConstructor = laClass.getConstructor(InetAddress.class, int.class); + Object linkAddress = laConstructor.newInstance(address, prefixLength); + ArrayList mLinkAddresses = (ArrayList) getDeclaredField(linkProperties, "mLinkAddresses"); + mLinkAddresses.clear(); + mLinkAddresses.add(linkAddress); + } + + /** + * 设置 IP 地址、网关、DNS (5.0 之后 ) + * @param ip 静态 IP + * @param gateway 网关 + * @param dns DNS + * @param prefixLength 网络前缀长度 + * @param object wifi 配置信息 + * @throws Exception 设置失败, 抛出异常 + */ + private void setStaticIpConfig(final String ip, final String gateway, final String dns, final int prefixLength, final Object object) throws Exception { + // 从 WifiConfig 成员变量 mIpConfiguration 获取 staticIpConfiguration + // 获取 staticIpConfiguration 变量 + Object staticIpConfigClass = getField(object, "staticIpConfiguration"); + if (staticIpConfigClass == null) { + // 创建静态 IP 配置类 + staticIpConfigClass = Class.forName("android.net.StaticIpConfiguration").newInstance(); + } + // 初始化 LinkAddress 并设置 IP 地址 + Class laClass = Class.forName("android.net.LinkAddress"); + Constructor laConstructor = laClass.getConstructor(InetAddress.class, int.class); + Object linkAddress = laConstructor.newInstance(InetAddress.getByName(ip), prefixLength); + // 设置地址 IP 地址 ipAddress + setValueField(staticIpConfigClass, linkAddress, "ipAddress"); + // 设置网关 gateway + setValueField(staticIpConfigClass, InetAddress.getByName(gateway), "gateway"); + if (dns != null) { // 判断是否需要设置域名 + // 设置 DNS + List mDnses = (ArrayList) getDeclaredField(staticIpConfigClass, "dnsServers"); + mDnses.clear(); // or add a new dns address, here I just want to replace DNS1 + mDnses.add(InetAddress.getByName(dns)); + } + // 设置赋值 staticIpConfiguration 属性 + setValueField(object, staticIpConfigClass, "staticIpConfiguration"); + } + + /** + * 通过反射获取 + * @param object Object + * @param name 字段名 + * @return 对应的字段 + * @throws Exception 获取失败, 抛出异常 + */ + private Object getField(final Object object, final String name) throws Exception { + Field field = object.getClass().getField(name); + return field.get(object); + } + + /** + * 通过反射获取 + * @param object Object + * @param name 字段名 + * @return 对应的字段 + * @throws Exception 获取失败, 抛出异常 + */ + private Object getDeclaredField(final Object object, final String name) throws Exception { + Field field = object.getClass().getDeclaredField(name); + field.setAccessible(true); + return field.get(object); + } + + /** + * 通过反射枚举类, 进行设置 + * @param object Object + * @param value 设置参数值 + * @param name 字段名 + * @throws Exception 设置失败, 抛出异常 + */ + private void setEnumField(final Object object, final String value, final String name) throws Exception { + Field field = object.getClass().getField(name); + field.set(object, Enum.valueOf((Class) field.getType(), value)); + } + + /** + * 通过反射, 进行设置 + * @param object Object + * @param val 设置参数值 + * @param name 字段名 + * @throws Exception 设置失败, 抛出异常 + */ + private void setValueField(final Object object, final Object val, final String name) throws Exception { + Field field = object.getClass().getField(name); + field.set(object, val); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiVo.java b/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiVo.java new file mode 100644 index 0000000000..473d612893 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiVo.java @@ -0,0 +1,132 @@ +package dev.utils.app.wifi; + +import android.net.wifi.ScanResult; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.Keep; + +import java.util.List; + +import dev.utils.LogPrintUtils; + +/** + * detail: Wifi 信息实体类 + * @author Ttt + */ +public class WifiVo implements Parcelable { + + // 日志 TAG + private static final String TAG = WifiVo.class.getSimpleName(); + + @Keep // wifi ssid + public String wSSID = null; + @Keep // wifi 密码 + public String wPwd = null; + @Keep // wifi 加密类型 + public int wType = WifiUtils.NOPWD; + @Keep // wifi 信号等级 + public int wLevel = 0; + + public WifiVo() { + } + + /** + * 获取 wifi 信息 + * @param scanResult 扫描的 wifi 信息 + * @return {@link WifiVo} + */ + public static WifiVo createWifiVo(final ScanResult scanResult) { + return createWifiVo(scanResult, false); + } + + /** + * 获取 wifi 信息 + * @param scanResult 扫描的 wifi 信息 + * @param isAppend {@code true} 添加引号, {@code false} 删除引号 + * @return {@link WifiVo} + */ + public static WifiVo createWifiVo(final ScanResult scanResult, final boolean isAppend) { + if (scanResult != null) { + try { + // 防止 wifi 名长度为 0 + if (scanResult.SSID.length() == 0) { + return null; + } + WifiVo wifiVo = new WifiVo(); + // wifi ssid + wifiVo.wSSID = WifiUtils.formatSSID(scanResult.SSID, isAppend); + // wifi 加密类型 + wifiVo.wType = WifiUtils.getWifiType(scanResult.capabilities); + // wifi 信号等级 + wifiVo.wLevel = scanResult.level; + return wifiVo; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createWifiVo"); + } + } + return null; + } + + /** + * 扫描 wifi 信息 + * @param listWifiVos 数据源 + * @param listScanResults 扫描返回的数据 + */ + public static void scanWifiVos(final List listWifiVos, final List listScanResults) { + if (listWifiVos == null || listScanResults == null) return; + // 清空旧数据 + listWifiVos.clear(); + // 遍历 wifi 列表数据 + for (int i = 0, len = listScanResults.size(); i < len; i++) { + // 如果出现异常、或者失败, 则无视当前的索引 wifi 信息 + try { + // 获取当前索引的 wifi 信息 + ScanResult scanResult = listScanResults.get(i); + // 防止 wifi 名长度为 0 + if (scanResult.SSID.length() == 0) { + continue; + } + // 保存 wifi 信息 + listWifiVos.add(createWifiVo(scanResult)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "scanWifiVos"); + } + } + } + + // ============== + // = Parcelable = + // ============== + + protected WifiVo(Parcel in) { + wSSID = in.readString(); + wPwd = in.readString(); + wType = in.readInt(); + wLevel = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public WifiVo createFromParcel(Parcel in) { + return new WifiVo(in); + } + + @Override + public WifiVo[] newArray(int size) { + return new WifiVo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel dest, final int flags) { + dest.writeString(wSSID); + dest.writeString(wPwd); + dest.writeInt(wType); + dest.writeInt(wLevel); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/ArrayUtils.java b/lib/DevApp/src/main/java/dev/utils/common/ArrayUtils.java new file mode 100644 index 0000000000..bab2ee8e9f --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/ArrayUtils.java @@ -0,0 +1,3088 @@ +package dev.utils.common; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: Array 数组工具类 + * @author Ttt + */ +public final class ArrayUtils { + + private ArrayUtils() { + } + + // 日志 TAG + private static final String TAG = ArrayUtils.class.getSimpleName(); + + // ========= + // = Array = + // ========= + + /** + * 判断数组是否为 null + * @param objects object[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Object[] objects) { + return objects == null || objects.length == 0; + } + + /** + * 判断数组是否为 null + * @param ints int[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final int[] ints) { + return ints == null || ints.length == 0; + } + + /** + * 判断数组是否为 null + * @param bytes byte[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final byte[] bytes) { + return bytes == null || bytes.length == 0; + } + + /** + * 判断数组是否为 null + * @param chars char[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final char[] chars) { + return chars == null || chars.length == 0; + } + + /** + * 判断数组是否为 null + * @param shorts short[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final short[] shorts) { + return shorts == null || shorts.length == 0; + } + + /** + * 判断数组是否为 null + * @param longs long[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final long[] longs) { + return longs == null || longs.length == 0; + } + + /** + * 判断数组是否为 null + * @param floats float[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final float[] floats) { + return floats == null || floats.length == 0; + } + + /** + * 判断数组是否为 null + * @param doubles double[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final double[] doubles) { + return doubles == null || doubles.length == 0; + } + + /** + * 判断数组是否为 null + * @param booleans boolean[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final boolean[] booleans) { + return booleans == null || booleans.length == 0; + } + + // = + + /** + * 判断数组是否不为 null + * @param objects object[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Object[] objects) { + return objects != null && objects.length != 0; + } + + /** + * 判断数组是否不为 null + * @param ints int[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final int[] ints) { + return ints != null && ints.length != 0; + } + + /** + * 判断数组是否不为 null + * @param bytes byte[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final byte[] bytes) { + return bytes != null && bytes.length != 0; + } + + /** + * 判断数组是否不为 null + * @param chars char[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final char[] chars) { + return chars != null && chars.length != 0; + } + + /** + * 判断数组是否不为 null + * @param shorts short[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final short[] shorts) { + return shorts != null && shorts.length != 0; + } + + /** + * 判断数组是否不为 null + * @param longs long[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final long[] longs) { + return longs != null && longs.length != 0; + } + + /** + * 判断数组是否不为 null + * @param floats float[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final float[] floats) { + return floats != null && floats.length != 0; + } + + /** + * 判断数组是否不为 null + * @param doubles double[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final double[] doubles) { + return doubles != null && doubles.length != 0; + } + + /** + * 判断数组是否不为 null + * @param booleans boolean[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final boolean[] booleans) { + return booleans != null && booleans.length != 0; + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取数组长度 + * @param objects object[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final Object[] objects) { + return length(objects, 0); + } + + /** + * 获取数组长度 + * @param ints int[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final int[] ints) { + return length(ints, 0); + } + + /** + * 获取数组长度 + * @param bytes byte[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final byte[] bytes) { + return length(bytes, 0); + } + + /** + * 获取数组长度 + * @param chars char[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final char[] chars) { + return length(chars, 0); + } + + /** + * 获取数组长度 + * @param shorts short[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final short[] shorts) { + return length(shorts, 0); + } + + /** + * 获取数组长度 + * @param longs long[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final long[] longs) { + return length(longs, 0); + } + + /** + * 获取数组长度 + * @param floats float[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final float[] floats) { + return length(floats, 0); + } + + /** + * 获取数组长度 + * @param doubles double[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final double[] doubles) { + return length(doubles, 0); + } + + /** + * 获取数组长度 + * @param booleans boolean[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final boolean[] booleans) { + return length(booleans, 0); + } + + // = + + /** + * 获取数组长度 + * @param objects object[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final Object[] objects, final int defaultLength) { + return objects != null ? objects.length : defaultLength; + } + + /** + * 获取数组长度 + * @param ints int[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final int[] ints, final int defaultLength) { + return ints != null ? ints.length : defaultLength; + } + + /** + * 获取数组长度 + * @param bytes byte[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final byte[] bytes, final int defaultLength) { + return bytes != null ? bytes.length : defaultLength; + } + + /** + * 获取数组长度 + * @param chars char[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final char[] chars, final int defaultLength) { + return chars != null ? chars.length : defaultLength; + } + + /** + * 获取数组长度 + * @param shorts short[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final short[] shorts, final int defaultLength) { + return shorts != null ? shorts.length : defaultLength; + } + + /** + * 获取数组长度 + * @param longs long[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final long[] longs, final int defaultLength) { + return longs != null ? longs.length : defaultLength; + } + + /** + * 获取数组长度 + * @param floats float[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final float[] floats, final int defaultLength) { + return floats != null ? floats.length : defaultLength; + } + + /** + * 获取数组长度 + * @param doubles double[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final double[] doubles, final int defaultLength) { + return doubles != null ? doubles.length : defaultLength; + } + + /** + * 获取数组长度 + * @param booleans boolean[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final boolean[] booleans, final int defaultLength) { + return booleans != null ? booleans.length : defaultLength; + } + + // = + + /** + * 判断数组长度是否等于期望长度 + * @param objects object[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Object[] objects, final int length) { + return objects != null && objects.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param ints int[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final int[] ints, final int length) { + return ints != null && ints.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param bytes byte[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final byte[] bytes, final int length) { + return bytes != null && bytes.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param chars char[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final char[] chars, final int length) { + return chars != null && chars.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param shorts short[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final short[] shorts, final int length) { + return shorts != null && shorts.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param longs long[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final long[] longs, final int length) { + return longs != null && longs.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param floats float[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final float[] floats, final int length) { + return floats != null && floats.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param doubles double[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final double[] doubles, final int length) { + return doubles != null && doubles.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param booleans boolean[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final boolean[] booleans, final int length) { + return booleans != null && booleans.length == length; + } + + // ============ + // = 数据获取 = + // ============ + + /** + * 获取数组对应索引数据 + * @param array 数组 + * @param pos 索引 + * @param 泛型 + * @return 数组指定索引的值 + */ + public static T get(final Object array, final int pos) { + if (array == null || pos < 0) return null; + try { + return (T) Array.get(array, pos); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getByArray"); + } + return null; + } + + /** + * 获取数组对应索引数据 + * @param array 数组 + * @param pos 索引 + * @param 泛型 + * @return 数组指定索引的值 + */ + public static T get(final T[] array, final int pos) { + return get(array, pos, null); + } + + /** + * 获取数组对应索引数据 + * @param ints int[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static int get(final int[] ints, final int pos) { + return get(ints, pos, -1); + } + + /** + * 获取数组对应索引数据 + * @param bytes byte[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static byte get(final byte[] bytes, final int pos) { + return get(bytes, pos, (byte) -1); + } + + /** + * 获取数组对应索引数据 + * @param chars char[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static char get(final char[] chars, final int pos) { + return get(chars, pos, (char) -1); + } + + /** + * 获取数组对应索引数据 + * @param shorts short[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static short get(final short[] shorts, final int pos) { + return get(shorts, pos, (short) -1); + } + + /** + * 获取数组对应索引数据 + * @param longs long[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static long get(final long[] longs, final int pos) { + return get(longs, pos, -1L); + } + + /** + * 获取数组对应索引数据 + * @param floats float[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static float get(final float[] floats, final int pos) { + return get(floats, pos, -1f); + } + + /** + * 获取数组对应索引数据 + * @param doubles double[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static double get(final double[] doubles, final int pos) { + return get(doubles, pos, -1d); + } + + /** + * 获取数组对应索引数据 + * @param booleans boolean[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static boolean get(final boolean[] booleans, final int pos) { + return get(booleans, pos, false); + } + + // = + + /** + * 获取数组对应索引数据 + * @param array 数组 + * @param pos 索引 + * @param defaultValue 默认值 + * @param 泛型 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static T get(final T[] array, final int pos, final T defaultValue) { + if (array != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return array[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param ints int[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static int get(final int[] ints, final int pos, final int defaultValue) { + if (ints != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return ints[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param bytes byte[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static byte get(final byte[] bytes, final int pos, final byte defaultValue) { + if (bytes != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return bytes[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param chars char[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static char get(final char[] chars, final int pos, final char defaultValue) { + if (chars != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return chars[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param shorts short[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static short get(final short[] shorts, final int pos, final short defaultValue) { + if (shorts != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return shorts[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param longs long[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static long get(final long[] longs, final int pos, final long defaultValue) { + if (longs != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return longs[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param floats float[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static float get(final float[] floats, final int pos, final float defaultValue) { + if (floats != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return floats[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param doubles double[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static double get(final double[] doubles, final int pos, final double defaultValue) { + if (doubles != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return doubles[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param booleans boolean[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static boolean get(final boolean[] booleans, final int pos, final boolean defaultValue) { + if (booleans != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return booleans[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + // = + + /** + * 获取数组第一条数据 + * @param array 数组 + * @param 泛型 + * @return 数组索引为 0 的值 + */ + public static T getFirst(final T[] array) { + return get(array, 0); + } + + /** + * 获取数组第一条数据 + * @param ints int[] + * @return 数组索引为 0 的值 + */ + public static int getFirst(final int[] ints) { + return get(ints, 0); + } + + /** + * 获取数组第一条数据 + * @param bytes byte[] + * @return 数组索引为 0 的值 + */ + public static byte getFirst(final byte[] bytes) { + return get(bytes, 0); + } + + /** + * 获取数组第一条数据 + * @param chars char[] + * @return 数组索引为 0 的值 + */ + public static char getFirst(final char[] chars) { + return get(chars, 0); + } + + /** + * 获取数组第一条数据 + * @param shorts short[] + * @return 数组索引为 0 的值 + */ + public static short getFirst(final short[] shorts) { + return get(shorts, 0); + } + + /** + * 获取数组第一条数据 + * @param longs long[] + * @return 数组索引为 0 的值 + */ + public static long getFirst(final long[] longs) { + return get(longs, 0); + } + + /** + * 获取数组第一条数据 + * @param floats float[] + * @return 数组索引为 0 的值 + */ + public static float getFirst(final float[] floats) { + return get(floats, 0); + } + + /** + * 获取数组第一条数据 + * @param doubles double[] + * @return 数组索引为 0 的值 + */ + public static double getFirst(final double[] doubles) { + return get(doubles, 0); + } + + /** + * 获取数组第一条数据 + * @param booleans boolean[] + * @return 数组索引为 0 的值 + */ + public static boolean getFirst(final boolean[] booleans) { + return get(booleans, 0); + } + + // = + + /** + * 获取数组最后一条数据 + * @param array 数组 + * @param 泛型 + * @return 数组索引 length - 1 的值 + */ + public static T getLast(final T[] array) { + return get(array, length(array) - 1); + } + + /** + * 获取数组最后一条数据 + * @param ints int[] + * @return 数组索引 length - 1 的值 + */ + public static int getLast(final int[] ints) { + return get(ints, length(ints) - 1); + } + + /** + * 获取数组最后一条数据 + * @param bytes byte[] + * @return 数组索引 length - 1 的值 + */ + public static byte getLast(final byte[] bytes) { + return get(bytes, length(bytes) - 1); + } + + /** + * 获取数组最后一条数据 + * @param chars char[] + * @return 数组索引 length - 1 的值 + */ + public static char getLast(final char[] chars) { + return get(chars, length(chars) - 1); + } + + /** + * 获取数组最后一条数据 + * @param shorts short[] + * @return 数组索引 length - 1 的值 + */ + public static short getLast(final short[] shorts) { + return get(shorts, length(shorts) - 1); + } + + /** + * 获取数组最后一条数据 + * @param longs long[] + * @return 数组索引 length - 1 的值 + */ + public static long getLast(final long[] longs) { + return get(longs, length(longs) - 1); + } + + /** + * 获取数组最后一条数据 + * @param floats float[] + * @return 数组索引 length - 1 的值 + */ + public static float getLast(final float[] floats) { + return get(floats, length(floats) - 1); + } + + /** + * 获取数组最后一条数据 + * @param doubles double[] + * @return 数组索引 length - 1 的值 + */ + public static double getLast(final double[] doubles) { + return get(doubles, length(doubles) - 1); + } + + /** + * 获取数组最后一条数据 + * @param booleans boolean[] + * @return 数组索引 length - 1 的值 + */ + public static boolean getLast(final boolean[] booleans) { + return get(booleans, length(booleans) - 1); + } + + // =================== + // = 数据获取 - 特殊 = + // =================== + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param offset 偏移量 + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final T[] array, final T value, final int number, final boolean notNull, final int offset) { + if (array != null) { + if (notNull && value == null) { + return null; + } + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + T t = array[i]; + // 判断是否一样 + if (equals(t, value)) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return null; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param offset 偏移量 + * @param 泛型 + * @return 对应索引的值 + */ + public static int getPosition(final T[] array, final T value, final int number, final boolean notNull, final int offset) { + if (array != null) { + if (notNull && value == null) { + return -1; + } + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + T t = array[i]; + // 判断是否一样 + if (equals(t, value)) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值 + * @param array 数组 + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final T[] array, final T value) { + return get(array, value, 0, false, 0); + } + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final T[] array, final T value, final int number) { + return get(array, value, number, false, 0); + } + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值 + * @param array 数组 + * @param value 值 + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final T[] array, final T value, final boolean notNull) { + return get(array, value, 0, notNull, 0); + } + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final T[] array, final T value, final int number, final boolean notNull) { + return get(array, value, number, notNull, 0); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值, 不允许值为 null + * @param array 数组 + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNotNull(final T[] array, final T value) { + return get(array, value, 0, true, 0); + } + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值, 不允许值为 null + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNotNull(final T[] array, final T value, final int number) { + return get(array, value, number, true, 0); + } + + // = + + /** + * 根据指定 value 获取索引 + * @param array 数组 + * @param value 值 + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final T[] array, final T value) { + return getPosition(array, value, 0, false, 0); + } + + /** + * 根据指定 value 获取索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final T[] array, final T value, final int number) { + return getPosition(array, value, number, false, 0); + } + + /** + * 根据指定 value 获取索引 + * @param array 数组 + * @param value 值 + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final T[] array, final T value, final boolean notNull) { + return getPosition(array, value, 0, notNull, 0); + } + + /** + * 根据指定 value 获取索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final T[] array, final T value, final int number, final boolean notNull) { + return getPosition(array, value, number, notNull, 0); + } + + // = + + /** + * 根据指定 value 获取索引, 不允许值为 null + * @param array 数组 + * @param value 值 + * @param 泛型 + * @return 对应的索引 + */ + public static int getPositionNotNull(final T[] array, final T value) { + return getPosition(array, value, 0, true, 0); + } + + /** + * 根据指定 value 获取索引, 不允许值为 null + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应的索引 + */ + public static int getPositionNotNull(final T[] array, final T value, final int number) { + return getPosition(array, value, number, true, 0); + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static int get(final int[] array, final int value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + int valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final int[] array, final int value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + int valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static byte get(final byte[] array, final byte value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + byte valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (byte) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final byte[] array, final byte value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + byte valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static char get(final char[] array, final char value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + char valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (char) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final char[] array, final char value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + char valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static short get(final short[] array, final short value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + short valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (short) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final short[] array, final short value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + short valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static long get(final long[] array, final long value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + long valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (long) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final long[] array, final long value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + long valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static float get(final float[] array, final float value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + float valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (float) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final float[] array, final float value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + float valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static double get(final double[] array, final double value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + double valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (double) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final double[] array, final double value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + double valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static boolean get(final boolean[] array, final boolean value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + boolean valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return false; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final boolean[] array, final boolean value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + boolean valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // ============ + // = 转换处理 = + // ============ + + /** + * int[] 转换 Integer[] + * @param ints int[] + * @return {@link Integer[]} + */ + public static Integer[] intsToIntegers(final int[] ints) { + if (ints != null) { + // 获取数组长度 + int len = ints.length; + // 创建数组 + Integer[] array = new Integer[len]; + for (int i = 0; i < len; i++) { + array[i] = ints[i]; + } + return array; + } + return null; + } + + /** + * byte[] 转换 Byte[] + * @param bytes byte[] + * @return {@link Byte[]} + */ + public static Byte[] bytesToBytes(final byte[] bytes) { + if (bytes != null) { + // 获取数组长度 + int len = bytes.length; + // 创建数组 + Byte[] array = new Byte[len]; + for (int i = 0; i < len; i++) { + array[i] = bytes[i]; + } + return array; + } + return null; + } + + /** + * char[] 转换 Character[] + * @param chars char[] + * @return {@link Character[]} + */ + public static Character[] charsToCharacters(final char[] chars) { + if (chars != null) { + // 获取数组长度 + int len = chars.length; + // 创建数组 + Character[] array = new Character[len]; + for (int i = 0; i < len; i++) { + array[i] = chars[i]; + } + return array; + } + return null; + } + + /** + * short[] 转换 Short[] + * @param shorts short[] + * @return {@link Short[]} + */ + public static Short[] shortsToShorts(final short[] shorts) { + if (shorts != null) { + // 获取数组长度 + int len = shorts.length; + // 创建数组 + Short[] array = new Short[len]; + for (int i = 0; i < len; i++) { + array[i] = shorts[i]; + } + return array; + } + return null; + } + + /** + * long[] 转换 Long[] + * @param longs long[] + * @return {@link Long[]} + */ + public static Long[] longsToLongs(final long[] longs) { + if (longs != null) { + // 获取数组长度 + int len = longs.length; + // 创建数组 + Long[] array = new Long[len]; + for (int i = 0; i < len; i++) { + array[i] = longs[i]; + } + return array; + } + return null; + } + + /** + * float[] 转换 Float[] + * @param floats float[] + * @return {@link Float[]} + */ + public static Float[] floatsToFloats(final float[] floats) { + if (floats != null) { + // 获取数组长度 + int len = floats.length; + // 创建数组 + Float[] array = new Float[len]; + for (int i = 0; i < len; i++) { + array[i] = floats[i]; + } + return array; + } + return null; + } + + /** + * double[] 转换 Double[] + * @param doubles double[] + * @return {@link Double[]} + */ + public static Double[] doublesToDoubles(final double[] doubles) { + if (doubles != null) { + // 获取数组长度 + int len = doubles.length; + // 创建数组 + Double[] array = new Double[len]; + for (int i = 0; i < len; i++) { + array[i] = doubles[i]; + } + return array; + } + return null; + } + + /** + * boolean[] 转换 Boolean[] + * @param booleans boolean[] + * @return {@link Boolean[]} + */ + public static Boolean[] booleansToBooleans(final boolean[] booleans) { + if (booleans != null) { + // 获取数组长度 + int len = booleans.length; + // 创建数组 + Boolean[] array = new Boolean[len]; + for (int i = 0; i < len; i++) { + array[i] = booleans[i]; + } + return array; + } + return null; + } + + // = + + /** + * Integer[] 转换 int[] + * @param integers Integer[] + * @param defaultValue 转换失败使用得默认值 + * @return int[] + */ + public static int[] integersToInts(final Integer[] integers, final int defaultValue) { + if (integers != null) { + // 获取数组长度 + int len = integers.length; + // 创建数组 + int[] array = new int[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = integers[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Byte[] 转换 byte[] + * @param bytes Byte[] + * @param defaultValue 转换失败使用得默认值 + * @return byte[] + */ + public static byte[] bytesToBytes(final Byte[] bytes, final byte defaultValue) { + if (bytes != null) { + // 获取数组长度 + int len = bytes.length; + // 创建数组 + byte[] array = new byte[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = bytes[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Character[] 转换 char[] + * @param characters Character[] + * @param defaultValue 转换失败使用得默认值 + * @return char[] + */ + public static char[] charactersToChars(final Character[] characters, final char defaultValue) { + if (characters != null) { + // 获取数组长度 + int len = characters.length; + // 创建数组 + char[] array = new char[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = characters[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Short[] 转换 short[] + * @param shorts Short[] + * @param defaultValue 转换失败使用得默认值 + * @return short[] + */ + public static short[] shortsToShorts(final Short[] shorts, final short defaultValue) { + if (shorts != null) { + // 获取数组长度 + int len = shorts.length; + // 创建数组 + short[] array = new short[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = shorts[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Long[] 转换 long[] + * @param longs Long[] + * @param defaultValue 转换失败使用得默认值 + * @return long[] + */ + public static long[] longsToLongs(final Long[] longs, final long defaultValue) { + if (longs != null) { + // 获取数组长度 + int len = longs.length; + // 创建数组 + long[] array = new long[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = longs[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Float[] 转换 float[] + * @param floats Float[] + * @param defaultValue 转换失败使用得默认值 + * @return float[] + */ + public static float[] floatsToFloats(final Float[] floats, final float defaultValue) { + if (floats != null) { + // 获取数组长度 + int len = floats.length; + // 创建数组 + float[] array = new float[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = floats[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Double[] 转换 double[] + * @param doubles Double[] + * @param defaultValue 转换失败使用得默认值 + * @return double[] + */ + public static double[] doublesToDoubles(final Double[] doubles, final double defaultValue) { + if (doubles != null) { + // 获取数组长度 + int len = doubles.length; + // 创建数组 + double[] array = new double[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = doubles[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Boolean[] 转换 boolean[] + * @param booleans Boolean[] + * @param defaultValue 转换失败使用得默认值 + * @return boolean[] + */ + public static boolean[] booleansToBooleans(final Boolean[] booleans, final boolean defaultValue) { + if (booleans != null) { + // 获取数组长度 + int len = booleans.length; + // 创建数组 + boolean[] array = new boolean[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = booleans[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + // ============= + // = 转换 List = + // ============= + + /** + * 转换数组为集合 + * @param array 数组 + * @param 泛型 + * @return {@link List} + */ + public static List asList(final T[] array) { + if (array != null) { + try { + return new ArrayList<>(Arrays.asList(array)); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "asList"); + } + } + return null; + } + + // = + + /** + * 转换数组为集合 + * @param ints int[] + * @return {@link List} + */ + public static List asList(final int[] ints) { + if (ints != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = ints.length; i < len; i++) { + lists.add(ints[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param bytes byte[] + * @return {@link List} + */ + public static List asList(final byte[] bytes) { + if (bytes != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = bytes.length; i < len; i++) { + lists.add(bytes[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param chars char[] + * @return {@link List} + */ + public static List asList(final char[] chars) { + if (chars != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = chars.length; i < len; i++) { + lists.add(chars[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param shorts short[] + * @return {@link List} + */ + public static List asList(final short[] shorts) { + if (shorts != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = shorts.length; i < len; i++) { + lists.add(shorts[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param longs long[] + * @return {@link List} + */ + public static List asList(final long[] longs) { + if (longs != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = longs.length; i < len; i++) { + lists.add(longs[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param floats float[] + * @return {@link List} + */ + public static List asList(final float[] floats) { + if (floats != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = floats.length; i < len; i++) { + lists.add(floats[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param doubles double[] + * @return {@link List} + */ + public static List asList(final double[] doubles) { + if (doubles != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = doubles.length; i < len; i++) { + lists.add(doubles[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param booleans boolean[] + * @return {@link List} + */ + public static List asList(final boolean[] booleans) { + if (booleans != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = booleans.length; i < len; i++) { + lists.add(booleans[i]); + } + return lists; + } + return null; + } + + // ============ + // = 快捷判断 = + // ============ + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + // = + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @param 泛型 + * @return 拼接后的数组集合 + */ + public static T[] arraycopy(final T[] prefix, final T[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建集合 + List lists = new ArrayList<>(prefixLength + suffixLength); + // 进行判断处理 + if (prefixLength != 0) { + for (int i = 0; i < prefixLength; i++) { + lists.add(prefix[i]); + } + } + if (suffixLength != 0) { + for (int i = 0; i < suffixLength; i++) { + lists.add(suffix[i]); + } + } + if (prefix != null) { + return (T[]) Arrays.copyOf(lists.toArray(), lists.size(), prefix.getClass()); + } else { + return (T[]) Arrays.copyOf(lists.toArray(), lists.size(), suffix.getClass()); + } + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static int[] arraycopy(final int[] prefix, final int[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + int[] arrays = new int[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static byte[] arraycopy(final byte[] prefix, final byte[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + byte[] arrays = new byte[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static char[] arraycopy(final char[] prefix, final char[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + char[] arrays = new char[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static short[] arraycopy(final short[] prefix, final short[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + short[] arrays = new short[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static long[] arraycopy(final long[] prefix, final long[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + long[] arrays = new long[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static float[] arraycopy(final float[] prefix, final float[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + float[] arrays = new float[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static double[] arraycopy(final double[] prefix, final double[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + double[] arrays = new double[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static boolean[] arraycopy(final boolean[] prefix, final boolean[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + boolean[] arrays = new boolean[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + // = + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @param 泛型 + * @return 指定长度数组 + */ + public static T[] newarray(final int length, final T[] data) { + if (data != null && length > 0) { + List lists = new ArrayList<>(); + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + lists.add(data[i]); + } + } else { + for (int i = 0; i < dataLength; i++) { + lists.add(data[i]); + } + // 补充长度 + for (int i = 0, len = length - dataLength; i < len; i++) { + lists.add(null); + } + } + // 返回数据 + return (T[]) Arrays.copyOf(lists.toArray(), length, data.getClass()); + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static int[] newarray(final int length, final int[] data) { + if (data != null && length > 0) { + int[] arrays = new int[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static byte[] newarray(final int length, final byte[] data) { + if (data != null && length > 0) { + byte[] arrays = new byte[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static char[] newarray(final int length, final char[] data) { + if (data != null && length > 0) { + char[] arrays = new char[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static short[] newarray(final int length, final short[] data) { + if (data != null && length > 0) { + short[] arrays = new short[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static long[] newarray(final int length, final long[] data) { + if (data != null && length > 0) { + long[] arrays = new long[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static float[] newarray(final int length, final float[] data) { + if (data != null && length > 0) { + float[] arrays = new float[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static double[] newarray(final int length, final double[] data) { + if (data != null && length > 0) { + double[] arrays = new double[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static boolean[] newarray(final int length, final boolean[] data) { + if (data != null && length > 0) { + boolean[] arrays = new boolean[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + // = + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @param 泛型 + * @return 裁剪后的数组 + */ + public static T[] subarray(final T[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + List lists = new ArrayList<>(length); + for (int i = off; i < off + length; i++) { + lists.add(data[i]); + } + return (T[]) Arrays.copyOf(lists.toArray(), length, data.getClass()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static int[] subarray(final int[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + int[] arrays = new int[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static byte[] subarray(final byte[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + byte[] arrays = new byte[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static char[] subarray(final char[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + char[] arrays = new char[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static short[] subarray(final short[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + short[] arrays = new short[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static long[] subarray(final long[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + long[] arrays = new long[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static float[] subarray(final float[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + float[] arrays = new float[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static double[] subarray(final double[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + double[] arrays = new double[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static boolean[] subarray(final boolean[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + boolean[] arrays = new boolean[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + // = + + /** + * 追加数组内容字符串 + * @param data 数组 + * @param 泛型 + * @return 追加数组内容字符串 + */ + public static String appendToString(final T[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + ((data[0] == null) ? "null" : data[0].toString()) + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + T t = data[i]; + builder.append("\"").append(((t == null) ? "null" : t.toString())).append("\","); + } + T end = data[len - 1]; + builder.append("\"" + ((end == null) ? "null" : end.toString()) + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final int[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final byte[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final char[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final short[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final long[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final float[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final double[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final boolean[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/BigDecimalUtils.java b/lib/DevApp/src/main/java/dev/utils/common/BigDecimalUtils.java new file mode 100644 index 0000000000..6bf8a3bb23 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/BigDecimalUtils.java @@ -0,0 +1,764 @@ +package dev.utils.common; + +import java.math.BigDecimal; + +import dev.utils.JCLogUtils; + +/** + * detail: 资金运算工具类 + * @author Ttt + *
+ *     @see 
+ *     向下取 round = BigDecimal.ROUND_DOWN;
+ * 
+ */ +public final class BigDecimalUtils { + + private BigDecimalUtils() { + } + + // 日志 TAG + private static final String TAG = BigDecimalUtils.class.getSimpleName(); + // 默认保留位数 + private static final int DEF_DIV_SCALE = 10; + + /** + * 提供精确的加法运算 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(final double v1, final double v2) { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的加法运算 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static BigDecimal add(final String v1, final String v2) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + return b1.add(b2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add"); + } + return null; + } + + // = + + /** + * 提供精确的加法运算 + * @param v1 被加数 + * @param v2 加数 + * @param scale 保留 scale 位小数 + * @return 两个参数的和 + */ + public static double add(final double v1, final double v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (scale <= 0) { + return b1.add(b2).intValue(); + } else { + return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add"); + } + return 0; + } + + /** + * 提供精确的加法运算 + * @param v1 被加数 + * @param v2 加数 + * @param scale 保留 scale 位小数 + * @return 两个参数的和 + */ + public static String add(final String v1, final String v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + if (scale <= 0) { + return b1.add(b2).intValue() + ""; + } else { + return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add"); + } + return null; + } + + // = + + /** + * 提供精确的减法运算 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double substract(final double v1, final double v2) { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的减法运算 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static BigDecimal substract(final String v1, final String v2) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + return b1.subtract(b2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "substract"); + } + return null; + } + + // = + + /** + * 提供精确的减法运算 + * @param v1 被减数 + * @param v2 减数 + * @param scale 保留 scale 位小数 + * @return 两个参数的差 + */ + public static String substract(final double v1, final double v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (scale <= 0) { + return b1.subtract(b2).intValue() + ""; + } else { + return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "substract"); + } + return null; + } + + /** + * 提供精确的减法运算 + * @param v1 被减数 + * @param v2 减数 + * @param scale 保留 scale 位小数 + * @return 两个参数的差 + */ + public static String substract(final String v1, final String v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + if (scale <= 0) { + return b1.subtract(b2).intValue() + ""; + } else { + return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "substract"); + } + return null; + } + + // = + + /** + * 提供精确的乘法运算 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double multiply(final double v1, final double v2) { + try { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "multiply"); + } + return 0d; + } + + /** + * 提供精确的乘法运算 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double multiply(final String v1, final String v2) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + return b1.multiply(b2).doubleValue(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "multiply"); + } + return 0d; + } + + // = + + /** + * 提供精确的乘法运算 + * @param v1 被乘数 + * @param v2 乘数 + * @param scale 保留 scale 位小数 + * @return 两个参数的积 + */ + public static double multiply(final double v1, final double v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (scale <= 0) { + return b1.multiply(b2).intValue(); + } else { + return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "multiply"); + } + return 0d; + } + + // = + + /** + * 提供精确的乘法运算 + * @param v1 被乘数 + * @param v2 乘数 + * @param scale 保留 scale 位小数 + * @return 两个参数的积 + */ + public static String multiply(final String v1, final String v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + if (scale <= 0) { + return b1.multiply(b2).intValue() + ""; + } else { + return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "multiply"); + } + return null; + } + + // = + + /** + * 提供 ( 相对 ) 精确的除法运算 + * 当发生除不尽的情况时, 精确到小数点以后 10 位, 以后的数字四舍五入 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double divide(final double v1, final double v2) { + return divide(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供 ( 相对 ) 精确的除法运算 + * 当发生除不尽的情况时, 由 scale 参数指定精度, 以后的数字四舍五入 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示需要精确到小数点以后几位 + * @return 两个参数的商 + */ + public static double divide(final double v1, final double v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (scale <= 0) { + return b1.divide(b2).intValue(); + } else { + return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "divide"); + } + return 0d; + } + + /** + * 提供 ( 相对 ) 精确的除法运算 + * 当发生除不尽的情况时, 由 scale 参数指定精度, 以后的数字四舍五入 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示需要精确到小数点以后几位 + * @return 两个参数的商 + */ + public static String divide(final String v1, final String v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + if (scale <= 0) { + return b1.divide(b2).intValue() + ""; + } else { + return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "divide"); + } + return null; + } + + // = + + /** + * 提供精确的小数位四舍五入处理 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(final double v, final int scale) { + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = new BigDecimal("1"); + if (scale <= 0) { + return b.divide(one).intValue(); + } else { + return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + } + + /** + * 提供精确的小数位四舍五入处理 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static String round(final String v, final int scale) { + try { + BigDecimal b = new BigDecimal(v); + if (scale <= 0) { + return b.intValue() + ""; + } else { + return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "round"); + } + return null; + } + + // = + + /** + * 提供精确的小数位获取 + * @param v 待处理的数字 + * @param scale 小数点后保留几位 + * @param roundingMode 取小数点模式 + * @return 最后的结果 + */ + public static double round(final double v, final int scale, final int roundingMode) { + try { + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = new BigDecimal("1"); + if (scale <= 0) { + return b.divide(one).intValue(); + } else { + return b.divide(one, scale, roundingMode).doubleValue(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "round"); + } + return 0d; + } + + // = + + /** + * 取余数 + * @param v1 被除数 + * @param v2 除数 + * @param scale 小数点后保留几位 + * @return 余数 + */ + public static String remainder(final String v1, final String v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + if (scale <= 0) { + return b1.remainder(b2).intValue() + ""; + } else { + return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remainder"); + } + return null; + } + + /** + * 取余数 + * @param v1 被除数 + * @param v2 除数 + * @param scale 小数点后保留几位 + * @return 余数 + */ + public static BigDecimal remainder(final BigDecimal v1, final BigDecimal v2, final int scale) { + try { + if (scale <= 0) { + return v1.remainder(v2); + } else { + return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remainder"); + } + return null; + } + + // = + + /** + * 比较大小 + * @param v1 输入的数值 + * @param v2 被比较的数字 + * @return {@code true} v1 >= v2, {@code false} v1 < v2 + */ + public static Boolean compare(final String v1, final String v2) { + try { + return new BigDecimal(v1).compareTo(new BigDecimal(v2)) != -1; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "compare"); + } + return true; + } + + /** + * 比较大小 + * @param v1 输入的数值 + * @param v2 被比较的数字 + * @return {@code true} v1 >= v2, {@code false} v1 < v2 + */ + public static Boolean compare(final BigDecimal v1, final BigDecimal v2) { + try { + return v1.compareTo(v2) != -1; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "compare"); + } + return false; + } + + /** + * 比较大小 + * @param v1 输入的数值 + * @param v2 被比较的数字 + * @return {@code true} v1 >= v2, {@code false} v1 < v2 + */ + public static Boolean compare(final String v1, final double v2) { + try { + return new BigDecimal(v1).compareTo(new BigDecimal(Double.toString(v2))) != -1; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "compare"); + } + return true; + } + + // = + + /** + * 金额分割, 四舍五入金额 + * @param value 金额数值 + * @return 指定格式处理的字符串 + */ + public static String formatMoney(final BigDecimal value) { + return formatMoney(value, 2, BigDecimal.ROUND_HALF_UP, 3, ","); + } + + /** + * 金额分割, 四舍五入金额 + * @param value 金额数值 + * @param scale 小数点后保留几位 + * @return 指定格式处理的字符串 + */ + public static String formatMoney(final BigDecimal value, final int scale) { + return formatMoney(value, scale, BigDecimal.ROUND_HALF_UP, 3, ","); + } + + /** + * 金额分割, 四舍五入金额 + * @param value 金额数值 + * @param scale 小数点后保留几位 + * @param mode 处理模式 + * @return 指定格式处理的字符串 + */ + public static String formatMoney(final BigDecimal value, final int scale, final int mode) { + return formatMoney(value, scale, mode, 3, ","); + } + + /** + * 金额分割, 四舍五入金额 + * @param value 金额数值 + * @param scale 小数点后保留几位 + * @param mode 处理模式 + * @param splitNumber 拆分位数 + * @return 指定格式处理的字符串 + */ + public static String formatMoney(final BigDecimal value, final int scale, final int mode, final int splitNumber) { + return formatMoney(value, scale, mode, splitNumber, ","); + } + + /** + * 金额分割, 四舍五入金额 + * @param value 金额数值 + * @param scale 小数点后保留几位 + * @param mode 处理模式 + * @param splitNumber 拆分位数 + * @param splitSymbol 拆分符号 + * @return 指定格式处理的字符串 + */ + public static String formatMoney(final BigDecimal value, final int scale, final int mode, final int splitNumber, final String splitSymbol) { + if (value == null) return null; + try { + // 如果等于 0, 直接返回 + if (value.doubleValue() == 0) { + return value.setScale(scale, mode).toPlainString(); + } + // 获取原始值字符串 - 非科学计数法 + String valuePlain = value.toPlainString(); + // 判断是否负数 + boolean isNegative = valuePlain.startsWith("-"); + // 处理后的数据 + BigDecimal bigDecimal = new BigDecimal(isNegative ? valuePlain.substring(1) : valuePlain); + // 范围处理 + valuePlain = bigDecimal.setScale(scale, mode).toPlainString(); + // 进行拆分小数点处理 + String[] values = valuePlain.split("\\."); + // 判断是否存在小数点 + boolean isDecimal = (values.length == 2); + + // 拼接符号 + String symbol = (splitSymbol != null) ? splitSymbol : ""; + // 防止出现负数 + int number = Math.max(splitNumber, 0); + // 格式化数据 - 拼接处理 + StringBuilder builder = new StringBuilder(); + // 进行处理小数点前的数值 + for (int len = values[0].length() - 1, i = len, splitPos = 1; i >= 0; i--) { + // 获取数据 + char ch = values[0].charAt(i); + builder.append(ch); // 保存数据 + // 判断是否需要追加符号 + if (number > 0 && splitPos % number == 0 && i != 0) { + builder.append(symbol); + } + splitPos++; + } + // 倒序处理 + builder.reverse(); + // 存在小数点, 则进行拼接 + if (isDecimal) { + builder.append(".").append(values[1]); + } + // 判断是否负数 + return isNegative ? "-" + builder.toString() : builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "formatMoney"); + } + return null; + } + + // = + + /** + * 获取自己想要的数据格式 + * @param value 需处理的数据 + * @param numOfIntPart 整数位数 + * @param numOfDecimalPart 小数位数 + * @return 处理过的数据 + */ + public static String adjustDouble(final String value, final int numOfIntPart, final int numOfDecimalPart) { + if (value == null) return null; + try { + // 按小数点的位置分割成整数部分和小数部分 + String[] array = value.split("\\."); + char[] tempA = new char[numOfIntPart]; + char[] tempB = new char[numOfDecimalPart]; + // 整数部分满足精度要求 ( 情况 1) + if (array[0].length() == numOfIntPart) { + // 直接获取整数部分长度字符 + for (int i = 0; i < array[0].length(); i++) { + tempA[i] = array[0].charAt(i); + } + // 小数部分精度大于或等于指定的精度 + if (numOfDecimalPart <= array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + tempB[i] = array[1].charAt(i); + } + } + // 小数部分精度小于指定的精度 + if (numOfDecimalPart > array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + if (i < array[1].length()) { + tempB[i] = array[1].charAt(i); + } else { + tempB[i] = '0'; + } + } + } + if (numOfDecimalPart == 0) { + return String.valueOf(tempA) + String.valueOf(tempB); + } + return String.valueOf(tempA) + "." + String.valueOf(tempB); + } + // 整数部分位数大于精度要求 ( 情况 2) + if (array[0].length() > numOfIntPart) { + // 先倒序获取指定位数的整数 + for (int i = array[0].length() - 1, j = 0; (i >= array[0].length() - numOfIntPart) && (j < numOfIntPart); i--, j++) { + tempA[j] = array[0].charAt(i); + } + char[] tempA1 = new char[numOfIntPart]; + // 调整顺序 + for (int j = 0, k = tempA.length - 1; j < numOfIntPart && (k >= 0); j++, k--) { + tempA1[j] = tempA[k]; + } + // 小数部分精度大于或等于指定的精度 + if (numOfDecimalPart <= array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + tempB[i] = array[1].charAt(i); + } + } + // 小数部分精度小于指定的精度 + if (numOfDecimalPart > array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + if (i < array[1].length()) { + tempB[i] = array[1].charAt(i); + } else { + tempB[i] = '0'; + } + } + } + return String.valueOf(tempA1) + "." + String.valueOf(tempB); + } + // 整数部分满足精度要求 ( 情况 3) + if (array[0].length() == numOfIntPart) { + // 直接获取整数部分长度字符 + for (int i = 0; i < array[0].length(); i++) { + tempA[i] = array[0].charAt(i); + } + // 小数部分精度小于指定的精度 + if (numOfDecimalPart > array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + if (i < array[1].length()) { + tempB[i] = array[1].charAt(i); + } else { + tempB[i] = '0'; + } + } + } + // 小数部分精度大于或等于指定的精度 + if (numOfDecimalPart <= array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + tempB[i] = array[1].charAt(i); + } + } + if (numOfDecimalPart == 0) { + return String.valueOf(tempA) + String.valueOf(tempB); + } + return String.valueOf(tempA) + "." + String.valueOf(tempB); + } + // 整数部分大于精度要求 ( 情况 4) + if (array[0].length() > numOfIntPart) { + // 先倒序获取指定位数的整数 + for (int i = array[0].length() - 1, j = 0; (i >= array[0].length() - numOfIntPart + 1) && (j < numOfIntPart); i--, j++) { + tempA[j] = array[0].charAt(i); + } + char[] tempA1 = new char[numOfIntPart]; + // 调整顺序 + for (int j = 0, k = tempA.length - 1; j < numOfIntPart && (k >= 0); j++) { + tempA1[j] = tempA[k]; + k--; + } + // 小数部分精度小于指定的精度 + if (numOfDecimalPart > array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + if (i >= array[1].length()) { + tempB[i] = '0'; + } else { + tempB[i] = array[1].charAt(i); + } + } + } + // 小数部分精度大于或等于指定的精度 + if (numOfDecimalPart <= array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + tempB[i] = array[1].charAt(i); + } + } + if (numOfDecimalPart == 0) { + return String.valueOf(tempA1) + String.valueOf(tempB); + } + return String.valueOf(tempA1) + "." + String.valueOf(tempB); + } + // 整数部分小于精度要求 ( 情况 5) + if (array[0].length() < numOfIntPart) { + // 先倒序获取指定位数的整数 + char[] tempA1 = new char[numOfIntPart]; + for (int i = array[0].length() - 1, j = 0; (i >= numOfIntPart - array[0].length() - (numOfIntPart - array[0].length())) && (j < numOfIntPart); i--, j++) { + tempA1[j] = array[0].charAt(i); + } + // 补 0 + for (int i = array[0].length(); i < array[0].length() + numOfIntPart - array[0].length(); i++) { + tempA1[i] = '0'; + } + + char[] tempA2 = new char[numOfIntPart]; + // 调整顺序 + for (int j = 0, k = tempA1.length - 1; j < numOfIntPart && (k >= 0); j++) { + tempA2[j] = tempA1[k]; + k--; + } + // 小数部分精度小于指定的精度 + if (numOfDecimalPart > array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + if (i < array[1].length()) { + tempB[i] = array[1].charAt(i); + } else { + tempB[i] = '0'; + } + } + } + // 小数部分精度大于或等于指定的精度 + if (numOfDecimalPart <= array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + tempB[i] = array[1].charAt(i); + } + } + if (numOfDecimalPart == 0) { + return String.valueOf(tempA2) + String.valueOf(tempB); + } + return String.valueOf(tempA2) + "." + String.valueOf(tempB); + } + // ( 情况 6) + if ((array[0].length() < numOfIntPart) && (array[1].length() < numOfDecimalPart)) { + String data = value; + for (int i = 0; i < numOfIntPart - array[0].length(); i++) { + data = "0" + data; + } + for (int i = 0; i < numOfDecimalPart - array[1].length(); i++) { + data = data + "0"; + } + return data; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "adjustDouble"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/ClassUtils.java b/lib/DevApp/src/main/java/dev/utils/common/ClassUtils.java new file mode 100644 index 0000000000..06c506f3c0 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/ClassUtils.java @@ -0,0 +1,233 @@ +package dev.utils.common; + +import java.lang.reflect.Constructor; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.Date; + +import dev.utils.JCLogUtils; + +/** + * detail: 类 (Class) 工具类 + * @author Ttt + */ +public final class ClassUtils { + + private ClassUtils() { + } + + // 日志 TAG + private static final String TAG = ClassUtils.class.getSimpleName(); + + /** + * 判断类是否是基础数据类型 - 目前支持 11 种 + * @param clazz {@link Class} + * @return {@code true} yes, {@code false} no + */ + public static boolean isBaseDataType(final Class clazz) { + return clazz != null && (clazz.isPrimitive() || clazz.equals(String.class) || clazz.equals(Boolean.class) + || clazz.equals(Integer.class) || clazz.equals(Long.class) || clazz.equals(Float.class) + || clazz.equals(Double.class) || clazz.equals(Byte.class) || clazz.equals(Character.class) + || clazz.equals(Short.class) || clazz.equals(Date.class) || clazz.equals(byte[].class) + || clazz.equals(Byte[].class)); + } + + /** + * 根据类获取对象, 不再必须一个无参构造 + * @param clazz {@link Class} + * @param 泛型 + * @return 初始化后的对象 + */ + public static T newInstance(final Class clazz) { + if (clazz == null) return null; + try { + Constructor[] cons = clazz.getDeclaredConstructors(); + for (Constructor c : cons) { + Class[] cls = c.getParameterTypes(); + if (cls.length == 0) { + c.setAccessible(true); + return (T) c.newInstance(); + } else { + Object[] objs = new Object[cls.length]; + for (int i = 0; i < cls.length; i++) { + objs[i] = getDefaultPrimiticeValue(cls[i]); + } + c.setAccessible(true); + return (T) c.newInstance(objs); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "newInstance"); + } + return null; + } + + /** + * 判断 Class 是否为原始类型 (boolean、char、byte、short、int、long、float、double) + * @param clazz {@link Class} + * @return 原始类型值 + */ + public static Object getDefaultPrimiticeValue(final Class clazz) { + if (clazz != null && clazz.isPrimitive()) { + return clazz == boolean.class ? false : 0; + } + return null; + } + + /** + * 判断是否集合类型 + * @param clazz {@link Class} + * @return {@code true} yes, {@code false} no + */ + public static boolean isCollection(final Class clazz) { + return (clazz != null && Collection.class.isAssignableFrom(clazz)); + } + + /** + * 判断是否数组类型 + * @param clazz {@link Class} + * @return {@code true} yes, {@code false} no + */ + public static boolean isArray(final Class clazz) { + return (clazz != null && clazz.isArray()); + } + + // = + + /** + * 获取父类泛型类型 + * @param object Object + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericSuperclass(final Object object) { + return getGenericSuperclass(object, 0); + } + + /** + * 获取父类泛型类型 + * @param object Object + * @param pos 泛型参数索引 + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericSuperclass(final Object object, final int pos) { + if (object != null) { + return getGenericSuperclass(object.getClass(), pos); + } + return null; + } + + // = + + /** + * 获取父类泛型类型 + * @param clazz {@link Class} + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericSuperclass(final Class clazz) { + return getGenericSuperclass(clazz, 0); + } + + /** + * 获取父类泛型类型 + * @param clazz {@link Class} + * @param pos 泛型参数索引 + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericSuperclass(final Class clazz, final int pos) { + if (clazz != null && pos >= 0) { + try { + return (Class) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getGenericSuperclass"); + } + } + return null; + } + + // = + + /** + * 获取接口泛型类型 + * @param object Object + * @param interfaceClazz 接口 Class + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericInterfaces(final Object object, final Class interfaceClazz) { + return getGenericInterfaces(object, interfaceClazz, 0); + } + + /** + * 获取接口泛型类型 + * @param object Object + * @param interfaceClazz 接口 Class + * @param pos 泛型参数索引 + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericInterfaces(final Object object, final Class interfaceClazz, final int pos) { + if (object != null) { + return getGenericInterfaces(object.getClass(), interfaceClazz, pos); + } + return null; + } + + // = + + /** + * 获取接口泛型类型 + * @param clazz {@link Class} + * @param interfaceClazz 接口 Class + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericInterfaces(final Class clazz, final Class interfaceClazz) { + return getGenericInterfaces(clazz, interfaceClazz, 0); + } + + /** + * 获取接口泛型类型 + * @param clazz {@link Class} + * @param interfaceClazz 接口 Class + * @param pos 泛型参数索引 + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericInterfaces(final Class clazz, final Class interfaceClazz, final int pos) { + if (clazz != null && interfaceClazz != null && pos >= 0) { + try { + // 获取接口类名 + String iName = interfaceClazz.getName(); + if (iName.equals("")) return null; + // 获取接口泛型类型数组 + Type[] types = clazz.getGenericInterfaces(); + // 循环类型 + for (Type type : types) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + // 判断是否属于该接口 + String rawType = parameterizedType.getRawType().toString(); + // 判断接口包名是否一致 + if (rawType.startsWith("interface ")) { + if (rawType.equals("interface " + iName)) { + return (Class) parameterizedType.getActualTypeArguments()[pos]; + } + } else { + if (rawType.equals(iName)) { + return (Class) parameterizedType.getActualTypeArguments()[pos]; + } + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getGenericInterfaces"); + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/CloneUtils.java b/lib/DevApp/src/main/java/dev/utils/common/CloneUtils.java new file mode 100644 index 0000000000..b11e17b42a --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/CloneUtils.java @@ -0,0 +1,147 @@ +package dev.utils.common; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: 克隆工具类 + * @author Ttt + */ +public final class CloneUtils { + + private CloneUtils() { + } + + // 日志 TAG + private static final String TAG = CloneUtils.class.getSimpleName(); + + /** + * 进行克隆 + * @param data Object implements {@link Serializable} + * @param 泛型 + * @return 克隆后的对象 + */ + public static T deepClone(final Serializable data) { + if (data == null) return null; + return (T) bytesToObject(serializableToBytes(data)); + } + + /** + * 通过序列化实体类, 获取对应的 byte[] 数据 + * @param serializable Object implements {@link Serializable} + * @return 克隆后 byte[] + */ + public static byte[] serializableToBytes(final Serializable serializable) { + if (serializable == null) return null; + ObjectOutputStream oos = null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(baos); + oos.writeObject(serializable); + return baos.toByteArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "serializableToBytes"); + return null; + } finally { + if (oos != null) { + try { + oos.close(); + } catch (Exception e) { + } + } + } + } + + // = + + /** + * 进行克隆 + * @param map 存储集合 + * @param datas 需要克隆的数据源 + * @param key + * @param value + */ + public static void deepClone(final Map map, final Map datas) { + if (map != null && datas != null && datas.size() > 0) { + Iterator> iterator = datas.entrySet().iterator(); + while (iterator.hasNext()) { + try { + Map.Entry entry = iterator.next(); + // 获取 key + K key = entry.getKey(); + // 克隆对象 + V cloneObj = (V) bytesToObject(serializableToBytes((Serializable) entry.getValue())); + if (cloneObj != null) { + // 保存到集合 + map.put(key, cloneObj); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "deepClone"); + } + } + } + } + + /** + * 进行克隆 + * @param collection 存储集合 + * @param datas 需要克隆的数据源 + * @param 泛型 + */ + public static void deepClone(final Collection collection, final Collection datas) { + if (collection != null && datas != null && datas.size() > 0) { + Iterator iterator = datas.iterator(); + while (iterator.hasNext()) { + try { + // 克隆对象 + T cloneObj = (T) bytesToObject(serializableToBytes((Serializable) iterator.next())); + if (cloneObj != null) { + collection.add(cloneObj); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "deepClone"); + } + } + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + /** + * byte[] 转为 Object + * @param bytes byte[] + * @return {@link Object} + */ + private static Object bytesToObject(final byte[] bytes) { + if (bytes == null) return null; + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); + return ois.readObject(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToObject"); + } finally { + if (ois != null) { + try { + ois.close(); + } catch (Exception e) { + } + } + } + return null; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/CloseUtils.java b/lib/DevApp/src/main/java/dev/utils/common/CloseUtils.java similarity index 80% rename from DevLibUtils/src/main/java/dev/utils/common/CloseUtils.java rename to lib/DevApp/src/main/java/dev/utils/common/CloseUtils.java index 7d764f519e..d67d24982c 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/CloseUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/common/CloseUtils.java @@ -5,22 +5,20 @@ import dev.utils.JCLogUtils; /** - * detail: 关闭工具类 - (关闭IO流等) - * Created by Blankj - * http://blankj.com - * Update to Ttt + * detail: 关闭 (IO 流 ) 工具类 + * @author Ttt */ public final class CloseUtils { private CloseUtils() { } - // 日志TAG + // 日志 TAG private static final String TAG = CloseUtils.class.getSimpleName(); /** * 关闭 IO - * @param closeables closeables + * @param closeables Closeable[] */ public static void closeIO(final Closeable... closeables) { if (closeables == null) return; @@ -37,7 +35,7 @@ public static void closeIO(final Closeable... closeables) { /** * 安静关闭 IO - * @param closeables closeables + * @param closeables Closeable[] */ public static void closeIOQuietly(final Closeable... closeables) { if (closeables == null) return; @@ -46,9 +44,8 @@ public static void closeIOQuietly(final Closeable... closeables) { try { closeable.close(); } catch (Exception ignore) { - JCLogUtils.eTag(TAG, ignore, "closeIO"); } } } } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/CollectionUtils.java b/lib/DevApp/src/main/java/dev/utils/common/CollectionUtils.java new file mode 100644 index 0000000000..c7f4bbdc60 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/CollectionUtils.java @@ -0,0 +1,1170 @@ +package dev.utils.common; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import dev.utils.JCLogUtils; + +/** + * detail: 集合工具类 (Collection - List、Set、Queue) 等 + * @author Ttt + *
+ *     @see 
+ * 
+ */ +public final class CollectionUtils { + + private CollectionUtils() { + } + + // 日志 TAG + private static final String TAG = CollectionUtils.class.getSimpleName(); + + // ============== + // = Collection = + // ============== + + /** + * 判断 Collection 是否为 null + * @param collection {@link Collection} + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Collection collection) { + return (collection == null || collection.size() == 0); + } + + /** + * 判断 Collection 是否不为 null + * @param collection {@link Collection} + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Collection collection) { + return (collection != null && collection.size() != 0); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取 Collection 长度 + * @param collection {@link Collection} + * @return 如果 Collection 为 null, 则返回默认长度, 如果不为 null, 则返回 collection.size() + */ + public static int length(final Collection collection) { + return length(collection, 0); + } + + /** + * 获取 Collection 长度 + * @param collection {@link Collection} + * @param defaultLength 集合为 null 默认长度 + * @return 如果 Collection 为 null, 则返回 defaultLength, 如果不为 null, 则返回 collection.size() + */ + public static int length(final Collection collection, final int defaultLength) { + return collection != null ? collection.size() : defaultLength; + } + + // = + + /** + * 获取长度 to Collection 是否等于期望长度 + * @param collection {@link Collection} + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Collection collection, final int length) { + return collection != null && collection.size() == length; + } + + // = + + /** + * 判断 Collection 长度是否大于指定长度 + * @param collection {@link Collection} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean greaterThan(final Collection collection, final int length) { + return collection != null && collection.size() > length; + } + + /** + * 判断 Collection 长度是否大于等于指定长度 + * @param collection {@link Collection} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean greaterThanOrEqual(final Collection collection, final int length) { + return collection != null && collection.size() >= length; + } + + // = + + /** + * 判断 Collection 长度是否小于指定长度 + * @param collection {@link Collection} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean lessThan(final Collection collection, final int length) { + return collection != null && collection.size() < length; + } + + /** + * 判断 Collection 长度是否小于等于指定长度 + * @param collection {@link Collection} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean lessThanOrEqual(final Collection collection, final int length) { + return collection != null && collection.size() <= length; + } + + // ============ + // = 数据获取 = + // ============ + + /** + * 获取数据 + * @param collection {@link Collection} + * @param pos 索引 + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final Collection collection, final int pos) { + if (collection != null) { + // 防止索引为负数 + if (pos < 0) { + return null; + } + if (collection instanceof List) { + try { + return (T) ((List) collection).get(pos); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } else { + try { + return (T) collection.toArray()[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + } + return null; + } + + /** + * 获取第一条数据 + * @param collection {@link Collection} + * @param 泛型 + * @return 索引为 0 的值 + */ + public static T getFirst(final Collection collection) { + return get(collection, 0); + } + + /** + * 获取最后一条数据 + * @param collection {@link Collection} + * @param 泛型 + * @return 索引 length - 1 的值 + */ + public static T getLast(final Collection collection) { + return get(collection, length(collection) - 1); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param offset 偏移量 + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final Collection collection, final T value, final int number, final boolean notNull, final int offset) { + if (collection != null) { + if (notNull && value == null) { + return null; + } + try { + // 保存当前临时次数 + int temp = number; + // 转换数组 + T[] arrays = (T[]) collection.toArray(); + // 进行循环判断 + for (int i = 0, len = arrays.length; i < len; i++) { + T t = arrays[i]; + // 判断值是否一样 + if (equals(t, value)) { + if (temp - 1 < 0) { + return arrays[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return null; + } + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的索引 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param offset 偏移量 + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final Collection collection, final T value, final int number, final boolean notNull, final int offset) { + if (collection != null) { + if (notNull && value == null) { + return -1; + } + try { + // 保存当前临时次数 + int temp = number; + // 转换数组 + T[] arrays = (T[]) collection.toArray(); + // 进行循环判断 + for (int i = 0, len = arrays.length; i < len; i++) { + T t = arrays[i]; + // 判断值是否一样 + if (equals(t, value)) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定 value 获取索引 + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final Collection collection, final T value) { + return getPosition(collection, value, 0, false, 0); + } + + /** + * 根据指定 value 获取索引 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final Collection collection, final T value, final int number) { + return getPosition(collection, value, number, false, 0); + } + + /** + * 根据指定 value 获取索引 + * @param collection {@link Collection} + * @param value 值 + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final Collection collection, final T value, final boolean notNull) { + return getPosition(collection, value, 0, notNull, 0); + } + + /** + * 根据指定 value 获取索引 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final Collection collection, final T value, final int number, final boolean notNull) { + return getPosition(collection, value, number, notNull, 0); + } + + // = + + /** + * 根据指定 value 获取索引, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应的索引 + */ + public static int getPositionNotNull(final Collection collection, final T value) { + return getPosition(collection, value, 0, true, 0); + } + + /** + * 根据指定 value 获取索引, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应的索引 + */ + public static int getPositionNotNull(final Collection collection, final T value, final int number) { + return getPosition(collection, value, number, true, 0); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置的下一个值 + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNext(final Collection collection, final T value) { + return get(collection, value, 0, false, 1); + } + + /** + * 根据指定 value 获取 value 所在位置的下一个值 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNext(final Collection collection, final T value, final int number) { + return get(collection, value, number, false, 1); + } + + /** + * 根据指定 value 获取 value 所在位置的下一个值 + * @param collection {@link Collection} + * @param value 值 + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNext(final Collection collection, final T value, final boolean notNull) { + return get(collection, value, 0, notNull, 1); + } + + /** + * 根据指定 value 获取 value 所在位置的下一个值 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNext(final Collection collection, final T value, final int number, final boolean notNull) { + return get(collection, value, number, notNull, 1); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置的下一个值, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNextNotNull(final Collection collection, final T value) { + return get(collection, value, 0, true, 1); + } + + /** + * 根据指定 value 获取 value 所在位置的下一个值, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNextNotNull(final Collection collection, final T value, final int number) { + return get(collection, value, number, true, 1); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置的上一个值 + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPrevious(final Collection collection, final T value) { + return get(collection, value, 0, false, -1); + } + + /** + * 根据指定 value 获取 value 所在位置的上一个值 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPrevious(final Collection collection, final T value, final int number) { + return get(collection, value, number, false, -1); + } + + /** + * 根据指定 value 获取 value 所在位置的上一个值 + * @param collection {@link Collection} + * @param value 值 + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPrevious(final Collection collection, final T value, final boolean notNull) { + return get(collection, value, 0, notNull, -1); + } + + /** + * 根据指定 value 获取 value 所在位置的上一个值 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPrevious(final Collection collection, final T value, final int number, final boolean notNull) { + return get(collection, value, number, notNull, -1); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置的上一个值, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPreviousNotNull(final Collection collection, final T value) { + return get(collection, value, 0, true, -1); + } + + /** + * 根据指定 value 获取 value 所在位置的上一个值, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPreviousNotNull(final Collection collection, final T value, final int number) { + return get(collection, value, number, true, -1); + } + + // ============ + // = 添加数据 = + // ============ + + /** + * 添加一条数据 + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean add(final Collection collection, final T value) { + return add(collection, value, false); + } + + /** + * 添加一条数据 + * @param collection {@link Collection} + * @param value 值 + * @param notNull 是否不允许添加 null 数据 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean add(final Collection collection, final T value, final boolean notNull) { + if (collection != null) { + if (notNull) { + if (value != null) { + try { + return collection.add(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add notNull"); + } + } + } else { + try { + return collection.add(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add"); + } + } + } + return false; + } + + /** + * 添加一条数据 - value 不允许为 null + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean addNotNull(final Collection collection, final T value) { + return add(collection, value, true); + } + + // = + + /** + * 添加集合数据 + * @param collection {@link Collection} + * @param values 准备添加的值 ( 集合 ) + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean addAll(final Collection collection, final Collection values) { + return addAll(collection, values, false); + } + + /** + * 添加集合数据 + * @param collection {@link Collection} + * @param values 准备添加的值 ( 集合 ) + * @param notNull 是否不允许添加 null 值 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean addAll(final Collection collection, final Collection values, final boolean notNull) { + if (collection != null && values != null) { + if (notNull) { + try { + for (T t : values) { + if (t != null) { + collection.add(t); + } + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addAll notNull"); + } + } else { + try { + return collection.addAll(values); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addAll"); + } + } + } + return false; + } + + /** + * 添加集合数据 - values 内的值不允许为 null + * @param collection {@link Collection} + * @param values 准备添加的值 ( 集合 ) + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean addAllNotNull(final Collection collection, final Collection values) { + return addAll(collection, values, true); + } + + // ================================ + // = 添加数据 - 到指定索引 (List) = + // ================================ + + /** + * 添加一条数据到指定索引后 + * @param index 索引 + * @param list 集合 + * @param value 值 + * @param 泛型 + */ + public static void add(final int index, final List list, final T value) { + add(index, list, value, false); + } + + /** + * 添加一条数据到指定索引后 + * @param index 索引 + * @param list 集合 + * @param value 值 + * @param notNull 是否不允许添加 null 数据 + * @param 泛型 + */ + public static void add(final int index, final List list, final T value, final boolean notNull) { + if (list != null) { + if (notNull) { + if (value != null) { + try { + list.add(index, value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add notNull"); + } + } + } else { + try { + list.add(index, value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add"); + } + } + } + } + + /** + * 添加一条数据到指定索引后 - value 不允许为 null + * @param index 索引 + * @param list 集合 + * @param value 值 + * @param 泛型 + */ + public static void addNotNull(final int index, final List list, final T value) { + add(index, list, value, true); + } + + // = + + /** + * 添加集合数据到指定索引后 + * @param index 索引 + * @param list 集合 + * @param values 准备添加的值 ( 集合 ) + * @param 泛型 + */ + public static void addAll(final int index, final List list, final List values) { + addAll(index, list, values, false); + } + + /** + * 添加集合数据到指定索引后 + * @param index 索引 + * @param list 集合 + * @param values 准备添加的值 ( 集合 ) + * @param notNull 是否不允许添加 null 值 + * @param 泛型 + */ + public static void addAll(final int index, final List list, final List values, final boolean notNull) { + if (list != null && values != null) { + if (notNull) { + try { + List tempList = new ArrayList<>(); + for (T t : values) { + if (t != null) { + tempList.add(t); + } + } + // 添加到集合中 + list.addAll(index, tempList); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addAll notNull"); + } + } else { + try { + list.addAll(index, values); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addAll"); + } + } + } + } + + /** + * 添加集合数据到指定索引后 - values 内的值不允许为 null + * @param index 索引 + * @param list 集合 + * @param values 准备添加的值 ( 集合 ) + * @param 泛型 + */ + public static void addAllNotNull(final int index, final List list, final List values) { + addAll(index, list, values, true); + } + + // ============ + // = 删除数据 = + // ============ + + /** + * 移除一条数据 + * @param collection {@link Collection} + * @param value 准备删除的值 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean remove(final Collection collection, final T value) { + if (collection != null) { + try { + return collection.remove(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remove"); + } + } + return false; + } + + /** + * 移除一条数据 + * @param list 集合 + * @param pos 准备删除的索引 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static T remove(final List list, final int pos) { + if (list != null) { + // 防止索引为负数 + if (pos < 0) { + return null; + } + try { + return list.remove(pos); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remove"); + } + } + return null; + } + + /** + * 移除集合数据 + * @param collection {@link Collection} + * @param values 准备删除的集合 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean removeAll(final Collection collection, final Collection values) { + if (collection != null && values != null) { + try { + return collection.removeAll(values); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeAll"); + } + } + return false; + } + + // = + + /** + * 清空集合中符合指定 value 的全部数据 + * @param collection {@link Collection} + * @param value 准备对比移除的值 + * @param 泛型 + */ + public static void clear(final Collection collection, final T value) { + if (collection != null) { + try { + Iterator iterator = collection.iterator(); + while (iterator.hasNext()) { + T t = iterator.next(); + // 判断值是否一样 + if (equals(t, value)) { + iterator.remove(); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "clear"); + } + } + } + + /** + * 保留集合中符合指定 value 的全部数据 + * @param collection {@link Collection} + * @param value 准备对比保留的值 + * @param 泛型 + */ + public static void clearNotBelong(final Collection collection, final T value) { + if (collection != null) { + try { + Iterator iterator = collection.iterator(); + while (iterator.hasNext()) { + T t = iterator.next(); + // 判断值是否不一样 ( 保留不一样的 ) + if (!equals(t, value)) { + iterator.remove(); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "clearNotBelong"); + } + } + } + + /** + * 清空集合全部数据 + * @param collection {@link Collection} + * @param 泛型 + */ + public static void clearAll(final Collection collection) { + if (collection != null) { + try { + collection.clear(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "clearAll"); + } + } + } + + /** + * 清空集合中为 null 的值 + * @param collection {@link Collection} + * @param 泛型 + */ + public static void clearNull(final Collection collection) { + clear(collection, null); + } + + // ==================== + // = 判断集合是否相同 = + // ==================== + + /** + * 判断两个集合是否相同 + * @param collection1 第一个集合 + * @param collection2 第二个集合 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEqualCollection(final Collection collection1, final Collection collection2) { + // 数据长度 + int len = 0; + // 判断数据是否相同 + if (collection1 != null && collection2 != null && (len = collection1.size()) == collection2.size()) { + if (len == 0) return true; + + // 进行判断类型, 如果不同, 则直接跳过不处理 + if (!collection1.getClass().getName().equals(collection2.getClass().getName())) { + return false; + } + + // 如果集合相等, 直接跳过 + if (collection1 == collection2) { + return true; + } + + T[] arrays1, arrays2; + try { + // 转换数组, 防止异常 + arrays1 = (T[]) collection1.toArray(); + arrays2 = (T[]) collection2.toArray(); + } catch (Exception e) { + return false; + } + if (arrays1 == null || arrays2 == null) { + return false; + } + + for (int i = 0; i < len; i++) { + // 判断两个值是否一样 + boolean equals = equals(arrays1[i], arrays2[i]); + // 如果不一样, 直接 return + if (!equals) { + return false; + } + } + return true; + } + // 如果不符合条件, 防止两个集合都是为 null + return (collection1 == null && collection2 == null); + } + + /** + * 判断多个集合是否相同 + * @param collections 集合数组 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEqualCollections(final Collection... collections) { + if (collections != null && collections.length >= 2) { + // 获取长度 + int len = collections.length; + // 设置临时值为第一个 + Collection temp = collections[0]; + // 进行判断 + for (int i = 1; i < len; i++) { + // 判断是否一样 + boolean equalCollection = isEqualCollection(temp, collections[i]); + // 如果不一样, 直接返回 + if (!equalCollection) { + return false; + } + } + return true; + } + return false; + } + + // ============ + // = 集合处理 = + // ============ + + // ======== + // = 并集 = + // ======== + + /** + * 两个集合并集处理 + * @param collection1 第一个集合 + * @param collection2 第二个集合 + * @param 泛型 + * @return 并集集合 + */ + public static Collection union(final Collection collection1, final Collection collection2) { + if (collection1 != null && collection2 != null) { + try { + // 初始化新的集合, 默认保存第一个集合的数据 + Set sets = new LinkedHashSet<>(collection1); + sets.addAll(collection2); + // 返回集合 + return sets; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "union"); + } + return null; + } else if (collection1 != null) { + return collection1; + } + return collection2; + } + + /** + * 多个集合并集处理 + * @param collections 集合数组 + * @param 泛型 + * @return 并集集合 + */ + public static Collection unions(final Collection... collections) { + if (collections != null) { + int len = collections.length; + if (len >= 2) { + try { + // 保存第一个集合 + Set sets = new LinkedHashSet<>(); + // 防止集合为 null + if (collections[0] != null) { + sets.addAll(collections[0]); + } + // 进行循环处理 + for (int i = 1; i < len; i++) { + // 获取集合值 + Collection value = collections[i]; + if (value != null) { + sets.addAll(value); + } + } + return sets; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "unions"); + } + return null; + } + return collections[0]; + } + return null; + } + + // ======== + // = 交集 = + // ======== + + /** + * 两个集合交集处理 + * @param collection1 第一个集合 + * @param collection2 第二个集合 + * @param 泛型 + * @return 交集集合 + */ + public static Collection intersection(final Collection collection1, final Collection collection2) { + if (collection1 != null && collection2 != null) { + try { + // 专门用于返回中转的集合 + Set sets = new LinkedHashSet<>(); + + // 初始化新的集合, 默认保存第一个集合的数据 + Set setsTemp = new LinkedHashSet<>(collection1); + // 循环第二个集合 + Iterator iterator = collection2.iterator(); + while (iterator.hasNext()) { + T t = iterator.next(); + // 判断是否存在, 存在则保存 + if (setsTemp.contains(t)) { + sets.add(t); + } + } + // 返回集合 + return sets; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "intersection"); + } + return null; + } else if (collection1 != null) { + return collection1; + } + return collection2; + } + + /** + * 两个集合交集的补集处理 + * @param collection1 第一个集合 + * @param collection2 第二个集合 + * @param 泛型 + * @return 交集集合 + */ + public static Collection disjunction(final Collection collection1, final Collection collection2) { + try { + // 先进行并集处理 + Collection unionC = union(collection1, collection2); + // 在进行交集处理 + Collection intersectionC = intersection(collection1, collection2); + // 再进行移除处理 + if (unionC != null && intersectionC != null) { + try { + // 移除数据 + unionC.removeAll(intersectionC); + return unionC; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "disjunction"); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "disjunction"); + } + return null; + } + + /** + * 两个集合差集 ( 扣除 ) 处理 + * @param collection1 第一个集合 + * @param collection2 第二个集合 + * @param 泛型 + * @return 差集 ( 扣除 ) 集合 + */ + public static Collection subtract(final Collection collection1, final Collection collection2) { + try { + // 先进行交集处理 + Collection intersectionC = intersection(collection1, collection2); + // 保存到新的集合中 + Set sets = new LinkedHashSet<>(collection1); + // 进行移除 + sets.removeAll(intersectionC); + // 返回集合 + return sets; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subtract"); + } + return null; + } + + // ============ + // = 快捷判断 = + // ============ + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + // ============ + // = 转换处理 = + // ============ + + /** + * 转换数组 to Object + * @param collection {@link Collection} + * @param 泛型 + * @return 转换后的数组 + */ + public static Object[] toArray(final Collection collection) { + if (collection != null) { + try { + return collection.toArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toArray"); + } + } + return null; + } + + /** + * 集合翻转处理 + * @param collection {@link Collection} + * @param 泛型 + * @return 翻转后的集合 + */ + public static Collection reverse(final Collection collection) { + try { + // 返回集合 + List lists = new ArrayList<>(); + // 转换数据 + T[] arrays = (T[]) collection.toArray(); + // 循环处理 + for (int i = arrays.length - 1; i >= 0; i--) { + lists.add(arrays[i]); + } + // 保存新的数据 + return lists; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "reverse"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/ColorUtils.java b/lib/DevApp/src/main/java/dev/utils/common/ColorUtils.java new file mode 100644 index 0000000000..a05a7f7d64 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/ColorUtils.java @@ -0,0 +1,670 @@ +package dev.utils.common; + +import java.util.HashMap; +import java.util.Locale; + +import dev.utils.JCLogUtils; + +/** + * detail: 颜色工具类 ( 包括常用的色值 ) + * @author Ttt + */ +public final class ColorUtils { + + private ColorUtils() { + } + + // 日志 TAG + private static final String TAG = ColorUtils.class.getSimpleName(); + // 透明 + public static final int TRANSPARENT = 0x00000000; + // 白色 + public static final int WHITE = 0xffffffff; + // 白色 - 半透明 + public static final int WHITE_TRANSLUCENT = 0x80ffffff; + // 黑色 + public static final int BLACK = 0xff000000; + // 黑色 - 半透明 + public static final int BLACK_TRANSLUCENT = 0x80000000; + // 红色 + public static final int RED = 0xffff0000; + // 红色 - 半透明 + public static final int RED_TRANSLUCENT = 0x80ff0000; + // 绿色 + public static final int GREEN = 0xff00ff00; + // 绿色 - 半透明 + public static final int GREEN_TRANSLUCENT = 0x8000ff00; + // 蓝色 + public static final int BLUE = 0xff0000ff; + // 蓝色 - 半透明 + public static final int BLUE_TRANSLUCENT = 0x800000ff; + // 灰色 + public static final int GRAY = 0xff969696; + // 灰色 - 半透明 + public static final int GRAY_TRANSLUCENT = 0x80969696; + // 天蓝 + public static final int SKYBLUE = 0xff87ceeb; + // 橙色 + public static final int ORANGE = 0xffffa500; + // 金色 + public static final int GOLD = 0xffffd700; + // 粉色 + public static final int PINK = 0xffffc0cb; + // 紫红色 + public static final int FUCHSIA = 0xffff00ff; + // 灰白色 + public static final int GRAYWHITE = 0xfff2f2f2; + // 紫色 + public static final int PURPLE = 0xff800080; + // 青色 + public static final int CYAN = 0xff00ffff; + // 黄色 + public static final int YELLOW = 0xffffff00; + // 巧克力色 + public static final int CHOCOLATE = 0xffd2691e; + // 番茄色 + public static final int TOMATO = 0xffff6347; + // 橙红色 + public static final int ORANGERED = 0xffff4500; + // 银白色 + public static final int SILVER = 0xffc0c0c0; + // 深灰色 + public static final int DKGRAY = 0xFF444444; + // 亮灰色 + public static final int LTGRAY = 0xFFCCCCCC; + // 洋红色 + public static final int MAGENTA = 0xFFFF00FF; + // 高光 + public static final int HIGHLIGHT = 0x33ffffff; + // 低光 + public static final int LOWLIGHT = 0x33000000; + + /** + * 0-255 十进值转换成十六进制, 如 255 就是 ff + * 255 * 0.x = 十进制 转 十六进制 + *

+ * 透明度 0-100 + * 00、19、33、4C、66、7F、99、B2、CC、E5、FF + */ + + /** + * 获取十六进制透明度字符串 + * @param alpha 0-255 + * @return 透明度 ( 十六进制 ) 值 + */ + public static String toHexAlpha(final int alpha) { + try { + if (alpha >= 0 && alpha <= 255) { + return Integer.toHexString(alpha); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexAlpha"); + } + return null; + } + + // = + + /** + * 返回一个颜色中的透明度值 ( 返回十进制 ) + * @param color argb color + * @return alpha 值 + */ + public static int alpha(final int color) { + return color >>> 24; + } + + /** + * 返回一个颜色中的透明度百分比值 + * @param color argb color + * @return alpha 百分比值 + */ + public static float alphaPercent(final int color) { + return percentF(alpha(color), 255); + } + + // = + + /** + * 返回一个颜色中红色的色值 ( 返回十进制 ) + * @param color argb/rgb color + * @return red 值 + */ + public static int red(final int color) { + return (color >> 16) & 0xFF; + } + + /** + * 返回一个颜色中红色的百分比值 + * @param color argb/rgb color + * @return red 百分比值 + */ + public static float redPercent(final int color) { + return percentF(red(color), 255); + } + + // = + + /** + * 返回一个颜色中绿色的色值 ( 返回十进制 ) + * @param color argb/rgb color + * @return green 百分比值 + */ + public static int green(final int color) { + return (color >> 8) & 0xFF; + } + + /** + * 返回一个颜色中绿色的百分比值 + * @param color argb/rgb color + * @return green 百分比值 + */ + public static float greenPercent(final int color) { + return percentF(green(color), 255); + } + + // = + + /** + * 返回一个颜色中蓝色的色值 ( 返回十进制 ) + * @param color argb/rgb color + * @return blue 百分比值 + */ + public static int blue(final int color) { + return color & 0xFF; + } + + /** + * 返回一个颜色中蓝色的百分比值 + * @param color argb/rgb color + * @return blue 百分比值 + */ + public static float bluePercent(final int color) { + return percentF(blue(color), 255); + } + + // = + + /** + * 根据对应的 red、green、blue 生成一个颜色值 + * @param red 红色值 [0-255] + * @param green 绿色值 [0-255] + * @param blue 蓝色值 [0-255] + * @return rgb 颜色值 + */ + public static int rgb(final int red, final int green, final int blue) { + return 0xff000000 | (red << 16) | (green << 8) | blue; + } + + /** + * 根据对应的 red、green、blue 生成一个颜色值 + * @param red 红色值 [0-255] + * @param green 绿色值 [0-255] + * @param blue 蓝色值 [0-255] + * @return rgb 颜色值 + */ + public static int rgb(final float red, final float green, final float blue) { + return 0xff000000 | + ((int) (red * 255.0f + 0.5f) << 16) | + ((int) (green * 255.0f + 0.5f) << 8) | + (int) (blue * 255.0f + 0.5f); + } + + // = + + /** + * 根据对应的 alpha、red、green、blue 生成一个颜色值 ( 含透明度 ) + * @param alpha 透明度 [0-255] + * @param red 红色值 [0-255] + * @param green 绿色值 [0-255] + * @param blue 蓝色值 [0-255] + * @return argb 颜色值 + */ + public static int argb(final int alpha, final int red, final int green, final int blue) { + return (alpha << 24) | (red << 16) | (green << 8) | blue; + } + + /** + * 根据对应的 alpha、red、green、blue 生成一个颜色值 ( 含透明度 ) + * @param alpha 透明度 [0-255] + * @param red 红色值 [0-255] + * @param green 绿色值 [0-255] + * @param blue 蓝色值 [0-255] + * @return argb 颜色值 + */ + public static int argb(final float alpha, final float red, final float green, final float blue) { + return ((int) (alpha * 255.0f + 0.5f) << 24) | + ((int) (red * 255.0f + 0.5f) << 16) | + ((int) (green * 255.0f + 0.5f) << 8) | + (int) (blue * 255.0f + 0.5f); + } + + // = + + /** + * 判断颜色 RGB 是否有效 + * @param color rgb color + * @return {@code true} yes, {@code false} no + */ + public static boolean isRGB(final int color) { + int red = red(color); + int green = green(color); + int blue = blue(color); + return (red <= 255 && red >= 0) && + (green <= 255 && green >= 0) && + (blue <= 255 && blue >= 0); + } + + /** + * 判断颜色 ARGB 是否有效 + * @param color argb color + * @return {@code true} yes, {@code false} no + */ + public static boolean isARGB(final int color) { + int alpha = alpha(color); + int red = red(color); + int green = green(color); + int blue = blue(color); + return (alpha <= 255 && alpha >= 0) && + (red <= 255 && red >= 0) && + (green <= 255 && green >= 0) && + (blue <= 255 && blue >= 0); + } + + // = + + /** + * 设置透明度 + * @param color argb/rgb color + * @param alpha 透明度 [0-255] + * @return argb 颜色值 + */ + public static int setAlpha(final int color, final int alpha) { + return (color & 0x00ffffff) | (alpha << 24); + } + + /** + * 设置透明度 + * @param color argb/rgb color + * @param alpha 透明度 [0-255] + * @return argb 颜色值 + */ + public static int setAlpha(final int color, final float alpha) { + return (color & 0x00ffffff) | ((int) (alpha * 255.0f + 0.5f) << 24); + } + + /** + * 改变颜色值中的红色色值 + * @param color argb/rgb color + * @param red 红色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setRed(final int color, final int red) { + return (color & 0xff00ffff) | (red << 16); + } + + /** + * 改变颜色值中的红色色值 + * @param color argb/rgb color + * @param red 红色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setRed(final int color, final float red) { + return (color & 0xff00ffff) | ((int) (red * 255.0f + 0.5f) << 16); + } + + /** + * 改变颜色值中的绿色色值 + * @param color argb/rgb color + * @param green 绿色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setGreen(final int color, final int green) { + return (color & 0xffff00ff) | (green << 8); + } + + /** + * 改变颜色值中的绿色色值 + * @param color argb/rgb color + * @param green 绿色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setGreen(final int color, final float green) { + return (color & 0xffff00ff) | ((int) (green * 255.0f + 0.5f) << 8); + } + + /** + * 改变颜色值中的蓝色色值 + * @param color argb/rgb color + * @param blue 蓝色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setBlue(final int color, final int blue) { + return (color & 0xffffff00) | blue; + } + + /** + * 改变颜色值中的蓝色色值 + * @param color argb/rgb color + * @param blue 蓝色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setBlue(final int color, final float blue) { + return (color & 0xffffff00) | (int) (blue * 255.0f + 0.5f); + } + + // = + + /** + * 解析颜色字符串, 返回对应的颜色值 + * @param colorStr argb/rgb color String + * @return argb/rgb 颜色值 + */ + private static int priParseColor(final String colorStr) { + if (colorStr.charAt(0) == '#') { + // Use a long to avoid rollovers on #ffXXXXXX + long color = Long.parseLong(colorStr.substring(1), 16); + if (colorStr.length() == 7) { + // Set the alpha value + color |= 0x00000000ff000000; + } else if (colorStr.length() != 9) { + throw new IllegalArgumentException("Unknown color"); + } + return (int) color; + } else { + Integer color = sColorNameMaps.get(colorStr.toLowerCase(Locale.ROOT)); + if (color != null) { + return color; + } + } + throw new IllegalArgumentException("Unknown color"); + } + + /** + * 解析颜色字符串, 返回对应的颜色值 + *
+     *     支持的格式:
+     *     #RRGGBB
+     *     #AARRGGBB
+     *     'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta', 'yellow', 'lightgray', 'darkgray'
+     * 
+ * @param colorStr argb/rgb color String + * @return argb/rgb 颜色值 + */ + public static int parseColor(final String colorStr) { + if (colorStr != null) { + try { + return priParseColor(colorStr); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseColor"); + } + } + return -1; + } + + /** + * 颜色值 转换 RGB 颜色字符串 + * @param colorInt rgb int color + * @return rgb color String + */ + public static String intToRgbString(final int colorInt) { + int color = colorInt; + color = color & 0x00ffffff; + String colorStr = Integer.toHexString(color); + while (colorStr.length() < 6) { + colorStr = "0" + colorStr; + } + return "#" + colorStr; + } + + /** + * 颜色值 转换 ARGB 颜色字符串 + * @param colorInt argb int color + * @return argb color String + */ + public static String intToArgbString(final int colorInt) { + String colorString = Integer.toHexString(colorInt); + while (colorString.length() < 6) { + colorString = "0" + colorString; + } + while (colorString.length() < 8) { + colorString = "f" + colorString; + } + return "#" + colorString; + } + + // = + + /** + * 获取随机颜色值 + * @return 随机颜色值 + */ + public static int getRandomColor() { + return getRandomColor(true); + } + + /** + * 获取随机颜色值 + * @param supportAlpha 是否支持透明度 + * @return argb/rgb 颜色值 + */ + public static int getRandomColor(final boolean supportAlpha) { + int high = supportAlpha ? (int) (Math.random() * 0x100) << 24 : 0xFF000000; + return high | (int) (Math.random() * 0x1000000); + } + + /** + * 判断是否为 ARGB 格式的十六进制颜色, 例如: FF990587 + * @param colorStr color String + * @return {@code true} yes, {@code false} no + */ + public static boolean judgeColorString(final String colorStr) { + if (colorStr != null && colorStr.length() == 8) { + for (int i = 0; i < colorStr.length(); i++) { + char cc = colorStr.charAt(i); + return !(cc != '0' && cc != '1' && cc != '2' && cc != '3' && cc != '4' && cc != '5' && cc != '6' && cc != '7' && cc != '8' && cc != '9' + && cc != 'A' && cc != 'B' && cc != 'C' && cc != 'D' && cc != 'E' && cc != 'F' + && cc != 'a' && cc != 'b' && cc != 'c' && cc != 'd' && cc != 'e' && cc != 'f'); + } + } + return false; + } + + // = + + /** + * 颜色加深 ( 单独修改 RGB 值, 不变动透明度 ) + * @param colorStr color String + * @param darkValue 加深值 + * @return 加深后的颜色值 + */ + public static int setDark(final String colorStr, final int darkValue) { + int color = parseColor(colorStr); + if (color == -1) return -1; + return setDark(color, darkValue); + } + + /** + * 颜色加深 ( 单独修改 RGB 值, 不变动透明度 ) + * @param color int color + * @param darkValue 加深值 + * @return 加深后的颜色值 + */ + public static int setDark(final int color, final int darkValue) { + int red = red(color); + int green = green(color); + int blue = blue(color); + // 进行加深 ( 累减 ) + red -= darkValue; + green -= darkValue; + blue -= darkValue; + // 颜色值 + int colorTemp = color; + // 进行设置 + colorTemp = setRed(colorTemp, clamp(red, 255, 0)); + colorTemp = setGreen(colorTemp, clamp(green, 255, 0)); + colorTemp = setBlue(colorTemp, clamp(blue, 255, 0)); + return colorTemp; + } + + /** + * 颜色变浅, 变亮 ( 单独修改 RGB 值, 不变动透明度 ) + * @param colorStr color String + * @param lightValue 变亮 ( 变浅 ) 值 + * @return 变亮 ( 变浅 ) 后的颜色值 + */ + public static int setLight(final String colorStr, final int lightValue) { + int color = parseColor(colorStr); + if (color == -1) return -1; + return setLight(color, lightValue); + } + + /** + * 颜色变浅, 变亮 ( 单独修改 RGB 值, 不变动透明度 ) + * @param color int color + * @param lightValue 变亮 ( 变浅 ) 值 + * @return 变亮 ( 变浅 ) 后的颜色值 + */ + public static int setLight(final int color, final int lightValue) { + int red = red(color); + int green = green(color); + int blue = blue(color); + // 进行变浅, 变亮 ( 累加 ) + red += lightValue; + green += lightValue; + blue += lightValue; + // 颜色值 + int colorTemp = color; + // 进行设置 + colorTemp = setRed(colorTemp, clamp(red, 255, 0)); + colorTemp = setGreen(colorTemp, clamp(green, 255, 0)); + colorTemp = setBlue(colorTemp, clamp(blue, 255, 0)); + return colorTemp; + } + + /** + * 设置透明度加深 + * @param colorStr color String + * @param darkValue 加深值 + * @return 透明度加深后的颜色值 + */ + public static int setAlphaDark(final String colorStr, final int darkValue) { + int color = parseColor(colorStr); + if (color == -1) return -1; + return setAlphaDark(color, darkValue); + } + + /** + * 设置透明度加深 + * @param color int color + * @param darkValue 加深值 + * @return 透明度加深后的颜色值 + */ + public static int setAlphaDark(final int color, final int darkValue) { + int alpha = alpha(color); + // 透明度加深 + alpha += darkValue; + // 进行设置 + return setAlpha(color, clamp(alpha, 255, 0)); + } + + /** + * 设置透明度变浅 + * @param colorStr color String + * @param lightValue 变浅值 + * @return 透明度变浅后的颜色值 + */ + public static int setAlphaLight(final String colorStr, final int lightValue) { + int color = parseColor(colorStr); + if (color == -1) return -1; + return setAlphaLight(color, lightValue); + } + + /** + * 设置透明度变浅 + * @param color int color + * @param lightValue 变浅值 + * @return 透明度变浅后的颜色值 + */ + public static int setAlphaLight(final int color, final int lightValue) { + int alpha = alpha(color); + // 透明度变浅 + alpha -= lightValue; + // 进行设置 + return setAlpha(color, clamp(alpha, 255, 0)); + } + + // = + + // 颜色字典集合 + private static final HashMap sColorNameMaps; + + static { + sColorNameMaps = new HashMap<>(); + sColorNameMaps.put("transparent", TRANSPARENT); + sColorNameMaps.put("white", WHITE); + sColorNameMaps.put("black", BLACK); + sColorNameMaps.put("red", RED); + sColorNameMaps.put("green", GREEN); + sColorNameMaps.put("blue", BLUE); + sColorNameMaps.put("gray", GRAY); + sColorNameMaps.put("grey", GRAY); + sColorNameMaps.put("skyblue", SKYBLUE); + sColorNameMaps.put("orange", ORANGE); + sColorNameMaps.put("gold", GOLD); + sColorNameMaps.put("pink", PINK); + sColorNameMaps.put("fuchsia", FUCHSIA); + sColorNameMaps.put("graywhite", GRAYWHITE); + sColorNameMaps.put("purple", PURPLE); + sColorNameMaps.put("cyan", CYAN); + sColorNameMaps.put("yellow", YELLOW); + sColorNameMaps.put("chocolate", CHOCOLATE); + sColorNameMaps.put("tomato", TOMATO); + sColorNameMaps.put("orangered", ORANGERED); + sColorNameMaps.put("silver", SILVER); + sColorNameMaps.put("darkgray", DKGRAY); + sColorNameMaps.put("lightgray", LTGRAY); + sColorNameMaps.put("lightgrey", LTGRAY); + sColorNameMaps.put("magenta", MAGENTA); + sColorNameMaps.put("highlight", HIGHLIGHT); + sColorNameMaps.put("lowlight", LOWLIGHT); + sColorNameMaps.put("aqua", 0xFF00FFFF); + sColorNameMaps.put("lime", 0xFF00FF00); + sColorNameMaps.put("maroon", 0xFF800000); + sColorNameMaps.put("navy", 0xFF000080); + sColorNameMaps.put("olive", 0xFF808000); + sColorNameMaps.put("teal", 0xFF008080); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = NumberUtils = + // =============== + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + private static float percentF(final int value, final int max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return (float) value / (float) max; + } + + /** + * 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max + * @param value 指定值 + * @param max 最大值 + * @param min 最小值 + * @return 介于 max、min 之间的 value + */ + private static int clamp(final int value, final int max, final int min) { + return value > max ? max : value < min ? min : value; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/ConvertUtils.java b/lib/DevApp/src/main/java/dev/utils/common/ConvertUtils.java new file mode 100644 index 0000000000..01f58e85e4 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/ConvertUtils.java @@ -0,0 +1,1824 @@ +package dev.utils.common; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Arrays; + +import dev.utils.JCLogUtils; + +/** + * detail: 转换工具类 (Byte、Hex 等 ) + * @author Ttt + *
+ *     byte 是字节数据类型、有符号型的、占 1 个字节、大小范围为 [ -128 - 127]
+ *     当大于 127 时则开始缩进 127 = 127, 128 = -128, 129 = -127
+ *     char 是字符数据类型、无符号型的、占 2 个字节 (unicode 码 )、大小范围为 [0 - 65535]
+ *     

+ * Binary( 二进制 ) toBinaryString + * Oct( 八进制 ) + * Dec( 十进制 ) + * Hex( 十六进制 ) 以 0x 开始的数据表示十六进制 + *

+ * 位移加密: bytesBitwiseAND(byte[] bytes) + * @see
+ *
+ */ +public final class ConvertUtils { + + private ConvertUtils() { + } + + // 日志 TAG + private static final String TAG = ConvertUtils.class.getSimpleName(); + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + // 用于建立十六进制字符的输出的大写字符数组 + private static final char[] HEX_DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * Object 转换所需类型对象 + * @param object Object + * @param 泛型 + * @return Object convert T object + */ + public static T convert(final Object object) { + try { + return (T) object; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "convert"); + } + return null; + } + + // = + + /** + * char[] 转 String + * @param data char[] + * @return {@link String} + */ + public static String toString(final char[] data) { + return toString(data, null); + } + + /** + * byte[] 转 String + * @param data byte[] + * @return {@link String} + */ + public static String toString(final byte[] data) { + return toString(data, null); + } + + /** + * char[] 转 String + * @param data char[] + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + public static String toString(final char[] data, final String defaultStr) { + if (length(data) == 0) { + try { + return new String(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + } + return defaultStr; + } + + /** + * byte[] 转 String + * @param data byte[] + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + public static String toString(final byte[] data, final String defaultStr) { + if (data != null) { + try { + return new String(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + } + return defaultStr; + } + + // = + + /** + * char 转 String + * @param data char + * @return {@link String} + */ + public static String toString(final char data) { + return toString(data, null); + } + + /** + * byte 转 String + * @param data byte + * @return {@link String} + */ + public static String toString(final byte data) { + return toString(data, null); + } + + /** + * char 转 String + * @param data char + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + public static String toString(final char data, final String defaultStr) { + try { + return Character.toString(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + return defaultStr; + } + + /** + * byte 转 String + * @param data byte + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + public static String toString(final byte data, final String defaultStr) { + try { + return Byte.toString(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + return defaultStr; + } + + // = + + /** + * Object 转 String + * @param object Object + * @return {@link String} + */ + public static String toString(final Object object) { + return toString(object, null); + } + + /** + * Object 转 String + * @param object Object + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + public static String toString(final Object object, final String defaultStr) { + if (object != null) { + try { + if (object instanceof String) { + return (String) object; + } else { + Class clazz = object.getClass(); + // 判断是否数组类型 + if (clazz.isArray()) { + // = 基本数据类型 = + if (clazz.isAssignableFrom(int[].class)) { + return Arrays.toString((int[]) object); + } else if (clazz.isAssignableFrom(boolean[].class)) { + return Arrays.toString((boolean[]) object); + } else if (clazz.isAssignableFrom(long[].class)) { + return Arrays.toString((long[]) object); + } else if (clazz.isAssignableFrom(double[].class)) { + return Arrays.toString((double[]) object); + } else if (clazz.isAssignableFrom(float[].class)) { + return Arrays.toString((float[]) object); + } else if (clazz.isAssignableFrom(byte[].class)) { + return Arrays.toString((byte[]) object); + } else if (clazz.isAssignableFrom(char[].class)) { + return Arrays.toString((char[]) object); + } else if (clazz.isAssignableFrom(short[].class)) { + return Arrays.toString((short[]) object); + } + // = 基本类型封装 = + if (clazz.isAssignableFrom(Integer[].class)) { + return Arrays.toString((Integer[]) object); + } else if (clazz.isAssignableFrom(Boolean[].class)) { + return Arrays.toString((Boolean[]) object); + } else if (clazz.isAssignableFrom(Long[].class)) { + return Arrays.toString((Long[]) object); + } else if (clazz.isAssignableFrom(Double[].class)) { + return Arrays.toString((Double[]) object); + } else if (clazz.isAssignableFrom(Float[].class)) { + return Arrays.toString((Float[]) object); + } else if (clazz.isAssignableFrom(Byte[].class)) { + return Arrays.toString((Byte[]) object); + } else if (clazz.isAssignableFrom(Character[].class)) { + return Arrays.toString((Character[]) object); + } else if (clazz.isAssignableFrom(Short[].class)) { + return Arrays.toString((Short[]) object); + } + } + return object.toString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + } + return defaultStr; + } + + // = + + /** + * 字符串 转 int + * @param str String + * @return int + */ + public static int toInt(final String str) { + return toInt(str, 0); + } + + /** + * 字符串 转 int + * @param str String + * @param defaultValue 默认值 + * @return int 如果转换失败, 则返回 defaultValue + */ + public static int toInt(final String str, final int defaultValue) { + if (str == null) return defaultValue; + try { + return Integer.parseInt(str); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toInt"); + } + return defaultValue; + } + + /** + * 字符串 转 boolean + * @param str String + * @return boolean + */ + public static boolean toBoolean(final String str) { + return toBoolean(str, false); + } + + /** + * 字符串 转 boolean + * @param str String + * @param defaultValue 默认值 + * @return boolean 如果转换失败, 则返回 defaultValue + */ + public static boolean toBoolean(final String str, final boolean defaultValue) { + if (str == null) return defaultValue; + try { + // 判断是否 0 + return str.equalsIgnoreCase("true") || str.equalsIgnoreCase("1"); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toBoolean"); + } + return defaultValue; + } + + /** + * 字符串 转 float + * @param str String + * @return float + */ + public static float toFloat(final String str) { + return toFloat(str, 0f); + } + + /** + * 字符串 转 float + * @param str String + * @param defaultValue 默认值 + * @return float 如果转换失败, 则返回 defaultValue + */ + public static float toFloat(final String str, final float defaultValue) { + if (str == null) return defaultValue; + try { + return Float.parseFloat(str); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toFloat"); + } + return defaultValue; + } + + /** + * 字符串 转 double + * @param str String + * @return double + */ + public static double toDouble(final String str) { + return toDouble(str, 0d); + } + + /** + * 字符串 转 double + * @param str String + * @param defaultValue 默认值 + * @return double 如果转换失败, 则返回 defaultValue + */ + public static double toDouble(final String str, final double defaultValue) { + if (str == null) return defaultValue; + try { + return Double.parseDouble(str); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toDouble"); + } + return defaultValue; + } + + /** + * 字符串 转 long + * @param str String + * @return long + */ + public static long toLong(final String str) { + return toLong(str, 0L); + } + + /** + * 字符串 转 long + * @param str String + * @param defaultValue 默认值 + * @return long 如果转换失败, 则返回 defaultValue + */ + public static long toLong(final String str, final long defaultValue) { + if (str == null) return defaultValue; + try { + return Long.parseLong(str); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toLong"); + } + return defaultValue; + } + + // ============ + // = 转换对象 = + // ============ + + /** + * 基本类型对象 转 int + * @param value Integer + * @return int + */ + public static int toInt(final Integer value) { + return toInt(value, 0); + } + + /** + * 基本类型对象 转 int + * @param value Integer + * @param defaultValue 默认值 + * @return int 如果转换失败, 则返回 defaultValue + */ + public static int toInt(final Integer value, final int defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toInt"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 boolean + * @param value Boolean + * @return boolean 如果转换失败, 则返回 defaultValue + */ + public static boolean toBoolean(final Boolean value) { + return toBoolean(value, false); + } + + /** + * 基本类型对象 转 boolean + * @param value Boolean + * @param defaultValue 默认值 + * @return boolean 如果转换失败, 则返回 defaultValue + */ + public static boolean toBoolean(final Boolean value, final boolean defaultValue) { + if (value == null) return defaultValue; + return value; + } + + /** + * 基本类型对象 转 float + * @param value Float + * @return float 如果转换失败, 则返回 defaultValue + */ + public static float toFloat(final Float value) { + return toFloat(value, 0f); + } + + /** + * 基本类型对象 转 float + * @param value Float + * @param defaultValue 默认值 + * @return float 如果转换失败, 则返回 defaultValue + */ + public static float toFloat(final Float value, final float defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toFloat"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 double + * @param value Double + * @return double 如果转换失败, 则返回 defaultValue + */ + public static double toDouble(final Double value) { + return toDouble(value, 0d); + } + + /** + * 基本类型对象 转 double + * @param value Double + * @param defaultValue 默认值 + * @return double 如果转换失败, 则返回 defaultValue + */ + public static double toDouble(final Double value, final double defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toDouble"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 long + * @param value Long + * @return long 如果转换失败, 则返回 defaultValue + */ + public static long toLong(final Long value) { + return toLong(value, 0L); + } + + /** + * 基本类型对象 转 long + * @param value Long + * @param defaultValue 默认值 + * @return long 如果转换失败, 则返回 defaultValue + */ + public static long toLong(final Long value, final long defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toLong"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 short + * @param value Short + * @return short 如果转换失败, 则返回 defaultValue + */ + public static short toShort(final Short value) { + return toShort(value, (short) 0); + } + + /** + * 基本类型对象 转 short + * @param value Short + * @param defaultValue 默认值 + * @return short 如果转换失败, 则返回 defaultValue + */ + public static short toShort(final Short value, final short defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toShort"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 char + * @param value Character + * @return char 如果转换失败, 则返回 defaultValue + */ + public static char toChar(final Character value) { + return toChar(value, (char) 0); + } + + /** + * 基本类型对象 转 char + * @param value Character + * @param defaultValue 默认值 + * @return char 如果转换失败, 则返回 defaultValue + */ + public static char toChar(final Character value, final char defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toChar"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 byte + * @param value Byte + * @return byte 如果转换失败, 则返回 defaultValue + */ + public static byte toByte(final Byte value) { + return toByte(value, (byte) 0); + } + + /** + * 基本类型对象 转 byte + * @param value Byte + * @param defaultValue 默认值 + * @return byte 如果转换失败, 则返回 defaultValue + */ + public static byte toByte(final Byte value, final byte defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toByte"); + } + return defaultValue; + } + + // ======== + // = 其他 = + // ======== + + /** + * 字符串 获取 char ( 默认第一位 ) + * @param str String + * @param defaultValue 默认值 + * @return 第一位值, 如果获取失败则返回 defaultValue + */ + public static char toChar(final String str, final char defaultValue) { + return toChar(str, 0, defaultValue); + } + + /** + * 字符串 获取 char + * @param str String + * @param pos 索引 + * @param defaultValue 默认值 + * @return 指定索引的值, 如果获取失败则返回 defaultValue + */ + public static char toChar(final String str, final int pos, final char defaultValue) { + if (str == null || pos < 0) return defaultValue; + try { + return str.charAt(pos); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toChar"); + } + return defaultValue; + } + + // = + + /** + * char 转换 unicode 编码 + * @param value char + * @return int + */ + public static int toCharInt(final char value) { + return (int) value; + } + + /** + * 字符串 获取 char[] + * @param str String + * @return char[] + */ + public static char[] toChars(final String str) { + if (str != null) { + try { + return str.toCharArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toChars"); + } + } + return null; + } + + /** + * 字符串 获取 byte[] + * @param str String + * @return byte[] + */ + public static byte[] toBytes(final String str) { + if (str != null) { + try { + return str.getBytes(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toBytes"); + } + } + return null; + } + + // = + + /** + * 字符串转换对应的进制 + *
+     *     如: parseInt("1f603", 16) = 128515
+     * 
+ * @param str String + * @param radix 进制 + * @return 对应进制的值 + */ + public static int parseInt(final String str, final int radix) { + if (str == null) return -1; + try { + return Integer.parseInt(str, radix); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseInt"); + } + return -1; + } + + /** + * 字符串转换对应的进制 + * @param str String + * @param radix 进制 + * @return 对应进制的值 + */ + public static long parseLong(final String str, final int radix) { + if (str == null) return -1; + try { + return Long.parseLong(str, radix); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseLong"); + } + return -1; + } + + // = + + /** + * 将 short 转换成字节数组 + * @param data short + * @return byte[] + */ + public static byte[] valueOf(final short data) { + try { + byte[] bytes = new byte[2]; + for (int i = 0; i < 2; i++) { + int offset = (bytes.length - 1 - i) * 8; + bytes[i] = (byte) ((data >>> offset) & 0xff); + } + return bytes; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "valueOf"); + } + return null; + } + + /** + * 将 int 转换成字节数组 + * @param data int + * @return byte[] + */ + public static byte[] valueOf(final int data) { + try { + byte[] bytes = new byte[4]; + for (int i = 0; i < 4; i++) { + int offset = (bytes.length - 1 - i) * 8; + bytes[i] = (byte) ((data >>> offset) & 0xFF); + } + return bytes; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "valueOf"); + } + return null; + } + + // = + + /** + * byte[] 转为 Object + * @param bytes byte[] + * @return {@link Object} + */ + public static Object bytesToObject(final byte[] bytes) { + if (bytes == null) return null; + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); + return ois.readObject(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToObject"); + } finally { + if (ois != null) { + try { + ois.close(); + } catch (Exception e) { + } + } + } + return null; + } + + /** + * Object 转为 byte[] + * @param object Object + * @return byte[] + */ + public static byte[] objectToBytes(final Object object) { + if (object == null) return null; + ObjectOutputStream oos = null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(baos); + oos.writeObject(object); + return baos.toByteArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "objectToBytes"); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (Exception e) { + } + } + } + return null; + } + + // = + + /** + * byte[] 转换 char[], 并且进行补码 + * @param data byte[] + * @return char[] + */ + public static char[] bytesToChars(final byte[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + try { + char[] chars = new char[len]; + for (int i = 0; i < len; i++) { + chars[i] = (char) (data[i] & 0xff); + } + return chars; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToChars"); + } + return null; + } + + /** + * char[] 转换 byte[] + * @param data char[] + * @return byte[] + */ + public static byte[] charsToBytes(final char[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + try { + byte[] bytes = new byte[len]; + for (int i = 0; i < len; i++) { + bytes[i] = (byte) (data[i]); + } + return bytes; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "charsToBytes"); + } + return null; + } + + // ============================================== + // = (int、double、long、float)[] 转换 String[] = + // ============================================== + + /** + * int[] 转换 string[] + * @param datas int[] + * @return String[] + */ + public static String[] intsToStrings(final int[] datas) { + return intsToStrings(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 string[] + * @param off 起始值 + * @param datas int[] + * @return String[] + */ + public static String[] intsToStrings(final int off, final int[] datas) { + return intsToStrings(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 string[] + * @param off 起始值 + * @param length 所需长度 + * @param datas int[] + * @return String[] + */ + public static String[] intsToStrings(final int off, final int length, final int[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + String[] strings = new String[length - off]; + for (int i = 0, len = strings.length; i < len; i++) { + strings[i] = datas[off + i] + ""; + } + return strings; + } + + // = + + /** + * double[] 转换 string[] + * @param datas double[] + * @return String[] + */ + public static String[] doublesToStrings(final double[] datas) { + return doublesToStrings(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * double[] 转换 string[] + * @param off 起始值 + * @param datas double[] + * @return String[] + */ + public static String[] doublesToStrings(final int off, final double[] datas) { + return doublesToStrings(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * double[] 转换 string[] + * @param off 起始值 + * @param length 所需长度 + * @param datas double[] + * @return String[] + */ + public static String[] doublesToStrings(final int off, final int length, final double[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + String[] strings = new String[length - off]; + for (int i = 0, len = strings.length; i < len; i++) { + strings[i] = datas[off + i] + ""; + } + return strings; + } + + // = + + /** + * long[] 转换 string[] + * @param datas long[] + * @return String[] + */ + public static String[] longsToStrings(final long[] datas) { + return longsToStrings(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * long[] 转换 string[] + * @param off 起始值 + * @param datas long[] + * @return String[] + */ + public static String[] longsToStrings(final int off, final long[] datas) { + return longsToStrings(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * long[] 转换 string[] + * @param off 起始值 + * @param length 所需长度 + * @param datas long[] + * @return String[] + */ + public static String[] longsToStrings(final int off, final int length, final long[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + String[] strings = new String[length - off]; + for (int i = 0, len = strings.length; i < len; i++) { + strings[i] = datas[off + i] + ""; + } + return strings; + } + + // = + + /** + * float[] 转换 string[] + * @param datas float[] + * @return String[] + */ + public static String[] floatsToStrings(final float[] datas) { + return floatsToStrings(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * float[] 转换 string[] + * @param off 起始值 + * @param datas float[] + * @return String[] + */ + public static String[] floatsToStrings(final int off, final float[] datas) { + return floatsToStrings(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * float[] 转换 string[] + * @param off 起始值 + * @param length 所需长度 + * @param datas float[] + * @return String[] + */ + public static String[] floatsToStrings(final int off, final int length, final float[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + String[] strings = new String[length - off]; + for (int i = 0, len = strings.length; i < len; i++) { + strings[i] = datas[off + i] + ""; + } + return strings; + } + + // ====================================== + // = int[] 转换 (double、long、float)[] = + // ====================================== + + /** + * int[] 转换 double[] + * @param datas int[] + * @return double[] + */ + public static double[] intsToDoubles(final int[] datas) { + return intsToDoubles(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 double[] + * @param off 起始值 + * @param datas int[] + * @return double[] + */ + public static double[] intsToDoubles(final int off, final int[] datas) { + return intsToDoubles(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 double[] + * @param off 起始值 + * @param length 所需长度 + * @param datas int[] + * @return double[] + */ + public static double[] intsToDoubles(final int off, final int length, final int[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + double[] doubles = new double[length - off]; + for (int i = 0, len = doubles.length; i < len; i++) { + doubles[i] = datas[off + i]; + } + return doubles; + } + + // = + + /** + * int[] 转换 long[] + * @param datas int[] + * @return long[] + */ + public static long[] intsToLongs(final int[] datas) { + return intsToLongs(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 long[] + * @param off 起始值 + * @param datas int[] + * @return long[] + */ + public static long[] intsToLongs(final int off, final int[] datas) { + return intsToLongs(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 long[] + * @param off 起始值 + * @param length 所需长度 + * @param datas int[] + * @return long[] + */ + public static long[] intsToLongs(final int off, final int length, final int[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + long[] longs = new long[length - off]; + for (int i = 0, len = longs.length; i < len; i++) { + longs[i] = datas[off + i]; + } + return longs; + } + + // = + + /** + * int[] 转换 float[] + * @param datas int[] + * @return float[] + */ + public static float[] intsToFloats(final int[] datas) { + return intsToFloats(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 float[] + * @param off 起始值 + * @param datas int[] + * @return float[] + */ + public static float[] intsToFloats(final int off, final int[] datas) { + return intsToFloats(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 float[] + * @param off 起始值 + * @param length 所需长度 + * @param datas int[] + * @return float[] + */ + public static float[] intsToFloats(final int off, final int length, final int[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + float[] floats = new float[length - off]; + for (int i = 0, len = floats.length; i < len; i++) { + floats[i] = datas[off + i]; + } + return floats; + } + + // ============================================== + // = String[] 转换 (int、double、long、float)[] = + // ============================================== + + /** + * string[] 转换 int[] + * @param datas String[] + * @return int[] + */ + public static int[] stringsToInts(final String... datas) { + return stringsToInts(0, (datas != null) ? datas.length : 0, -1, datas); + } + + /** + * string[] 转换 int[] + * @param off 起始值 + * @param datas String[] + * @return int[] + */ + public static int[] stringsToInts(final int off, final String... datas) { + return stringsToInts(off, (datas != null) ? datas.length : 0, -1, datas); + } + + /** + * string[] 转换 int[] + * @param off 起始值 + * @param length 所需长度 + * @param datas String[] + * @return int[] + */ + public static int[] stringsToInts(final int off, final int length, final String... datas) { + return stringsToInts(off, length, -1, datas); + } + + /** + * string[] 转换 int[] + * @param off 起始值 + * @param length 所需长度 + * @param errorValue 转换错误使用值 + * @param datas String[] + * @return int[] + */ + public static int[] stringsToInts(final int off, final int length, final int errorValue, final String... datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + int[] ints = new int[length - off]; + for (int i = 0, len = ints.length; i < len; i++) { + try { + ints[i] = Integer.parseInt(datas[off + i]); + } catch (Exception e) { + ints[i] = errorValue; + } + } + return ints; + } + + // = + + /** + * string[] 转换 double[] + * @param datas String[] + * @return double[] + */ + public static double[] stringsToDoubles(final String... datas) { + return stringsToDoubles(0, (datas != null) ? datas.length : 0, -1d, datas); + } + + /** + * string[] 转换 double[] + * @param off 起始值 + * @param datas String[] + * @return double[] + */ + public static double[] stringsToDoubles(final int off, final String... datas) { + return stringsToDoubles(off, (datas != null) ? datas.length : 0, -1d, datas); + } + + /** + * string[] 转换 double[] + * @param off 起始值 + * @param length 所需长度 + * @param datas String[] + * @return double[] + */ + public static double[] stringsToDoubles(final int off, final int length, final String... datas) { + return stringsToDoubles(off, length, -1d, datas); + } + + /** + * string[] 转换 double[] + * @param off 起始值 + * @param length 所需长度 + * @param errorValue 转换错误使用值 + * @param datas String[] + * @return double[] + */ + public static double[] stringsToDoubles(final int off, final int length, final double errorValue, final String... datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + double[] doubles = new double[length - off]; + for (int i = 0, len = doubles.length; i < len; i++) { + try { + doubles[i] = Double.parseDouble(datas[off + i]); + } catch (Exception e) { + doubles[i] = errorValue; + } + } + return doubles; + } + + // = + + /** + * string[] 转换 long[] + * @param datas String[] + * @return long[] + */ + public static long[] stringsToLongs(final String... datas) { + return stringsToLongs(0, (datas != null) ? datas.length : 0, -1L, datas); + } + + /** + * string[] 转换 long[] + * @param off 起始值 + * @param datas String[] + * @return long[] + */ + public static long[] stringsToLongs(final int off, final String... datas) { + return stringsToLongs(off, (datas != null) ? datas.length : 0, -1L, datas); + } + + /** + * string[] 转换 long[] + * @param off 起始值 + * @param length 所需长度 + * @param datas String[] + * @return long[] + */ + public static long[] stringsToLongs(final int off, final int length, final String... datas) { + return stringsToLongs(off, length, -1L, datas); + } + + /** + * string[] 转换 long[] + * @param off 起始值 + * @param length 所需长度 + * @param errorValue 转换错误使用值 + * @param datas String[] + * @return long[] + */ + public static long[] stringsToLongs(final int off, final int length, final long errorValue, final String... datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + long[] longs = new long[length - off]; + for (int i = 0, len = longs.length; i < len; i++) { + try { + longs[i] = Long.parseLong(datas[off + i]); + } catch (Exception e) { + longs[i] = errorValue; + } + } + return longs; + } + + // = + + /** + * string[] 转换 float[] + * @param datas String[] + * @return float[] + */ + public static float[] stringsToFloats(final String... datas) { + return stringsToFloats(0, (datas != null) ? datas.length : 0, -1f, datas); + } + + /** + * string[] 转换 float[] + * @param off 起始值 + * @param datas String[] + * @return float[] + */ + public static float[] stringsToFloats(final int off, final String... datas) { + return stringsToFloats(off, (datas != null) ? datas.length : 0, -1f, datas); + } + + /** + * string[] 转换 float[] + * @param off 起始值 + * @param length 所需长度 + * @param datas String[] + * @return float[] + */ + public static float[] stringsToFloats(final int off, final int length, final String... datas) { + return stringsToFloats(off, length, -1f, datas); + } + + /** + * string[] 转换 float[] + * @param off 起始值 + * @param length 所需长度 + * @param errorValue 转换错误使用值 + * @param datas String[] + * @return float[] + */ + public static float[] stringsToFloats(final int off, final int length, final float errorValue, final String... datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + float[] floats = new float[length - off]; + for (int i = 0, len = floats.length; i < len; i++) { + try { + floats[i] = Float.parseFloat(datas[off + i]); + } catch (Exception e) { + floats[i] = errorValue; + } + } + return floats; + } + + // ====================================== + // = (double、long、float)[] 转换 int[] = + // ====================================== + + /** + * double[] 转换 int[] + * @param datas double[] + * @return int[] + */ + public static int[] doublesToInts(final double[] datas) { + return doublesToInts(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * double[] 转换 int[] + * @param off 起始值 + * @param datas double[] + * @return int[] + */ + public static int[] doublesToInts(final int off, final double[] datas) { + return doublesToInts(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * double[] 转换 int[] + * @param off 起始值 + * @param length 所需长度 + * @param datas double[] + * @return int[] + */ + public static int[] doublesToInts(final int off, final int length, final double[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + int[] ints = new int[length - off]; + for (int i = 0, len = ints.length; i < len; i++) { + try { + ints[i] = (int) datas[off + i]; + } catch (Exception e) { + } + } + return ints; + } + + // = + + /** + * long[] 转换 int[] + * @param datas long[] + * @return int[] + */ + public static int[] longsToInts(final long[] datas) { + return longsToInts(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * long[] 转换 int[] + * @param off 起始值 + * @param datas long[] + * @return int[] + */ + public static int[] longsToInts(final int off, final long[] datas) { + return longsToInts(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * long[] 转换 int[] + * @param off 起始值 + * @param length 所需长度 + * @param datas long[] + * @return int[] + */ + public static int[] longsToInts(final int off, final int length, final long[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + int[] ints = new int[length - off]; + for (int i = 0, len = ints.length; i < len; i++) { + try { + ints[i] = (int) datas[off + i]; + } catch (Exception e) { + } + } + return ints; + } + + // = + + /** + * float[] 转换 int[] + * @param datas float[] + * @return int[] + */ + public static int[] floatsToInts(final float[] datas) { + return floatsToInts(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * float[] 转换 int[] + * @param off 起始值 + * @param datas float[] + * @return int[] + */ + public static int[] floatsToInts(final int off, final float[] datas) { + return floatsToInts(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * float[] 转换 int[] + * @param off 起始值 + * @param length 所需长度 + * @param datas float[] + * @return int[] + */ + public static int[] floatsToInts(final int off, final int length, final float[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + int[] ints = new int[length - off]; + for (int i = 0, len = ints.length; i < len; i++) { + try { + ints[i] = (int) datas[off + i]; + } catch (Exception e) { + } + } + return ints; + } + + // =================== + // = Binary - 二进制 = + // =================== + + /** + * 将 字节转换 为 二进制字符串 + * @param datas byte[] + * @return 二进制字符串 + */ + public static String toBinaryString(final byte... datas) { + if (datas == null || datas.length == 0) return null; + try { + StringBuilder builder = new StringBuilder(); + for (byte value : datas) { + for (int j = 7; j >= 0; --j) { + builder.append(((value >> j) & 0x01) == 0 ? '0' : '1'); + } + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToBits"); + } + return null; + } + + /** + * 二进制字符串 转换 byte[] 解码 + *
+     *     例: "011000010111001101100100" 传入 decodeBinary, 返回 byte[], 通过 new String(byte()) 获取配合 toBinaryString 使用
+     * 
+ * @param str String + * @return 解码后的 byte[] + */ + public static byte[] decodeBinary(final String str) { + if (str == null) return null; + try { + String data = str; + int lenMod = data.length() % 8; + int byteLen = data.length() / 8; + // add "0" until length to 8 times + if (lenMod != 0) { + for (int i = lenMod; i < 8; i++) { + data = "0" + data; + } + byteLen++; + } + byte[] bytes = new byte[byteLen]; + for (int i = 0; i < byteLen; ++i) { + for (int j = 0; j < 8; ++j) { + bytes[i] <<= 1; + bytes[i] |= data.charAt(i * 8 + j) - '0'; + } + } + return bytes; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bitsToBytes"); + } + return null; + } + + // ====================== + // = Hex - 十六进制处理 = + // ====================== + + /** + * 判断是否十六进制数据 + * @param data 待检验数据 + * @return {@code true} yes, {@code false} no + */ + public static boolean isHex(final String data) { + if (data == null) return false; + // 获取数据长度 + int len = data.length(); + if (len > 0) { + for (int i = len - 1; i >= 0; i--) { + char c = data.charAt(i); + if (!(c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f')) { + return false; + } + } + return true; + } + return false; + } + + /** + * 将十六进制字节数组解码 + * @param data 十六进制 byte[] + * @return 十六进制转 ( 解 ) 码后的数据 + */ + public static byte[] decodeHex(final byte[] data) { + return decodeHex((length(data) == 0) ? null : bytesToChars(data)); + } + + /** + * 将十六进制字符串解码 + * @param str 十六进制 String + * @return 十六进制转 ( 解 ) 码后的数据 + */ + public static byte[] decodeHex(final String str) { + return decodeHex(isEmpty(str) ? null : str.toCharArray()); + } + + /** + * 将十六进制字符数组解码 + * @param data 十六进制 char[] + * @return 十六进制转 ( 解 ) 码后的数据 + */ + public static byte[] decodeHex(final char[] data) { + if (data == null) return null; + try { + int len = data.length; + byte[] out = new byte[len >> 1]; + // 十六进制由两个字符组成 + for (int i = 0, j = 0; j < len; i++) { + int d = toDigit(data[j], j) << 4; + j++; + d = d | toDigit(data[j], j); + j++; + out[i] = (byte) (d & 0xFF); + } + return out; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "decodeHex"); + } + return null; + } + + /** + * 十六进制 char 转换 int + * @param hexChar 十六进制 char + * @return 十六进制转 ( 解 ) 码后的整数 + */ + public static int hexToInt(final char hexChar) { + if (hexChar >= '0' && hexChar <= '9') { + return hexChar - '0'; + } else if (hexChar >= 'A' && hexChar <= 'F') { + return hexChar - 'A' + 10; + } else { + throw new IllegalArgumentException(); + } + } + + /** + * 将十六进制字符转换成一个整数 + * @param ch 十六进制 char + * @param index 十六进制字符在字符数组中的位置 + * @return 一个整数 + * @throws Exception 当 ch 不是一个合法的十六进制字符时, 抛出运行时异常 + */ + private static int toDigit(final char ch, final int index) throws Exception { + int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new Exception(String.format("Illegal hexadecimal character %s at index %s", ch, index)); + } + return digit; + } + + // = + + // toHexString(0x1f603) = 1f603 + // parseInt("1f603", 16) = 128515 + // toHexString(128515) = 1f603 + + /** + * int 转换十六进制 + *
+     *     如: toHexString(0x1f603) 返回: 1f603
+     * 
+ * @param value int + * @return 十六进制字符串 + */ + public static String toHexString(final int value) { + try { + return Integer.toHexString(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + /** + * long 转换十六进制 + * @param value long + * @return 十六进制字符串 + */ + public static String toHexString(final long value) { + try { + return Long.toHexString(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + /** + * double 转换十六进制 + * @param value double + * @return 十六进制字符串 + */ + public static String toHexString(final double value) { + try { + return Double.toHexString(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + /** + * float 转换十六进制 + * @param value float + * @return 十六进制字符串 + */ + public static String toHexString(final float value) { + try { + return Float.toHexString(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // = + + /** + * 将 string 转换为 十六进制 char[] + * @param str String + * @return 十六进制 char[] + */ + public static char[] toHexChars(final String str) { + return toHexChars(str, true); + } + + /** + * 将 string 转换为 十六进制 char[] + * @param str String + * @param toLowerCase {@code true} 小写格式, {@code false} 大写格式 + * @return 十六进制 char[] + */ + public static char[] toHexChars(final String str, final boolean toLowerCase) { + return toHexChars(isEmpty(str) ? null : str.getBytes(), toLowerCase ? HEX_DIGITS : HEX_DIGITS_UPPER); + } + + // = + + /** + * 将 byte[] 转换为 十六进制 char[] + * @param data byte[] + * @return 十六进制 char[] + */ + public static char[] toHexChars(final byte[] data) { + return toHexChars(data, true); + } + + /** + * 将 byte[] 转换为 十六进制 char[] + * @param data byte[] + * @param toLowerCase {@code true} 小写格式, {@code false} 大写格式 + * @return 十六进制 char[] + */ + public static char[] toHexChars(final byte[] data, final boolean toLowerCase) { + return toHexChars(data, toLowerCase ? HEX_DIGITS : HEX_DIGITS_UPPER); + } + + /** + * 将 byte[] 转换为 十六进制 char[] + * @param data byte[] + * @param hexDigits {@link #HEX_DIGITS}、{@link #HEX_DIGITS_UPPER} + * @return 十六进制 char[] + */ + private static char[] toHexChars(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + return toHexString(data, hexDigits).toCharArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexChars"); + } + return null; + } + + // = + + /** + * 将 string 转换 十六进制字符串 + * @param str 待转换数据 + * @return 十六进制字符串 + */ + public static String toHexString(final String str) { + return toHexString(str, true); + } + + /** + * 将 string 转换 十六进制字符串 + * @param str 待转换数据 + * @param toLowerCase {@code true} 小写格式, {@code false} 大写格式 + * @return 十六进制字符串 + */ + public static String toHexString(final String str, final boolean toLowerCase) { + return toHexString(isEmpty(str) ? null : str.getBytes(), toLowerCase ? HEX_DIGITS : HEX_DIGITS_UPPER); + } + + // = + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制字符串 + */ + public static String toHexString(final byte[] data) { + return toHexString(data, true); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param toLowerCase {@code true} 小写格式, {@code false} 大写格式 + * @return 十六进制字符串 + */ + public static String toHexString(final byte[] data, final boolean toLowerCase) { + return toHexString(data, toLowerCase ? HEX_DIGITS : HEX_DIGITS_UPPER); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS}、{@link #HEX_DIGITS_UPPER} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // = + +// String data = "test"; +// // 转换二进制字符串 +// String result = toBinaryString(data.getBytes()); +// // 获取二进制数据 +// byte[] bytes = result.getBytes(); +// // 位移编码 +// bytesBitwiseAND(bytes); +// // = +// // 位移解码 +// bytesBitwiseAND(bytes); +// // 二进制数据解码 +// byte[] byteResult = decodeBinary(new String(bytes)); +// // 转换为原始数据 +// String data1 = new String(byteResult); +// // 判断是否一致 +// boolean equals = data.equals(data1); + + /** + * 按位求补 byte[] 位移编解码 ( 共用同一个方法 ) + * @param data byte[] + */ + public static void bytesBitwiseAND(final byte[] data) { + if (data == null) return; + for (int i = 0, len = data.length; i < len; i++) { + int d = data[i]; + d = ~d; // 按位补运算符, 翻转操作数的每一位, 即 0 变成 1, 1 变成 0, 再通过反转后的二进制初始化回十六进制 + data[i] = (byte) d; + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + // ============== + // = ArrayUtils = + // ============== + + /** + * 获取数组长度 + * @param objects object[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + private static int length(final Object... objects) { + return objects != null ? objects.length : 0; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/CoordinateUtils.java b/lib/DevApp/src/main/java/dev/utils/common/CoordinateUtils.java similarity index 56% rename from DevLibUtils/src/main/java/dev/utils/common/CoordinateUtils.java rename to lib/DevApp/src/main/java/dev/utils/common/CoordinateUtils.java index a8bf0ce0c9..93b22d6e80 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/CoordinateUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/common/CoordinateUtils.java @@ -3,25 +3,41 @@ import static java.lang.Math.PI; /** - * detail: 坐标相关工具类 - * @author Blankj + * detail: 坐标 (GPS 纠偏 ) 相关工具类 + * @author Ttt + *
+ *     地球坐标系 (WGS-84)
+ *     火星坐标系 (GCJ-02)
+ *     百度坐标系 (BD09)
+ *     

+ * @see
+ *

+ * 1. WGS84 坐标系: 即地球坐标系, 国际上通用的坐标系, 设备一般包含 GPS 芯片或者北斗芯片获取的经纬度为 WGS84 地理坐标系 + * 谷歌地图采用的是 WGS84 地理坐标系 ( 中国范围除外 ) GPS 设备得到的经纬度就是在 WGS84 坐标系下的经纬度, 通常通过底层接口得到的定位信息都是 WGS84 坐标系 + *

+ * 2. GCJ02 坐标系: 即火星坐标系, 是由中国国家测绘局制订的地理信息系统的坐标系统, 由 WGS84 坐标系经加密后的坐标系 + * 国家规定, 中国大陆所有公开地理数据都需要至少用 GCJ-02 进行加密, 也就是说我们从国内公司的产品中得到的数据, 一定是经过了加密的 + * 绝大部分国内互联网地图提供商都是使用 GCJ-02 坐标系, 包括高德地图, 谷歌地图中国区等 + *

+ * 3. BD09 坐标系: 即百度坐标系, 其在 GCJ-02 上多增加了一次变换, 用来保护用户隐私, 从百度产品中得到的坐标都是 BD-09 坐标系 + *
*/ public final class CoordinateUtils { - private CoordinateUtils(){ + private CoordinateUtils() { } - private final static double X_PI = 3.14159265358979324 * 3000.0 / 180.0; - private final static double A = 6378245.0; - private final static double EE = 0.00669342162296594323; + private static final double X_PI = 3.14159265358979324 * 3000.0 / 180.0; + private static final double A = 6378245.0; + private static final double EE = 0.00669342162296594323; /** * BD09 坐标转 GCJ02 坐标 * @param lng BD09 坐标纬度 * @param lat BD09 坐标经度 - * @return GCJ02 坐标:[经度,纬度] + * @return GCJ02 坐标 [ 经度, 纬度 ] */ - public static double[] bd09ToGcj02(double lng, double lat) { + public static double[] bd09ToGcj02(final double lng, final double lat) { double x = lng - 0.0065; double y = lat - 0.006; double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI); @@ -35,9 +51,9 @@ public static double[] bd09ToGcj02(double lng, double lat) { * GCJ02 坐标转 BD09 坐标 * @param lng GCJ02 坐标经度 * @param lat GCJ02 坐标纬度 - * @return BD09 坐标:[经度,纬度] + * @return BD09 坐标 [ 经度, 纬度 ] */ - public static double[] gcj02ToBd09(double lng, double lat) { + public static double[] gcj02ToBd09(final double lng, final double lat) { double z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * X_PI); double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * X_PI); double bd_lng = z * Math.cos(theta) + 0.0065; @@ -49,12 +65,10 @@ public static double[] gcj02ToBd09(double lng, double lat) { * GCJ02 坐标转 WGS84 坐标 * @param lng GCJ02 坐标经度 * @param lat GCJ02 坐标纬度 - * @return WGS84 坐标:[经度,纬度] + * @return WGS84 坐标 [ 经度, 纬度 ] */ - public static double[] gcj02ToWGS84(double lng, double lat) { - if (outOfChina(lng, lat)) { - return new double[]{lng, lat}; - } + public static double[] gcj02ToWGS84(final double lng, final double lat) { + if (outOfChina(lng, lat)) return new double[]{lng, lat}; double dlat = transformLat(lng - 105.0, lat - 35.0); double dlng = transformLng(lng - 105.0, lat - 35.0); double radlat = lat / 180.0 * PI; @@ -72,12 +86,10 @@ public static double[] gcj02ToWGS84(double lng, double lat) { * WGS84 坐标转 GCJ02 坐标 * @param lng WGS84 坐标经度 * @param lat WGS84 坐标纬度 - * @return GCJ02 坐标:[经度,纬度] + * @return GCJ02 坐标 [ 经度, 纬度 ] */ - public static double[] wgs84ToGcj02(double lng, double lat) { - if (outOfChina(lng, lat)) { - return new double[]{lng, lat}; - } + public static double[] wgs84ToGcj02(final double lng, final double lat) { + if (outOfChina(lng, lat)) return new double[]{lng, lat}; double dlat = transformLat(lng - 105.0, lat - 35.0); double dlng = transformLng(lng - 105.0, lat - 35.0); double radlat = lat / 180.0 * PI; @@ -95,9 +107,9 @@ public static double[] wgs84ToGcj02(double lng, double lat) { * BD09 坐标转 WGS84 坐标 * @param lng BD09 坐标经度 * @param lat BD09 坐标纬度 - * @return WGS84 坐标:[经度,纬度] + * @return WGS84 坐标 [ 经度, 纬度 ] */ - public static double[] bd09ToWGS84(double lng, double lat) { + public static double[] bd09ToWGS84(final double lng, final double lat) { double[] gcj = bd09ToGcj02(lng, lat); return gcj02ToWGS84(gcj[0], gcj[1]); } @@ -106,14 +118,20 @@ public static double[] bd09ToWGS84(double lng, double lat) { * WGS84 坐标转 BD09 坐标 * @param lng WGS84 坐标经度 * @param lat WGS84 坐标纬度 - * @return BD09 坐标:[经度,纬度] + * @return BD09 坐标 [ 经度, 纬度 ] */ - public static double[] wgs84ToBd09(double lng, double lat) { + public static double[] wgs84ToBd09(final double lng, final double lat) { double[] gcj = wgs84ToGcj02(lng, lat); return gcj02ToBd09(gcj[0], gcj[1]); } - private static double transformLat(double lng, double lat) { + /** + * 转换经度 + * @param lng 经度 + * @param lat 纬度 + * @return 转换后的经度 + */ + private static double transformLat(final double lng, final double lat) { double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0; @@ -121,7 +139,13 @@ private static double transformLat(double lng, double lat) { return ret; } - private static double transformLng(double lng, double lat) { + /** + * 转换纬度 + * @param lng 经度 + * @param lat 纬度 + * @return 转换后的纬度 + */ + private static double transformLng(final double lng, final double lat) { double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0; @@ -129,7 +153,13 @@ private static double transformLng(double lng, double lat) { return ret; } - private static boolean outOfChina(double lng, double lat) { + /** + * 判断是否中国境外 + * @param lng 经度 + * @param lat 纬度 + * @return {@code true} yes, {@code false} no + */ + public static boolean outOfChina(final double lng, final double lat) { return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271; } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/DateUtils.java b/lib/DevApp/src/main/java/dev/utils/common/DateUtils.java new file mode 100644 index 0000000000..9350b4d0fe --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/DateUtils.java @@ -0,0 +1,1133 @@ +package dev.utils.common; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 日期工具类 + * @author Ttt + */ +public final class DateUtils { + + private DateUtils() { + } + + // 日志 TAG + private static final String TAG = DateUtils.class.getSimpleName(); + + // ================ + // = 日期格式类型 = + // ================ + + public static final String yyyy = "yyyy"; + public static final String yyyyMMdd = "yyyy-MM-dd"; + public static final String yyyyMMdd2 = "yyyyMMdd"; + public static final String yyyyMMdd3 = "yyyy年MM月dd日"; + public static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; + public static final String yyyyMMddHHmmss_2 = "yyyy年M月d日 HH:mm:ss"; + public static final String MMdd = "MM-dd"; + public static final String MMdd2 = "MM月dd日"; + public static final String MMdd3 = "MMdd"; + public static final String HHmm = "HH:mm"; + public static final String HHmm2 = "HHmm"; + public static final String HHmmss = "HH:mm:ss"; + public static final String HHmmss2 = "HHmmss"; + public static final String hhmmMMDDyyyy = "hh:mm M月d日 yyyy"; + public static final String hhmmssMMDDyyyy = "hh:mm:ss M月d日 yyyy"; + + // 一分钟 60 秒 + public static final int MINUTE_S = 60; + // 一小时 60 * 60 秒 + public static final int HOUR_S = 3600; + // 一天 24 * 60 * 60 秒 + public static final int DAY_S = 86400; + + // 秒与毫秒的倍数 + public static final long SEC = 1000; + // 分与毫秒的倍数 + public static final long MIN = SEC * 60; + // 时与毫秒的倍数 + public static final long HOUR = MIN * 60; + // 天与毫秒的倍数 + public static final long DAY = HOUR * 24; + // 周与毫秒的倍数 + public static final long WEEK = DAY * 7; + // 月与毫秒的倍数 + public static final long MONTH = DAY * 30; + // 年与毫秒的倍数 + public static final long YEAR = DAY * 365; + + /** + * 获取当前日期的字符串 - yyyy-MM-dd HH:mm:ss + * @return 当前日期 yyyy-MM-dd HH:mm:ss 格式字符串 + */ + public static String getDateNow() { + return getDateNow(yyyyMMddHHmmss); + } + + /** + * 获取当前日期的字符串 + * @param format 日期格式, 如: yyyy-MM-dd HH:mm:ss + * @return 当前日期指定格式字符串 + */ + public static String getDateNow(final String format) { + if (format == null) return null; + try { + return new SimpleDateFormat(format).format(Calendar.getInstance().getTime()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDateNow"); + } + return null; + } + + // = + + /** + * 将时间戳转换日期字符串 + * @param time 时间戳 + * @param format 日期格式 + * @return 按照指定格式的日期字符串 + */ + public static String formatTime(final long time, final String format) { + if (format == null) return null; + try { + return new SimpleDateFormat(format).format(time); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "formatTime"); + } + return null; + } + + /** + * 将 Date 转换日期字符串 + * @param date 日期 + * @param format 日期格式 + * @return 按照指定格式的日期字符串 + */ + public static String formatDate(final Date date, final String format) { + if (date == null || format == null) return null; + try { + return new SimpleDateFormat(format).format(date); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "formatDate"); + } + return null; + } + + // = + + /** + * 将时间戳转换成 Date + * @param time 时间戳 + * @return {@link Date} + */ + public static Date parseDate(final long time) { + try { + return new Date(time); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseDate"); + } + return null; + } + + /** + * 将日期字符串转换为 Date - 默认表示 time 属于 yyyy-MM-dd HH:mm:ss 格式 + * @param time 时间 + * @return {@link Date} + */ + public static Date parseDate(final String time) { + return parseDate(time, yyyyMMddHHmmss); + } + + /** + * 将日期字符串转换为 Date + * @param time 时间 + * @param format 日期格式 + * @return {@link Date} + */ + public static Date parseDate(final String time, final String format) { + if (time == null || format == null) return null; + try { + return new SimpleDateFormat(format).parse(time); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseDate"); + } + return null; + } + + // = + + /** + * 解析时间字符串转换为 long 毫秒 - 默认表示 time 属于 yyyy-MM-dd HH:mm:ss 格式 + * @param time 时间 + * @return 毫秒时间 + */ + public static long parseLong(final String time) { + return parseLong(time, yyyyMMddHHmmss); + } + + /** + * 解析时间字符串转换为 long 毫秒 + * @param time 时间 + * @param format 时间的格式 + * @return 毫秒时间 + */ + public static long parseLong(final String time, final String format) { + if (time == null || format == null) return 0L; + try { + // 按规定的时间格式, 进行格式化时间, 并且获取 long 时间毫秒, 返回毫秒时间 + return new SimpleDateFormat(format).parse(time).getTime(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseLong"); + } + return 0L; + } + + /** + * 转换时间为指定字符串 + * @param time 需要转换的时间 + * @param timeFormat time 的时间格式 + * @param format 把 time 转换成需要的格式 + * @return 转换指定格式的时间字符串 + */ + public static String parseToString(final String time, final String timeFormat, final String format) { + if (time != null && timeFormat != null && format != null) { + try { + Date date = parseDate(time, timeFormat); + // 把时间转为所需格式字符串 + return formatDate(date, format); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseToString"); + } + } + return null; + } + + // = + + /** + * 获取时间差 - 分钟 + * @param time 毫秒 + * @return 分钟 + */ + public static int getTimeDiffMinute(final long time) { + return (int) (time / 60000); // 60 * 1000 + } + + /** + * 获取时间差 - 小时 + * @param time 毫秒 + * @return 小时 + */ + public static int getTimeDiffHour(final long time) { + return (int) (time / 3600000); // 60 * 1000 * 60 + } + + /** + * 获取时间差 - 天 + * @param time 毫秒 + * @return 天 + */ + public static int getTimeDiffDay(final long time) { + return (int) (time / 86400000); // 60 * 1000 * 60 * 24 + } + + /** + * 获取时间差 - ( 传入时间 - 当前时间 ) + * @param time 毫秒 + * @return 与当前时间的时间差 ( 毫秒 ) + */ + public static long getTimeDiff(final long time) { + return time - System.currentTimeMillis(); + } + + /** + * 获取时间差 + * @param time1 时间 yyyy-MM-dd HH:mm:ss 格式 + * @param time2 对比时间 yyyy-MM-dd HH:mm:ss 格式 + * @return 时间差 ( 毫秒 ) + */ + public static long getTimeDiff(final String time1, final String time2) { + long timeLong1 = parseLong(time1); + long timeLong2 = parseLong(time2); + if (timeLong1 > 1L && timeLong2 > 1L) { + return timeLong1 - timeLong2; + } + return -2L; + } + + /** + * 获取时间差 + * @param time1 时间 + * @param timeFormat1 时间格式 + * @param time2 对比时间 + * @param timeFormat2 对比时间格式 + * @return 时间差 ( 毫秒 ) + */ + public static long getTimeDiff(final String time1, final String timeFormat1, final String time2, final String timeFormat2) { + long timeLong1 = parseLong(time1, timeFormat1); + long timeLong2 = parseLong(time2, timeFormat2); + if (timeLong1 > 1L && timeLong2 > 1L) { + return timeLong1 - timeLong2; + } + return -2L; + } + + // ============ + // = 获取时间 = + // ============ + + /** + * 获取年 + * @param date 日期 + * @return 年 + */ + public static int getYear(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.YEAR); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getYear"); + } + return -1; + } + + /** + * 获取月 (0 - 11) + 1 + * @param date 日期 + * @return 月 + */ + public static int getMonth(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.MONTH) + 1; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getMonth"); + } + return -1; + } + + /** + * 获取日 + * @param date 日期 + * @return 日 + */ + public static int getDay(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.DAY_OF_MONTH); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDay"); + } + return -1; + } + + /** + * 获取日期是星期几 + * @param date 日期 + * @return 日 + */ + public static int getWeek(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int week = calendar.get(Calendar.DAY_OF_WEEK); + return week; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getWeek"); + } + return -1; + } + + /** + * 获取时 - 24 + * @param date 日期 + * @return 时 + */ + public static int get24Hour(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.HOUR_OF_DAY); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get24Hour"); + } + return -1; + } + + /** + * 获取时 - 12 + * @param date 日期 + * @return 时 + */ + public static int get12Hour(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.HOUR); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get12Hour"); + } + return -1; + } + + /** + * 获取分 + * @param date 日期 + * @return 分 + */ + public static int getMinute(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.MINUTE); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getMinute"); + } + return -1; + } + + /** + * 获取秒 + * @param date 日期 + * @return 秒 + */ + public static int getSecond(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.SECOND); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getSecond"); + } + return -1; + } + + /** + * 转换时间处理, 小于 10, 则自动补充 0x + * @param time 待处理时间 + * @return 自动补 0 时间字符串 + */ + public static String convertTime(final int time) { + return convertTime(time, true); + } + + /** + * 转换时间处理, 小于 10, 则自动补充 0x + * @param time 待处理时间 + * @param append 判断是否需要自动补 0 + * @return 自动补 0 时间字符串 + */ + public static String convertTime(final int time, final boolean append) { + if (append) { + int timeTmpe = time; + // 防止出现负数 + timeTmpe = Math.max(0, timeTmpe); + return timeTmpe >= 10 ? timeTmpe + "" : "0" + timeTmpe; + } + return time + ""; + } + + // = + + /** + * 获取年 + * @return 年 + */ + public static int getYear() { + return Calendar.getInstance().get(Calendar.YEAR); + } + + /** + * 获取月 (0 - 11) + 1 + * @return 月 + */ + public static int getMonth() { + int month = Calendar.getInstance().get(Calendar.MONTH) + 1; + return month; + } + + /** + * 获取日 + * @return 日 + */ + public static int getDay() { + return Calendar.getInstance().get(Calendar.DATE); + } + + /** + * 获取星期数 + * @return 星期 + */ + public static int getWeek() { + return Calendar.getInstance().get(Calendar.DAY_OF_WEEK); + } + + /** + * 获取时 - 24 + * @return 24 小时制小时 + */ + public static int get24Hour() { + return Calendar.getInstance().get(Calendar.HOUR_OF_DAY); + } + + /** + * 获取时 - 12 + * @return 12 小时制小时 + */ + public static int get12Hour() { + return Calendar.getInstance().get(Calendar.HOUR); + } + + /** + * 获取分 + * @return 分钟 + */ + public static int getMinute() { + return Calendar.getInstance().get(Calendar.MINUTE); + } + + /** + * 获取秒 + * @return 秒数 + */ + public static int getSecond() { + return Calendar.getInstance().get(Calendar.SECOND); + } + + // = + + /** + * 判断是否闰年 + * @param year 年数 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLeapYear(final int year) { + // 判断是否闰年 + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; + } + + /** + * 根据年份、月份, 获取对应的天数 ( 完整天数, 无判断是否属于未来日期 ) + * @param year 年数 + * @param month 月份 + * @return 指定年份所属的月份的天数 + */ + public static int getMonthDayNumberAll(final int year, final int month) { + int number = 31; + // 判断返回的标识数字 + switch (month) { + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + number = 31; + break; + case 2: + if (isLeapYear(year)) { + number = 29; + } else { + number = 28; + } + break; + case 4: + case 6: + case 9: + case 11: + number = 30; + break; + } + return number; + } + + /** + * 根据年份, 获取对应的月份 + * @param year 年份 + * @return 内部判断是否相同一年, 不能超过限制未来的月份 + */ + public static int getYearMonthNumber(final int year) { + // 如: 当前 2019-01, 传入 2019 则返回 1 + // 传入 2018 返回 12, 不会返回未来的月份 + if (year == getYear()) { + return getMonth(); + } + return 12; + } + + /** + * 根据年份、月份, 获取对应的天数 + * @param year 年份 + * @param month 月份 + * @return 内部判断是否相同一年、月份, 不能超过限制未来的天数 + */ + public static int getMonthDayNumber(final int year, final int month) { + // 判断年份, 相同则判断月份 + if (year == getYear()) { + // 判断月份, 先同则返回天数 + if (getYearMonthNumber(year) == month) { + return getDay(); + } + } + return getMonthDayNumberAll(year, month); + } + + // = + + /** + * 生成 HH 按时间排序数组 + * @return 按小时排序的数组 + */ + public static String[] getArrayToHH() { + List lists = getListToHH(); + return lists.toArray(new String[lists.size()]); + } + + /** + * 生成 HH 按时间排序集合 + * @return 按小时排序的集合 + */ + public static List getListToHH() { + List lists = new ArrayList<>(); + for (int i = 0; i < 24; i++) { + lists.add(convertTime(i, true)); + } + return lists; + } + + /** + * 生成 MM 按时间排序数组 + * @return 按分钟排序的数组 + */ + public static String[] getArrayToMM() { + List lists = getListToMM(); + return lists.toArray(new String[lists.size()]); + } + + /** + * 生成 MM 按时间排序集合 + * @return 按分钟排序的集合 + */ + public static List getListToMM() { + List lists = new ArrayList<>(); + for (int i = 0; i < 60; i++) { + lists.add(convertTime(i, true)); + } + return lists; + } + + /** + * 生成 HH:mm 按间隔时间排序数组 + * @param type 0 = 00:00 - 23:00 = 每 1 小时间隔 + * 1 = 00:00 - 23:45 = 每 15 分钟间隔 + * 2 = 00:00 - 23:30 = 每 30 分钟间隔 + * @return 指定格式的数组 + */ + public static String[] getArrayToHHMM(final int type) { + List lists = getListToHHMM(type); + return lists.toArray(new String[lists.size()]); + } + + /** + * 生成 HH:mm 按间隔时间排序集合 + * @param type 0 = 00:00 - 23:00 = 每 1 小时间隔 + * 1 = 00:00 - 23:45 = 每 15 分钟间隔 + * 2 = 00:00 - 23:30 = 每 30 分钟间隔 + * @return 指定格式的集合 + */ + public static List getListToHHMM(final int type) { + List lists = new ArrayList<>(); + switch (type) { + case 0: + for (int i = 0; i < 24; i++) { + lists.add(convertTime(i, true) + ":00"); + } + break; + case 1: + for (int i = 0; i < 96; i++) { // 00 15 30 45 = 4 (24 * 4) + if (i % 2 == 0) { // 判断是否偶数 00、30 + // 小时数 + String hour = convertTime(i / 4, true); + // 分钟数 + String minute = i % 4 == 0 ? "00" : "30"; + // 累加时间 + lists.add(hour + ":" + minute); + } else { // 15、45 + // 小时数 + String hour = convertTime(i / 4, true); + // 分钟数 + String minute = (i - 1) % 4 == 0 ? "15" : "45"; + // 累加时间 + lists.add(hour + ":" + minute); + } + } + break; + case 2: + for (int i = 0; i < 48; i++) { // 00 30 = 2 (24 * 2) + // 小时处理 + int hour = i / 2; + // 属于偶数 + if (i % 2 == 0) { + lists.add(convertTime(hour, true) + ":00"); + } else { + lists.add(convertTime(hour, true) + ":30"); + } + } + break; + } + return lists; + } + + /** + * 获取 HH:mm 按间隔时间排序的集合中, 指定时间所在索引 + * @param time HH:mm 格式 + * @param type 0 = 00:00 - 23:00 = 每 1 小时间隔 + * 1 = 00:00 - 23:45 = 每 15 分钟间隔 + * 2 = 00:00 - 23:30 = 每 30 分钟间隔 + * @return 指定数据, 在对应格式类型内的索引 + */ + public static int getListToHHMMPosition(final String time, final int type) { + if (time != null && time.length() != 0) { + // 进行拆分 + String[] timeSplit = time.split(":"); + if (timeSplit != null && timeSplit.length == 2) { + // 转换小时 + int hour = toInt(timeSplit[0], -1); + // 判断是否小于 0 + if (hour < 0) { + return -1; + } else if (hour > 24) { + return -1; + } + + // 判断格式, 进行格式处理 + switch (type) { + case 0: + return hour; + case 1: + case 2: + // 转换分钟 + int minute = toInt(timeSplit[1], -1); + // 判断是否小于 0 + if (minute < 0) { + return -1; + } else if (minute > 59) { + return -1; + } + // 判断间隔 + if (type == 1) { + if (minute >= 0 && minute < 15) { + return hour * 4; + } else if (minute >= 15 && minute < 30) { + return hour * 4 + 1; + } else if (minute >= 30 && minute < 45) { + return hour * 4 + 2; + } else if (minute >= 30 && minute < 60) { + return hour * 4 + 3; + } + } else if (type == 2) { // 30 分钟一个间隔 + // 大于等于 30, 表示属于基数 + if (minute >= 30) { // 属于奇数 (30), 需要加 1 + return hour * 2 + 1; + } else { + return hour * 2; + } + } + break; + } + } + } + return -1; + } + + // = + + /** + * 传入时间, 获取时间 (00:00:00 格式 ) - 不处理大于一天 + * @param time 时间 ( 秒为单位 ) + * @return 转换 (00:00:00 格式 ) 时间字符串 + */ + public static String secToTimeRetain(final int time) { + return secToTimeRetain(time, false); + } + + /** + * 传入时间, 获取时间 (00:00:00 格式 ) + * @param time 时间 ( 秒为单位 ) + * @param isHandlerMDay 是否处理大于一天的时间 + * @return 转换 (00:00:00 格式 ) 时间字符串 + */ + public static String secToTimeRetain(final int time, final boolean isHandlerMDay) { + try { + if (time <= 0) { + return "00:00:00"; + } else { + // 取模 + int rSecond; + int rMinute; + // 差数 + int dSecond; + int dMinute; + int dHour; + // 转换时间格式 + if (time < MINUTE_S) { // 小于一分钟 + return "00:00:" + ((time >= 10) ? time : ("0" + time)); + } else if (time >= MINUTE_S && time < HOUR_S) { // 小于一小时 + dSecond = time % MINUTE_S; // 取模分钟, 获取多出的秒数 + dMinute = (time - dSecond) / MINUTE_S; + return "00:" + ((dMinute >= 10) ? dMinute : ("0" + dMinute)) + ":" + ((dSecond >= 10) ? dSecond : ("0" + dSecond)); + } else if (time >= HOUR_S && time < DAY_S) { // 小于等于一天 + rMinute = time % HOUR_S; // 取模小时, 获取多出的分钟 + dHour = (time - rMinute) / HOUR_S; // 获取小时 + dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 + dMinute = dSecond / MINUTE_S; // 获取多出的分钟 + rSecond = dSecond % MINUTE_S; // 取模分钟, 获取多余的秒数 + return ((dHour >= 10) ? dHour : ("0" + dHour)) + ":" + ((dMinute >= 10) ? dMinute : ("0" + dMinute)) + ":" + ((rSecond >= 10) ? rSecond : "0" + rSecond); + } else { // 多余的时间, 直接格式化 + // 大于一天的情况 + if (isHandlerMDay) { + rMinute = time % HOUR_S; // 取模小时, 获取多出的分钟 + dHour = (time - rMinute) / HOUR_S; // 获取小时 + dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 + dMinute = dSecond / MINUTE_S; // 获取多出的分钟 + rSecond = dSecond % MINUTE_S; // 取模分钟, 获取多余的秒数 + return ((dHour >= 10) ? dHour : ("0" + dHour)) + ":" + ((dMinute >= 10) ? dMinute : ("0" + dMinute)) + ":" + ((rSecond >= 10) ? rSecond : "0" + rSecond); + } + } + } + } catch (Exception e) { + } + return null; + } + + /** + * 传入时间, 时间参数 ( 小时、分钟、秒 ) + * @param time 时间 ( 秒为单位 ) + * @return int[] { 小时、分钟、秒 } + */ + public static int[] convertTimeArys(final int time) { + try { + if (time <= 0) { + return new int[]{0, 0, 0}; + } else { + // 取模 + int rSecond; + int rMinute; + // 差数 + int dSecond; + int dMinute; + int dHour; + // 转换时间格式 + if (time < MINUTE_S) { // 小于一分钟 + return new int[]{0, 0, time}; + } else if (time >= MINUTE_S && time < HOUR_S) { // 小于一小时 + dSecond = time % MINUTE_S; // 取模分钟, 获取多出的秒数 + dMinute = (time - dSecond) / MINUTE_S; + return new int[]{0, dMinute, dSecond}; + } else if (time >= HOUR_S && time < DAY_S) { // 小于等于一天 + rMinute = time % HOUR_S; // 取模小时, 获取多出的分钟 + dHour = (time - rMinute) / HOUR_S; // 获取小时 + dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 + dMinute = dSecond / MINUTE_S; // 获取多出的分钟 + rSecond = dSecond % MINUTE_S; // 取模分钟, 获取多余的秒数 + return new int[]{dHour, dMinute, rSecond}; + } else { // 多余的时间, 直接格式化 + // 大于一天的情况 + rMinute = time % HOUR_S; // 取模小时, 获取多出的分钟 + dHour = (time - rMinute) / HOUR_S; // 获取小时 + dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 + dMinute = dSecond / MINUTE_S; // 获取多出的分钟 + rSecond = dSecond % MINUTE_S; // 取模分钟, 获取多余的秒数 + return new int[]{dHour, dMinute, rSecond}; + } + } + } catch (Exception e) { + } + return null; + } + + /** + * 转换时间 + * @param millis 时间毫秒 + * @param precision precision = 0, return null + * precision = 1, return 天 + * precision = 2, return 天, 小时 + * precision = 3, return 天, 小时, 分钟 + * precision = 4, return 天, 小时, 分钟, 秒 + * precision = 5, return 天, 小时, 分钟, 秒, 毫秒 + * @return 转换指定格式的时间字符串 + */ + public static String millisToFitTimeSpan(final long millis, final int precision) { + if (millis <= 0 || precision <= 0) return null; + + long millisTime = millis; + int precisionFormat = precision; + + StringBuilder builder = new StringBuilder(); + String[] units = {"天", "小时", "分钟", "秒", "毫秒"}; + int[] unitLen = {86400000, 3600000, 60000, 1000, 1}; + precisionFormat = Math.min(precisionFormat, 5); + for (int i = 0; i < precisionFormat; i++) { + if (millisTime >= unitLen[i]) { + long mode = millisTime / unitLen[i]; + millisTime -= mode * unitLen[i]; + builder.append(mode).append(units[i]); + } + } + return builder.toString(); + } + + /** + * 转换时间为数组 + * @param millis 时间毫秒 + * @return int[5] { 天, 小时, 分钟, 秒, 毫秒 } + */ + public static int[] millisToTimeArys(final long millis) { + if (millis <= 0) return null; + int[] timeArys = new int[5]; + int[] unitLen = {86400000, 3600000, 60000, 1000, 1}; + long millisTime = millis; + for (int i = 0; i < 5; i++) { + if (millisTime >= unitLen[i]) { + long mode = millisTime / unitLen[i]; + millisTime -= mode * unitLen[i]; + timeArys[i] = (int) mode; + } + } + return timeArys; + } + + // ====================== + // = 判断是否在区间范围 = + // ====================== + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTimeHHmm(final String startTime, final String endTime) { + return isInTime(formatTime(System.currentTimeMillis(), HHmm), startTime, endTime, HHmm); + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTimeHHmm(final String nowTime, final String startTime, final String endTime) { + return isInTime(nowTime, startTime, endTime, HHmm); + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTimeHHmmss(final String startTime, final String endTime) { + return isInTime(formatTime(System.currentTimeMillis(), HHmmss), startTime, endTime, HHmmss); + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTimeHHmmss(final String nowTime, final String startTime, final String endTime) { + return isInTime(nowTime, startTime, endTime, HHmmss); + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param format 时间格式 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTime(final String nowTime, final String startTime, final String endTime, final String format) { + if (nowTime == null || startTime == null || endTime == null || format == null) return false; + try { + // 格式化日期 + SimpleDateFormat sdf = new SimpleDateFormat(format); + // 当前时间转换 + long now = sdf.parse(nowTime).getTime(); + // 开始时间转换 + long start = sdf.parse(startTime).getTime(); + // 结束时间转换 + long end = sdf.parse(endTime).getTime(); + // 判断结束时间是否小于开始时间 + if (end < start) { // 结束属于第二天区域 + if (now >= start || now <= end) { + return true; + } + } else { + if (now >= start && now <= end) { + return true; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isInTime"); + } + return false; + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTime(final long nowTime, final long startTime, final long endTime) { + return isInDate(new Date(nowTime), new Date(startTime), new Date(endTime)); + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInDate(final Date nowTime, final Date startTime, final Date endTime) { + if (nowTime == null || startTime == null || endTime == null) { + return false; + } else if (nowTime.getTime() == startTime.getTime() || nowTime.getTime() == endTime.getTime()) { + return true; + } + // 当前时间 + Calendar now = Calendar.getInstance(); + now.setTime(nowTime); + // 开始时间 + Calendar begin = Calendar.getInstance(); + begin.setTime(startTime); + // 结束时间 + Calendar end = Calendar.getInstance(); + end.setTime(endTime); + // 判断是否在 begin 之后的时间, 并且在 end 之前的时间 + return now.after(begin) && now.before(end); + } + + // = + + /** + * 获取指定时间距离该时间第二天的指定时段的时间 ( 判断凌晨情况 ) + * @param endTime 结束时间 HH:mm + * @return 距离指定结束时间还有多少毫秒 + */ + public static long getEndTimeDiffHHmm(final String endTime) { + return getEndTimeDiff(System.currentTimeMillis(), endTime, HHmm); + } + + /** + * 获取指定时间距离该时间第二天的指定时段的时间 ( 判断凌晨情况 ) + * @param startTime 开始时间 + * @param endTime 结束时间 HH:mm + * @return 距离指定结束时间还有多少毫秒 + */ + public static long getEndTimeDiffHHmm(final long startTime, final String endTime) { + return getEndTimeDiff(startTime, endTime, HHmm); + } + + /** + * 获取指定时间距离该时间第二天的指定时段的时间差 ( 判断凌晨情况 ) + * @param endTime 结束时间 + * @param format 格式 如: HH:mm + * @return 距离指定结束时间还有多少毫秒 + */ + public static long getEndTimeDiff(final String endTime, final String format) { + return getEndTimeDiff(System.currentTimeMillis(), endTime, format); + } + + /** + * 获取指定时间距离该时间第二天的指定时段的时间差 ( 判断凌晨情况 ) + *
+     *     如当前时间 2018-12-07 15:27:23, 判断距离 14:39:20 (endTime) 有多久
+     *     如果过了这个时间段, 则返回 2018-12-08 14:39:20 ( 明天的这个时间段时间 )
+     *     如果没有过这个时间段 ( 如: 17:39:20) 则返回当天时间段 2018-12-07 17:39:20 (2018-12-07 + endTime)
+     * 
+ * @param startTime 开始时间 + * @param endTime 结束时间 + * @param format 格式 如: HH:mm + * @return 距离指定结束时间还有多少毫秒 + */ + public static long getEndTimeDiff(final long startTime, final String endTime, final String format) { + if (startTime < 1 || endTime == null || format == null) return -1; + try { + // 判断格式是否加了秒 + boolean isSecond = format.endsWith(":ss"); + // 获取开始时间 + String start = formatTime(startTime, format); + // 转换时间 + int startNumber = Integer.parseInt(start.replace(":", "")); + // 获取结束时间转换 + int endNumber = Integer.parseInt(endTime.replace(":", "")); + // 时间处理 + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date(startTime)); // 设置当前时间 + // 如果当前时间大于结束时间, 表示非第二天 + if (startNumber > endNumber) { + // 时间累加一天 + calendar.add(Calendar.DATE, 1); // 当前日期加一天 + } + // 获取天数时间 + String yyyyMMddDate = formatDate(calendar.getTime(), yyyyMMdd); + // 累加时间 + String yyyyMMddHHmmssDate = yyyyMMddDate + " " + endTime + (isSecond ? "" : ":00"); + // 返回转换后的时间 + return parseLong(yyyyMMddHHmmssDate, yyyyMMddHHmmss); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getEndTimeDiff"); + } + return -1; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + /** + * 字符串 转 int + * @param str String + * @param defaultValue 默认值 + * @return int 如果转换失败, 则返回 defaultValue + */ + private static int toInt(final String str, final int defaultValue) { + if (str == null) return defaultValue; + try { + return Integer.parseInt(str); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toInt"); + } + return defaultValue; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/DevCommonUtils.java b/lib/DevApp/src/main/java/dev/utils/common/DevCommonUtils.java new file mode 100644 index 0000000000..d2d7e7f0f0 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/DevCommonUtils.java @@ -0,0 +1,2162 @@ +package dev.utils.common; + +import java.lang.reflect.Array; +import java.security.MessageDigest; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Map; +import java.util.Random; +import java.util.UUID; + +import dev.utils.JCLogUtils; + +/** + * detail: 开发常用方法工具类 + * @author Ttt + */ +public final class DevCommonUtils { + + private DevCommonUtils() { + } + + // 日志 TAG + private static final String TAG = DevCommonUtils.class.getSimpleName(); + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + + // ================ + // = 计时相关处理 = + // ================ + + /** + * 耗时时间记录 + * @param builder 拼接 Builder + * @param startTime 开始时间 + * @param endTime 结束时间 + */ + public static void timeRecord(final StringBuilder builder, final long startTime, final long endTime) { + timeRecord(builder, null, startTime, endTime); + } + + /** + * 耗时时间记录 + * @param builder 拼接 Builder + * @param title 标题 + * @param startTime 开始时间 + * @param endTime 结束时间 + */ + public static void timeRecord(final StringBuilder builder, final String title, final long startTime, final long endTime) { + if (builder == null) return; + // 使用时间 + long diffTime = endTime - startTime; + // 计算时间 + if (!isEmpty(title)) { + builder.append(NEW_LINE_STR); + builder.append(title); + } + // 计算时间 + builder.append(NEW_LINE_STR + "开始时间: " + formatTime(startTime, yyyyMMddHHmmss)); + builder.append(NEW_LINE_STR + "结束时间: " + formatTime(endTime, yyyyMMddHHmmss)); + builder.append(NEW_LINE_STR + "所用时间(毫秒): " + diffTime); + builder.append(NEW_LINE_STR + "所用时间(秒): " + (diffTime / 1000)); + } + + // = + + /** + * 获取操作时间 + * @param operateTime 操作时间 ( 毫秒 ) + * @return 操作时间 + */ + public static long getOperateTime(final long operateTime) { + return getOperateTime(operateTime, -1); + } + + /** + * 获取操作时间 + * @param operateTime 操作时间 ( 毫秒 ) + * @param randomTime 随机时间范围 ( 毫秒 ) + * @return 操作时间 + */ + public static long getOperateTime(final long operateTime, final int randomTime) { + int random = 0; + // 大于 2 才处理 + if (randomTime >= 2) { + // 随机时间 + random = getRandom(randomTime); + } + // 返回操作时间 + return Math.max(0, operateTime) + random; + } + + /** + * 堵塞操作 + * @param sleepTime 堵塞时间 ( 毫秒 ) + */ + public static void sleepOperate(final long sleepTime) { + sleepOperate(sleepTime, -1); + } + + /** + * 堵塞操作 + * @param sleepTime 堵塞时间 ( 毫秒 ) + * @param randomTime 随机时间范围 ( 毫秒 ) + */ + public static void sleepOperate(final long sleepTime, final int randomTime) { + long time = getOperateTime(sleepTime, randomTime); + if (time > 0) { + try { + Thread.sleep(time); + } catch (Throwable e1) { + JCLogUtils.eTag(TAG, e1, "sleepOperate"); + } + } + } + + // = + + /** + * 判断是否网络资源 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isHttpRes(final String str) { + if (!isEmpty(str)) { + // 属于第一位开始, 才是属于网络资源 + return str.toLowerCase().startsWith("http:") || + str.toLowerCase().startsWith("https:"); + } + return false; + } + + /** + * 循环 MD5 加密处理 + * @param str 待处理数据 + * @param number MD5 加密次数 + * @param isUppercase 是否大写处理 + * @param salts 特殊 salt 拼接 + * @return 循环加密后的字符串 + */ + public static String whileMD5(final String str, final int number, final boolean isUppercase, final String... salts) { + if (str != null && number >= 1) { + int saltLen = (salts != null) ? salts.length : 0; + // 临时字符串 + String dataTemp = str; + // 判断是否大写 + if (isUppercase) { + // 循环加密 + for (int i = 0; i < number; i++) { + if (saltLen >= i) { + String salt = salts[i]; + if (salt != null) { + dataTemp = md5Upper(dataTemp + salt); + } else { + dataTemp = md5Upper(dataTemp); + } + } else { + dataTemp = md5Upper(dataTemp); + } + } + } else { + // 循环加密 + for (int i = 0; i < number; i++) { + if (saltLen >= i) { + String salt = salts[i]; + if (salt != null) { + dataTemp = md5(dataTemp + salt); + } else { + dataTemp = md5(dataTemp); + } + } else { + dataTemp = md5(dataTemp); + } + } + } + } + return str; + } + + // ============== + // = 获取唯一数 = + // ============== + + /** + * 获取随机唯一数 + * @return {@link UUID} + */ + public static UUID randomUUID() { + return UUID.randomUUID(); + } + + /** + * 获取随机唯一数 HashCode + * @return 随机 UUID hashCode + */ + public static int randomUUIDToHashCode() { + return UUID.randomUUID().hashCode(); + } + + /** + * 获取随机唯一数 HashCode + * @param uuid {@link UUID} + * @return 随机 UUID hashCode + */ + public static int randomUUIDToHashCode(final UUID uuid) { + return (uuid != null) ? uuid.hashCode() : 0; + } + + /** + * 获取随机规则生成 UUID + * @return 随机规则生成 UUID + */ + public static UUID getRandomUUID() { + // 获取随机数 + String random1 = (900000 + new Random().nextInt(10000)) + ""; + // 获取随机数 + String random2 = (900000 + new Random().nextInt(10000)) + ""; + // 获取当前时间 + String time = System.currentTimeMillis() + random1 + random2; + // 生成唯一随机 uuid time.hashCode(), random1.hashCode() | random2.hashCode() + UUID randomUUID = new UUID(time.hashCode(), ((long) random1.hashCode() << 32) | random2.hashCode()); + return randomUUID; + } + + /** + * 获取随机规则生成 UUID 字符串 + * @return 随机规则生成 UUID 字符串 + */ + public static String getRandomUUIDToString() { + return getRandomUUID().toString(); + } + + // ============== + // = 字符串处理 = + // ============== + + /** + * 转换手机号 + * @param phone 待处理字符串 + * @return 处理后的字符串 + */ + public static String converHideMobile(final String phone) { + return converHideMobile(phone, "*"); + } + + /** + * 转换手机号 + * @param phone 待处理字符串 + * @param symbol 转换符号 + * @return 处理后的字符串 + */ + public static String converHideMobile(final String phone, final String symbol) { + return converSymbolHide(3, phone, symbol); + } + + /** + * 转换符号处理 + * @param start 开始位置 + * @param str 待处理字符串 + * @param symbol 转换符号 + * @return 处理后的字符串 + */ + public static String converSymbolHide(final int start, final String str, final String symbol) { + if (!isEmpty(str)) { + if (start <= 0) { + return str; + } + // 获取数据长度 + int length = str.length(); + // 如果数据小于 start 位则直接返回 + if (length <= start) { + return str; + } else { // 大于 start 位 + StringBuilder builder = new StringBuilder(); + builder.append(str.substring(0, start)); + int len = length - start; + // 进行平分 + len /= 2; + // 进行遍历保存 + for (int i = 0; i < len; i++) { + builder.append(symbol); + } + builder.append(str.substring(start + len, length)); + return builder.toString(); + } + } + return ""; + } + + // = + + /** + * 裁剪超出的内容, 并且追加符号 ( 如 ...) + * @param maxLength 允许最大的长度 + * @param str 待处理字符串 + * @param symbol 转换符号 + * @return 处理后的字符串 + */ + public static String subEllipsize(final int maxLength, final String str, final String symbol) { + if (maxLength >= 1) { + // 获取内容长度 + int strLength = length(str); + // 防止为不存在数据 + if (strLength != 0) { + if (maxLength >= strLength) { + return str; + } + return str.substring(0, maxLength) + toCheckValue(symbol); + } + } + return ""; + } + + /** + * 裁剪符号处理 + * @param start 开始位置 + * @param symbolNumber 转换数量 + * @param str 待处理字符串 + * @param symbol 转换符号 + * @return 处理后的字符串 + */ + public static String subSymbolHide(final int start, final int symbolNumber, final String str, final String symbol) { + if (!isEmpty(str)) { + if (start <= 0 || symbolNumber <= 0) { + return str; + } + // 获取数据长度 + int length = str.length(); + // 如果数据小于 start 位则直接返回 + if (length <= start) { + return str; + } else { // 大于 start 位 + StringBuilder builder = new StringBuilder(); + builder.append(str.substring(0, start)); + int len = length - start - symbolNumber; + // 如果超出总长度, 则进行控制 + if (len <= 0) { // 表示后面的全部转换 + len = length - start; + } else { // 需要裁剪的数量 + len = symbolNumber; + } + // 进行遍历保存 + for (int i = 0; i < len; i++) { + builder.append(symbol); + } + builder.append(str.substring(start + len, length)); + return builder.toString(); + } + } + return ""; + } + + /** + * 裁剪内容, 设置符号处理 + * @param str 待处理字符串 + * @param frontRetainLength 前面保留的长度 + * @param rearRetainLength 后面保留的长度 + * @param symbol 转换符号 + * @return 处理后的字符串 + */ + public static String subSetSymbol(final String str, final int frontRetainLength, final int rearRetainLength, final String symbol) { + if (str != null) { + try { + + // 截取前面需保留的内容 + String startStr = str.substring(0, frontRetainLength); + // 截取后面需保留的内容 + String endStr = str.substring(str.length() - rearRetainLength); + // 特殊符号长度 + int symbolLength = str.length() - (frontRetainLength + rearRetainLength); + if (symbolLength >= 1) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < symbolLength; i++) { + builder.append(symbol); + } + return startStr + builder.toString() + endStr; + } + return startStr + endStr; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subSetSymbol"); + } + } + return null; + } + + // ================== + // = 替换、截取操作 = + // ================== + + /** + * 裁剪字符串 + * @param str 需要裁剪的字符串 + * @param endIndex 结束裁剪的位置 + * @return 裁剪后的字符串 + */ + public static String substring(final String str, final int endIndex) { + return substring(str, 0, endIndex, true); + } + + /** + * 裁剪字符串 + * @param str 需要裁剪的字符串 + * @param endIndex 结束裁剪的位置 + * @param isReturn 开始位置超过限制是否返回内容 + * @return 裁剪后的字符串 + */ + public static String substring(final String str, final int endIndex, final boolean isReturn) { + return substring(str, 0, endIndex, isReturn); + } + + /** + * 裁剪字符串 + * @param str 需要裁剪的字符串 + * @param beginIndex 开始裁剪的位置 + * @param endIndex 结束裁剪的位置 + * @param isReturn 开始位置超过限制是否返回内容 + * @return 裁剪后的字符串 + */ + public static String substring(final String str, final int beginIndex, final int endIndex, final boolean isReturn) { + if (!isEmpty(str) && beginIndex >= 0 && endIndex >= 0 && endIndex >= beginIndex) { + // 获取数据长度 + int len = length(str); + // 防止超过限制 + if (beginIndex > len) { + return isReturn ? str : ""; + } + // 防止超过限制 + return str.substring(beginIndex, (endIndex >= len) ? len : endIndex); + } + return isReturn ? str : ""; + } + + // = + + /** + * 替换 ( 删除 - 替换成 "") 字符串中符合 特定标记字符的 startsWith - endsWith + * * 如 _____a_a_a_a________ 传入 _ 等于 ____a_a_a_a____ + * @param str 待处理字符串 + * @param suffix 替换符号字符串 + * @return 处理后的字符串 + */ + public static String toReplaceSEWith(final String str, final String suffix) { + return toReplaceSEWith(str, suffix, ""); + } + + /** + * 替换字符串中符合 特定标记字符的 startsWith - endsWith + * 如 _____a_a_a_a________ 传入 _, c 等于 c____a_a_a_a____c + * @param str 待处理字符串 + * @param suffix 替换匹配内容 + * @param value 需要替换的内容 + * @return 处理后的字符串 + */ + public static String toReplaceSEWith(final String str, final String suffix, final String value) { + try { + if (isEmpty(str) || isEmpty(suffix) || isEmpty(value) || suffix.equals(value)) + return str; + // 获取编辑内容长度 + int kLength = suffix.length(); + // 保存新的 Builder 中, 减少内存开销 + StringBuilder builder = new StringBuilder(str); + // 判断是否在最头部 + if (builder.indexOf(suffix) == 0) { + builder.delete(0, kLength); + // 追加内容 + builder.insert(0, value); + } + // 获取尾部的位置 + int lastIndexOf = -1; + // 数据长度 + int bufLength = -1; + // 判断是否在最尾部 + if ((lastIndexOf = builder.lastIndexOf(suffix)) == ((bufLength = builder.length()) - kLength)) { + builder.delete(lastIndexOf, bufLength); + // 追加内容 + builder.insert(lastIndexOf, value); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toReplaceSEWith"); + } + return str; + } + + // = + + /** + * 替换开头字符串 + * @param str 待处理字符串 + * @param prefix 开头匹配字符串 + * @return 处理后的字符串 + */ + public static String toReplaceStartsWith(final String str, final String prefix) { + return toReplaceStartsWith(str, prefix, ""); + } + + /** + * 替换开头字符串 + * @param str 待处理字符串 + * @param prefix 开头匹配字符串 + * @param startAppend 开头追加的内容 + * @return 处理后的字符串 + */ + public static String toReplaceStartsWith(final String str, final String prefix, final String startAppend) { + if (!isEmpty(str) && !isEmpty(prefix)) { + try { + if (str.startsWith(prefix)) { + return toCheckValue(startAppend) + str.substring(prefix.length()); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toReplaceStartsWith"); + } + } + return str; + } + + /** + * 替换结尾字符串 + * @param str 待处理字符串 + * @param suffix 结尾匹配字符串 + * @return 处理后的字符串 + */ + public static String toReplaceEndsWith(final String str, final String suffix) { + return toReplaceEndsWith(str, suffix, ""); + } + + /** + * 替换结尾字符串 + * @param str 待处理字符串 + * @param suffix 结尾匹配字符串 + * @param value 需要替换的内容 + * @return 处理后的字符串 + */ + public static String toReplaceEndsWith(final String str, final String suffix, final String value) { + if (!isEmpty(str) && !isEmpty(suffix)) { + try { + if (str.endsWith(suffix)) { + return str.substring(0, str.length() - suffix.length()) + value; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toReplaceEndsWith"); + } + } + return str; + } + + // = + + /** + * 这个方法功能主要把字符符合标记的 头部和尾部都替换成 "" + * 如 _____a_a_a_a________ 传入 _ 等于 a_a_a_a + * 替换字符串中符合 特定标记字符的 startsWith(indexOf) - endsWith(lastIndexOf) + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @return 处理后的字符串 + */ + public static String toClearSEWiths(final String str, final String suffix) { + if (isEmpty(str) || isEmpty(suffix)) return str; + try { + // 获取编辑内容长度 + int kLength = suffix.length(); + // 保存新的 Builder 中, 减少内存开销 + StringBuilder builder = new StringBuilder(str); + // 进行循环判断 - 属于最前面的, 才进行处理 + while (builder.indexOf(suffix) == 0) { + builder.delete(0, kLength); + } + // 获取尾部的位置 + int lastIndexOf = -1; + // 数据长度 + int bufLength = -1; + // 进行循环判断 - 属于最后面的, 才进行处理 + while ((lastIndexOf = builder.lastIndexOf(suffix)) == ((bufLength = builder.length()) - kLength)) { + builder.delete(lastIndexOf, bufLength); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toClearSEWiths"); + } + return str; + } + + /** + * 清空属于特定字符串开头的字段 + * 如 _____a_a_a_a________ 传入 _ 等于 a_a_a_a_____ + * 替换字符串中符合 特定标记字符的 endsWith(lastIndexOf) + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @return 处理后的字符串 + */ + public static String toClearStartsWith(final String str, final String suffix) { + if (isEmpty(str) || isEmpty(suffix)) return str; + try { + // 获取编辑内容长度 + int kLength = suffix.length(); + // 保存新的 Builder 中, 减少内存开销 + StringBuilder builder = new StringBuilder(str); + // 进行循环判断 - 属于最前面的, 才进行处理 + while (builder.indexOf(suffix) == 0) { + builder.delete(0, kLength); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toClearStartsWith"); + } + return str; + } + + /** + * 清空属于特定字符串结尾的字段 + * 如 _____a_a_a_a________ 传入 _ 等于 _____a_a_a_a + * 替换字符串中符合 特定标记字符的 endsWith(lastIndexOf) + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @return 处理后的字符串 + */ + public static String toClearEndsWith(final String str, final String suffix) { + if (isEmpty(str) || isEmpty(suffix)) return str; + try { + // 获取编辑内容长度 + int kLength = suffix.length(); + // 保存新的 Builder 中, 减少内存开销 + StringBuilder builder = new StringBuilder(str); + // 获取最后一位位置 + int sLength = 0; + // 进行循环判断 - 属于最前面的, 才进行处理 + while (builder.lastIndexOf(suffix) == ((sLength = builder.length()) - kLength)) { + builder.delete(sLength - kLength, sLength); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toClearEndsWith"); + } + return str; + } + + // = + + /** + * 替换字符串 + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @param replace 替换的内容 + * @return 处理后的字符串 + */ + public static String replaceStr(final String str, final String suffix, final String replace) { + // 如果替换的内容或者判断的字符串为 null, 则直接跳过 + if (!isEmpty(str) && !isEmpty(suffix) && replace != null && !suffix.equals(replace)) { + try { + return str.replaceAll(suffix, replace); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "replaceStr"); + } + } + return str; + } + + /** + * 替换字符串 + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @param replace 替换的内容 + * @return 处理后的字符串, 替换失败则返回 null + */ + public static String replaceStrToNull(final String str, final String suffix, final String replace) { + // 如果替换的内容或者判断的字符串为 null, 则直接跳过 + if (!isEmpty(str) && !isEmpty(suffix) && replace != null && !suffix.equals(replace)) { + try { + return str.replaceAll(suffix, replace); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "replaceStrToNull"); + } + } + return null; + } + + /** + * 替换字符串 + * @param str 内容 + * @param suffixArys 匹配判断字符串数组 + * @param replaceArys 准备替换的字符串数组 + * @return 处理后的字符串 + */ + public static String replaceStrs(final String str, final String[] suffixArys, final String[] replaceArys) { + // 防止数据为 null + if (str != null && suffixArys != null && replaceArys != null) { + String cStr = str; + // 替换的特殊字符串长度 + int spCount = suffixArys.length; + // 替换的内容长度 + int reCount = replaceArys.length; + // 相同才进行处理 + if (spCount == reCount) { + // 遍历进行判断 + for (int i = 0; i < spCount; i++) { + // 进行替换字符串 + cStr = replaceStr(cStr, suffixArys[i], replaceArys[i]); + } + // 最终不为 null, 则进行返回 + return cStr; + } + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = RandomUtils = + // =============== + + /** + * 获取 0 - 最大随机数之间的随机数 + * @param max 最大随机数 + * @return 随机介于 [0, max) 的区间值 + */ + private static int getRandom(final int max) { + return getRandom(0, max); + } + + /** + * 获取两个数之间的随机数 ( 不含最大随机数, 需要 + 1) + * @param min 最小随机数 + * @param max 最大随机数 + * @return 随机介于 [min, max) 的区间值 + */ + private static int getRandom(final int min, final int max) { + if (min > max) { + return 0; + } else if (min == max) { + return min; + } + return min + new Random().nextInt(max - min); + } + + // ============= + // = DateUtils = + // ============= + + private static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; + + /** + * 将时间戳转换日期字符串 + * @param time 时间戳 + * @param format 日期格式 + * @return 按照指定格式的日期字符串 + */ + private static String formatTime(final long time, final String format) { + if (format == null) return null; + try { + return new SimpleDateFormat(format).format(time); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "formatTime"); + } + return null; + } + + // ============ + // = MD5Utils = + // ============ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + // 用于建立十六进制字符的输出的大写字符数组 + private static final char[] HEX_DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * 加密内容 (32 位小写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + private static String md5(final String data) { + if (data == null) return null; + try { + return md5(data.getBytes()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5"); + } + return null; + } + + /** + * 加密内容 (32 位小写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + private static String md5(final byte[] data) { + if (data == null) return null; + try { + // 获取 MD5 摘要算法的 MessageDigest 对象 + MessageDigest digest = MessageDigest.getInstance("MD5"); + // 使用指定的字节更新摘要 + digest.update(data); + // 获取密文 + return toHexString(digest.digest(), HEX_DIGITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5"); + } + return null; + } + + /** + * 加密内容 (32 位大写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + private static String md5Upper(final String data) { + if (data == null) return null; + try { + return md5Upper(data.getBytes()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5Upper"); + } + return null; + } + + /** + * 加密内容 (32 位大写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + private static String md5Upper(final byte[] data) { + if (data == null) return null; + try { + // 获取 MD5 摘要算法的 MessageDigest 对象 + MessageDigest digest = MessageDigest.getInstance("MD5"); + // 使用指定的字节更新摘要 + digest.update(data); + // 获取密文 + return toHexString(digest.digest(), HEX_DIGITS_UPPER); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5Upper"); + } + return null; + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS}、{@link #HEX_DIGITS_UPPER} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // ============== + // = ArrayUtils = + // ============== + + /** + * 判断数组是否为 null + * @param objects object[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Object[] objects) { + return objects == null || objects.length == 0; + } + + /** + * 判断数组是否为 null + * @param ints int[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final int[] ints) { + return ints == null || ints.length == 0; + } + + /** + * 判断数组是否为 null + * @param bytes byte[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final byte[] bytes) { + return bytes == null || bytes.length == 0; + } + + /** + * 判断数组是否为 null + * @param chars char[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final char[] chars) { + return chars == null || chars.length == 0; + } + + /** + * 判断数组是否为 null + * @param shorts short[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final short[] shorts) { + return shorts == null || shorts.length == 0; + } + + /** + * 判断数组是否为 null + * @param longs long[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final long[] longs) { + return longs == null || longs.length == 0; + } + + /** + * 判断数组是否为 null + * @param floats float[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final float[] floats) { + return floats == null || floats.length == 0; + } + + /** + * 判断数组是否为 null + * @param doubles double[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final double[] doubles) { + return doubles == null || doubles.length == 0; + } + + /** + * 判断数组是否为 null + * @param booleans boolean[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final boolean[] booleans) { + return booleans == null || booleans.length == 0; + } + + // = + + /** + * 判断数组是否不为 null + * @param objects object[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Object[] objects) { + return objects != null && objects.length != 0; + } + + /** + * 判断数组是否不为 null + * @param ints int[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final int[] ints) { + return ints != null && ints.length != 0; + } + + /** + * 判断数组是否不为 null + * @param bytes byte[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final byte[] bytes) { + return bytes != null && bytes.length != 0; + } + + /** + * 判断数组是否不为 null + * @param chars char[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final char[] chars) { + return chars != null && chars.length != 0; + } + + /** + * 判断数组是否不为 null + * @param shorts short[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final short[] shorts) { + return shorts != null && shorts.length != 0; + } + + /** + * 判断数组是否不为 null + * @param longs long[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final long[] longs) { + return longs != null && longs.length != 0; + } + + /** + * 判断数组是否不为 null + * @param floats float[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final float[] floats) { + return floats != null && floats.length != 0; + } + + /** + * 判断数组是否不为 null + * @param doubles double[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final double[] doubles) { + return doubles != null && doubles.length != 0; + } + + /** + * 判断数组是否不为 null + * @param booleans boolean[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final boolean[] booleans) { + return booleans != null && booleans.length != 0; + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取数组长度 + * @param objects object[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final Object[] objects) { + return length(objects, 0); + } + + /** + * 获取数组长度 + * @param ints int[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final int[] ints) { + return length(ints, 0); + } + + /** + * 获取数组长度 + * @param bytes byte[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final byte[] bytes) { + return length(bytes, 0); + } + + /** + * 获取数组长度 + * @param chars char[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final char[] chars) { + return length(chars, 0); + } + + /** + * 获取数组长度 + * @param shorts short[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final short[] shorts) { + return length(shorts, 0); + } + + /** + * 获取数组长度 + * @param longs long[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final long[] longs) { + return length(longs, 0); + } + + /** + * 获取数组长度 + * @param floats float[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final float[] floats) { + return length(floats, 0); + } + + /** + * 获取数组长度 + * @param doubles double[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final double[] doubles) { + return length(doubles, 0); + } + + /** + * 获取数组长度 + * @param booleans boolean[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final boolean[] booleans) { + return length(booleans, 0); + } + + // = + + /** + * 获取数组长度 + * @param objects object[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final Object[] objects, final int defaultLength) { + return objects != null ? objects.length : defaultLength; + } + + /** + * 获取数组长度 + * @param ints int[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final int[] ints, final int defaultLength) { + return ints != null ? ints.length : defaultLength; + } + + /** + * 获取数组长度 + * @param bytes byte[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final byte[] bytes, final int defaultLength) { + return bytes != null ? bytes.length : defaultLength; + } + + /** + * 获取数组长度 + * @param chars char[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final char[] chars, final int defaultLength) { + return chars != null ? chars.length : defaultLength; + } + + /** + * 获取数组长度 + * @param shorts short[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final short[] shorts, final int defaultLength) { + return shorts != null ? shorts.length : defaultLength; + } + + /** + * 获取数组长度 + * @param longs long[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final long[] longs, final int defaultLength) { + return longs != null ? longs.length : defaultLength; + } + + /** + * 获取数组长度 + * @param floats float[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final float[] floats, final int defaultLength) { + return floats != null ? floats.length : defaultLength; + } + + /** + * 获取数组长度 + * @param doubles double[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final double[] doubles, final int defaultLength) { + return doubles != null ? doubles.length : defaultLength; + } + + /** + * 获取数组长度 + * @param booleans boolean[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final boolean[] booleans, final int defaultLength) { + return booleans != null ? booleans.length : defaultLength; + } + + // = + + /** + * 判断数组长度是否等于期望长度 + * @param objects object[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Object[] objects, final int length) { + return objects != null && objects.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param ints int[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final int[] ints, final int length) { + return ints != null && ints.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param bytes byte[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final byte[] bytes, final int length) { + return bytes != null && bytes.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param chars char[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final char[] chars, final int length) { + return chars != null && chars.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param shorts short[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final short[] shorts, final int length) { + return shorts != null && shorts.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param longs long[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final long[] longs, final int length) { + return longs != null && longs.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param floats float[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final float[] floats, final int length) { + return floats != null && floats.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param doubles double[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final double[] doubles, final int length) { + return doubles != null && doubles.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param booleans boolean[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final boolean[] booleans, final int length) { + return booleans != null && booleans.length == length; + } + + // =================== + // = CollectionUtils = + // =================== + + /** + * 判断 Collection 是否为 null + * @param collection {@link Collection} + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Collection collection) { + return (collection == null || collection.size() == 0); + } + + /** + * 判断 Collection 是否不为 null + * @param collection {@link Collection} + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Collection collection) { + return (collection != null && collection.size() != 0); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取 Collection 长度 + * @param collection {@link Collection} + * @return 如果 Collection 为 null, 则返回默认长度, 如果不为 null, 则返回 collection.size() + */ + public static int length(final Collection collection) { + return length(collection, 0); + } + + /** + * 获取 Collection 长度 + * @param collection {@link Collection} + * @param defaultLength 集合为 null 默认长度 + * @return 如果 Collection 为 null, 则返回 defaultLength, 如果不为 null, 则返回 collection.size() + */ + public static int length(final Collection collection, final int defaultLength) { + return collection != null ? collection.size() : defaultLength; + } + + // = + + /** + * 获取长度 to Collection 是否等于期望长度 + * @param collection {@link Collection} + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Collection collection, final int length) { + return collection != null && collection.size() == length; + } + + // ============ + // = MapUtils = + // ============ + + /** + * 判断 Map 是否为 null + * @param map {@link Map} + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Map map) { + return (map == null || map.size() == 0); + } + + /** + * 判断 Map 是否不为 null + * @param map {@link Map} + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Map map) { + return (map != null && map.size() != 0); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取 Map 长度 + * @param map {@link Map} + * @return 如果 Map 为 null, 则返回默认长度, 如果不为 null, 则返回 map.size() + */ + public static int length(final Map map) { + return length(map, 0); + } + + /** + * 获取 Map 长度 + * @param map {@link Map} + * @param defaultLength 集合为 null 默认长度 + * @return 如果 Map 为 null, 则返回 defaultLength, 如果不为 null, 则返回 map.size() + */ + public static int length(final Map map, final int defaultLength) { + return map != null ? map.size() : defaultLength; + } + + // = + + /** + * 获取长度 to Map 是否等于期望长度 + * @param map {@link Map} + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Map map, final int length) { + return map != null && map.size() == length; + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @param isTrim 是否调用 trim() + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String str, final boolean isTrim) { + if (str != null) { + return isEmpty(isTrim ? str.trim() : str); + } + return false; + } + + /** + * 判断多个字符串是否存在为 null 的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + if (isEmpty(strs[i])) { + return true; + } + } + return false; + } + // 默认表示属于 null + return true; + } + + // = + + /** + * 判断字符串是否不为 null + * @param str 待校验的字符串 + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final String str) { + return (str != null && str.length() != 0); + } + + /** + * 判断字符串是否不为 null + * @param str 待校验的字符串 + * @param isTrim 是否调用 trim() + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final String str, final boolean isTrim) { + return isNotEmpty(isTrim ? str.trim() : str); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取字符串长度 + * @param str 待校验的字符串 + * @return 字符串长度, 如果字符串为 null, 则返回 0 + */ + public static int length(final String str) { + return str == null ? 0 : str.length(); + } + + /** + * 获取字符串长度 + * @param str 待校验的字符串 + * @param defaultLength 字符串为 null 时, 返回的长度 + * @return 字符串长度, 如果字符串为 null, 则返回 defaultLength + */ + public static int length(final String str, final int defaultLength) { + return str != null ? str.length() : defaultLength; + } + + // = + + /** + * 获取字符串长度 是否等于期望长度 + * @param str 待校验的字符串 + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final String str, final int length) { + return str != null && str.length() == length; + } + + // ============ + // = 对比判断 = + // ============ + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + /** + * 判断多个字符串是否相等, 只有全相等才返回 true - 对比大小写 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEquals(final String... strs) { + return isEquals(false, strs); + } + + /** + * 判断多个字符串是否相等, 只有全相等才返回 true + * @param isIgnore 是否忽略大小写 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEquals(final boolean isIgnore, final String... strs) { + if (strs != null) { + String last = null; + // 获取数据长度 + int len = strs.length; + // 如果最多只有一个数据判断, 则直接跳过 + if (len <= 1) { + return false; + } + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 如果等于 null, 则跳过 + if (val == null) { + return false; + } + if (last != null) { + if (isIgnore) { + if (!val.equalsIgnoreCase(last)) { + return false; + } + } else { + if (!val.equals(last)) { + return false; + } + } + } + last = val; + } + return true; + } + return false; + } + + /** + * 判断多个字符串, 只要有一个符合条件则通过 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isOrEquals(final String str, final String... strs) { + return isOrEquals(false, str, strs); + } + + /** + * 判断多个字符串, 只要有一个符合条件则通过 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isOrEquals(final boolean isIgnore, final String str, final String... strs) { + if (str != null && strs != null && strs.length != 0) { + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 如果等于 null, 则跳过 + if (val == null) { + continue; + } else { + if (isIgnore) { + if (val.equalsIgnoreCase(str)) { + return true; + } + } else { + if (val.equals(str)) { + return true; + } + } + } + } + } + return false; + } + + /** + * 判断一堆值中, 是否存在符合该条件的 ( 包含 ) + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContains(final String str, final String... strs) { + return isContains(false, str, strs); + } + + /** + * 判断一堆值中, 是否存在符合该条件的 ( 包含 ) + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContains(final boolean isIgnore, final String str, final String... strs) { + if (str != null && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取内容长度 + int cLength = strTemp.length(); + // 遍历判断 + for (int i = 0, len = strs.length; i < len; i++) { + // 获取参数 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val) && cLength != 0) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否包含 + if (valIgnore.indexOf(strTemp) != -1) { + return true; + } + } else { + // 判断是否包含 + if (val.indexOf(strTemp) != -1) { + return true; + } + } + } else { + // 下面这一串可以不要, 因为判断字符串是否包含 + // 已经处理了值不为 null, 并且需要判断的值长度不能为 0, 下面则不需要加上 + if (strTemp.equals(val)) { + return true; + } + } + } + } + return false; + } + + /** + * 判断内容, 是否属于特定字符串开头 - 对比大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isStartsWith(final String str, final String... strs) { + return isStartsWith(false, str, strs); + } + + /** + * 判断内容, 是否属于特定字符串开头 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isStartsWith(final boolean isIgnore, final String str, final String... strs) { + if (!isEmpty(str) && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val)) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否属于 val 开头 + if (strTemp.startsWith(valIgnore)) { + return true; + } + } else { + // 判断是否属于 val 开头 + if (strTemp.startsWith(val)) { + return true; + } + } + } + } + } + return false; + } + + /** + * 判断内容, 是否属于特定字符串结尾 - 对比大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEndsWith(final String str, final String... strs) { + return isEndsWith(false, str, strs); + } + + /** + * 判断内容, 是否属于特定字符串结尾 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEndsWith(final boolean isIgnore, final String str, final String... strs) { + if (!isEmpty(str) && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val)) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否属于 val 结尾 + if (strTemp.endsWith(valIgnore)) { + return true; + } + } else { + // 判断是否属于 val 结尾 + if (strTemp.endsWith(val)) { + return true; + } + } + } + } + } + return false; + } + + // ============ + // = 其他处理 = + // ============ + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + * 清空字符串全部空格 + * @param str 待处理字符串 + * @return 处理后的字符串 + */ + public static String toClearSpace(final String str) { + if (isEmpty(str)) return str; + return str.replaceAll(" ", ""); + } + + /** + * 清空字符串前后所有空格 + * @param str 待处理字符串 + * @return 处理后的字符串 + */ + public static String toClearSpaceTrim(final String str) { + if (isEmpty(str)) return str; + String strTemp = str; + // 如果前面或者后面都是空格开头, 就一直进行处理 + while (strTemp.startsWith(" ") || strTemp.endsWith(" ")) { + strTemp = strTemp.trim(); + } + return strTemp; + } + + // = + + /** + * 追加空格 + * @param number 空格数量 + * @return 指定数量的空格字符串 + */ + public static String appendSpace(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append(" "); + } + } + return builder.toString(); + } + + /** + * 追加 Tab + * @param number tab 键数量 + * @return 指定数量的 Tab 字符串 + */ + public static String appendTab(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append("\t"); + } + } + return builder.toString(); + } + + /** + * 追加换行 + * @param number 换行数量 + * @return 指定数量的换行字符串 + */ + public static String appendLine(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append(NEW_LINE_STR); + } + } + return builder.toString(); + } + + // = + + /** + * 检查字符串 + * @param str 待校验字符串 + * @return 如果待校验字符串为 null, 则返回默认字符串, 如果不为 null, 则返回该字符串 + */ + public static String toCheckValue(final String str) { + return toCheckValue("", str); + } + + /** + * 检查字符串 + * @param defaultStr 默认字符串 + * @param str 待校验字符串 + * @return 如果待校验字符串为 null, 则返回 defaultStr, 如果不为 null, 则返回该字符串 + */ + public static String toCheckValue(final String defaultStr, final String str) { + return isEmpty(str) ? defaultStr : str; + } + + /** + * 检查字符串 - 单独检查两个值 + * @param defaultStr 默认字符串 + * @param value1 第一个待校验字符串 + * @param value2 第二个待校验字符串 + * @return 两个待校验字符串中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValue(final String defaultStr, final String value1, final String value2) { + if (isEmpty(value1)) { + if (isEmpty(value2)) { + return defaultStr; + } else { + return value2; + } + } else { + return value1; + } + } + + /** + * 检查字符串 - 多个值 + * @param defaultStr 默认字符串 + * @param strs 待校验字符串数组 + * @return 字符串数组中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValues(final String defaultStr, final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + String val = strs[i]; + if (isEmpty(val)) { + if (i == len - 1) { + return defaultStr; // 属于最后一个, 则返回默认值 + } else { + continue; // 不属于最后一个则跳过 + } + } else { + return val; + } + } + } + return defaultStr; + } + + /** + * 检查字符串 - 多个值 ( 删除前后空格对比判断 ) + * @param defaultStr 默认字符串 + * @param strs 待校验字符串数组 + * @return 字符串数组中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValuesSpace(final String defaultStr, final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + // 删除前后空格处理后, 进行返回 + String val = toClearSpaceTrim(strs[i]); + if (isEmpty(val)) { + if (i == len - 1) { + return defaultStr; // 属于最后一个, 则返回默认值 + } else { + continue; // 不属于最后一个则跳过 + } + } else { + return val; + } + } + } + return defaultStr; + } + + // ================== + // = 数据格式化处理 = + // ================== + + /** + * 获取格式化后的字符串 + * @param format 待格式化字符串 + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getFormatString(final String format, final Object... args) { + if (format == null) return null; + try { + if (args != null && args.length != 0) { + return String.format(format, args); + } else { + return format; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFormatString"); + } + return null; + } + + // = + + /** + * 获取自动数量格式化后的字符串 ( 可变参数 ) + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getAutoFormatString(final Object... args) { + if (args != null && args.length != 0) { + try { + int length = args.length; + StringBuilder builder = new StringBuilder(); + builder.append("%s"); + if (length > 1) { + for (int i = 1; i < length; i++) { + builder.append(" %s"); + } + } + return String.format(builder.toString(), args); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAutoFormatString"); + } + } + return null; + } + + /** + * 获取自动数量格式化后的字符串 ( 可变参数 ) + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getAutoFormatString2(final Object... args) { + if (args != null && args.length != 0) { + try { + int length = args.length; + StringBuilder builder = new StringBuilder(); + builder.append("【%s】"); + if (length > 1) { + for (int i = 1; i < length; i++) { + builder.append(" %s"); + } + } + return String.format(builder.toString(), args); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAutoFormatString2"); + } + } + return null; + } + + // = + + /** + * StringBuilder 拼接处理 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final Object... args) { + return appends(new StringBuilder(), null, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param split 追加间隔 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final String split, final Object... args) { + return appends(new StringBuilder(), split, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param builder 拼接 Builder + * @param split 追加间隔 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final StringBuilder builder, final String split, final Object... args) { + return appends(builder, split, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param builder 拼接 Builder + * @param split 追加间隔 + * @param end 结尾是否追加 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final StringBuilder builder, final String split, final boolean end, final Object... args) { + if (builder != null && args != null) { + // 获取间隔字符串, 优化循环判断 + String str = isEmpty(split) ? "" : split; + // 循环处理 + for (int i = 0, len = args.length; i < len; i++) { + builder.append(args[i]); // 拼接数据 + // 判断是否结尾 + if (len - i == 1) { + // 判断结尾是否追加 + if (end) builder.append(str); // 间隔追加 + } else { + builder.append(str); // 间隔追加 + } + } + return builder; + } + return builder; + } + + // =============== + // = ObjectUtils = + // =============== + + /** + * 判断对象是否为空 + * @param object 对象 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Object object) { + if (object == null) { + return true; + } + try { + if (object.getClass().isArray() && Array.getLength(object) == 0) { + return true; + } + if (object instanceof CharSequence && object.toString().length() == 0) { + return true; + } + if (object instanceof Collection && ((Collection) object).isEmpty()) { + return true; + } + if (object instanceof Map && ((Map) object).isEmpty()) { + return true; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isEmpty"); + } + return false; + } + + /** + * 判断对象是否非空 + * @param object 对象 + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Object object) { + return !isEmpty(object); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/EncodeUtils.java b/lib/DevApp/src/main/java/dev/utils/common/EncodeUtils.java new file mode 100644 index 0000000000..1c62a5781f --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/EncodeUtils.java @@ -0,0 +1,293 @@ +package dev.utils.common; + +import dev.utils.JCLogUtils; +import dev.utils.common.cipher.Base64; + +/** + * detail: 编码工具类 + * @author Ttt + *
+ *     Base64 flags
+ *     

+ * CRLF 这个参数看起来比较眼熟, 它就是 Win 风格的换行符, 意思就是使用 CR LF 这一对作为一行的结尾而不是 Unix 风格的 LF + * DEFAULT 这个参数是默认, 使用默认的方法来加密 + * NO_PADDING 这个参数是略去加密字符串最后的 = + * NO_WRAP 这个参数意思是略去所有的换行符 ( 设置后 CRLF 就没用了 ) + * URL_SAFE 这个参数意思是加密时不使用对 URL 和文件名有特殊意义的字符来作为加密字符, 具体就是以 - 和 _ 取代 + 和 / + *
+ */ +public final class EncodeUtils { + + private EncodeUtils() { + } + + // 日志 TAG + private static final String TAG = EncodeUtils.class.getSimpleName(); + + // ========== + // = Base64 = + // ========== + + // =============== + // = Base64 编码 = + // =============== + + /** + * Base64 编码 + * @param input 待处理数据 + * @return Base64 编码后的 byte[] + */ + public static byte[] base64Encode(final String input) { + return base64Encode(input, Base64.NO_WRAP); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 编码后的 byte[] + */ + public static byte[] base64Encode(final String input, final int flags) { + return base64Encode(toBytes(input), flags); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @return Base64 编码后的 byte[] + */ + public static byte[] base64Encode(final byte[] input) { + return base64Encode(input, Base64.NO_WRAP); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 编码后的 byte[] + */ + public static byte[] base64Encode(final byte[] input, final int flags) { + if (input == null) return null; + return Base64.encode(input, flags); + } + + // = + + /** + * Base64 编码 + * @param input 待处理数据 + * @return Base64 编码后的 byte[] 转 String + */ + public static String base64EncodeToString(final String input) { + return base64EncodeToString(input, Base64.NO_WRAP); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 编码后的 byte[] 转 String + */ + public static String base64EncodeToString(final String input, final int flags) { + return base64EncodeToString(toBytes(input), flags); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @return Base64 编码后的 byte[] 转 String + */ + public static String base64EncodeToString(final byte[] input) { + return base64EncodeToString(input, Base64.NO_WRAP); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 编码后的 byte[] 转 String + */ + public static String base64EncodeToString(final byte[] input, final int flags) { + if (input == null) return null; + return toString(Base64.encode(input, flags)); + } + + // =============== + // = Base64 解码 = + // =============== + + /** + * Base64 解码 + * @param input 待处理数据 + * @return Base64 解码后的 byte[] + */ + public static byte[] base64Decode(final String input) { + return base64Decode(input, Base64.NO_WRAP); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 解码后的 byte[] + */ + public static byte[] base64Decode(final String input, final int flags) { + return base64Decode(toBytes(input), flags); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @return Base64 解码后的 byte[] + */ + public static byte[] base64Decode(final byte[] input) { + return base64Decode(input, Base64.NO_WRAP); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 解码后的 byte[] + */ + public static byte[] base64Decode(final byte[] input, final int flags) { + if (input == null) return null; + return Base64.decode(input, flags); + } + + // = + + /** + * Base64 解码 + * @param input 待处理数据 + * @return Base64 解码后的 byte[] 转 String + */ + public static String base64DecodeToString(final String input) { + return base64DecodeToString(input, Base64.NO_WRAP); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 解码后的 byte[] 转 String + */ + public static String base64DecodeToString(final String input, final int flags) { + return base64DecodeToString(toBytes(input), flags); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @return Base64 解码后的 byte[] 转 String + */ + public static String base64DecodeToString(final byte[] input) { + return base64DecodeToString(input, Base64.NO_WRAP); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 解码后的 byte[] 转 String + */ + public static String base64DecodeToString(final byte[] input, final int flags) { + if (input == null) return null; + return toString(Base64.decode(input, flags)); + } + + // ======== + // = Html = + // ======== + + /** + * Html 字符串编码 + *
+     *     TextUtils.htmlEncode()
+     * 
+ * @param input 待处理数据 + * @return Html 编码后的数据 + */ + public static String htmlEncode(final CharSequence input) { + if (input == null) return null; + StringBuilder builder = new StringBuilder(); + char ch; + for (int i = 0, len = input.length(); i < len; i++) { + ch = input.charAt(i); + switch (ch) { + case '<': + builder.append("<"); //$NON-NLS-1$ + break; + case '>': + builder.append(">"); //$NON-NLS-1$ + break; + case '&': + builder.append("&"); //$NON-NLS-1$ + break; + case '\'': + //http://www.w3.org/TR/xhtml1 + // The named character reference ' (the apostrophe, U+0027) was introduced in + // XML 1.0 but does not appear in HTML. Authors should therefore use ' instead + // of ' to work as expected in HTML 4 user agents. + builder.append("'"); //$NON-NLS-1$ + break; + case '"': + builder.append("""); //$NON-NLS-1$ + break; + default: + builder.append(ch); + } + } + return builder.toString(); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + /** + * byte[] 转 String + * @param data byte[] + * @return {@link String} + */ + private static String toString(final byte[] data) { + return toString(data, null); + } + + /** + * byte[] 转 String + * @param data byte[] + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + private static String toString(final byte[] data, final String defaultStr) { + if (data != null) { + try { + return new String(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + } + return defaultStr; + } + + /** + * 字符串 获取 byte[] + * @param str String + * @return byte[] + */ + private static byte[] toBytes(final String str) { + if (str != null) { + try { + return str.getBytes(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toBytes"); + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/FieldUtils.java b/lib/DevApp/src/main/java/dev/utils/common/FieldUtils.java new file mode 100644 index 0000000000..e77099757c --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/FieldUtils.java @@ -0,0 +1,201 @@ +package dev.utils.common; + +import java.io.Serializable; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.LinkedList; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 变量字段工具类 + * @author Ttt + */ +public final class FieldUtils { + + private FieldUtils() { + } + + // 日志 TAG + private static final String TAG = FieldUtils.class.getSimpleName(); + + /** + * 判断是否序列化 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isSerializable(final Field field) { + if (field == null) return false; + try { + Class[] clazzs = field.getType().getInterfaces(); + for (Class clazz : clazzs) { + if (Serializable.class == clazz) { + return true; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isSerializable"); + } + return false; + } + + /** + * 设置字段的值 + * @param field {@link Field} + * @param object Object + * @param value Object-Value + * @return 对应的 Object + */ + public static Object set(final Field field, final Object object, final Object value) { + if (field == null || object == null) return null; + try { + field.setAccessible(true); + field.set(object, value); + return field.get(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "set"); + } + return null; + } + + /** + * 获取字段的值 + * @param field {@link Field} + * @param object Object + * @return 对应的 Object + */ + public static Object get(final Field field, final Object object) { + if (field == null || object == null) return null; + try { + field.setAccessible(true); + return field.get(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + return null; + } + + // = + + /** + * 是否 long/Long 类型 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isLong(final Field field) { + return field != null && (field.getType() == long.class || field.getType() == Long.class); + } + + /** + * 是否 float/Float 类型 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isFloat(final Field field) { + return field != null && (field.getType() == float.class || field.getType() == Float.class); + } + + /** + * 是否 double/Double 类型 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isDouble(final Field field) { + return field != null && (field.getType() == double.class || field.getType() == Double.class); + } + + /** + * 是否 int/Integer 类型 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isInteger(final Field field) { + return field != null && (field.getType() == int.class || field.getType() == Integer.class); + } + + /** + * 是否 String 类型 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isString(final Field field) { + return field != null && (field.getType() == String.class); + } + + // = + + /** + * 获取字段的泛型类型, 如果不带泛型返回 null + * @param field {@link Field} + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericType(final Field field) { + if (field == null) return null; + try { + Type type = field.getGenericType(); + if (type instanceof ParameterizedType) { + type = ((ParameterizedType) type).getActualTypeArguments()[0]; + if (type instanceof Class) { + return (Class) type; + } + } else if (type instanceof Class) { + return (Class) type; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getGenericType"); + } + return null; + } + + /** + * 获取数组的类型 + * @param field {@link Field} + * @param 未知类型 + * @return 数组类型 + */ + public static Class getComponentType(final Field field) { + if (field == null || field.getType() == null) return null; + return field.getType().getComponentType(); + } + + /** + * 获取全部 Field, 包括父类 + * @param clazz {@link Class} + * @return {@link List} + */ + public static List getAllDeclaredFields(final Class clazz) { + if (clazz == null) return null; + try { + Class clazzTemp = clazz; + // find all field. + LinkedList fieldList = new LinkedList<>(); + while (clazzTemp != null && clazzTemp != Object.class) { + Field[] fs = clazzTemp.getDeclaredFields(); + for (int i = 0; i < fs.length; i++) { + Field f = fs[i]; + if (!isInvalid(f)) { + fieldList.addLast(f); + } + } + clazzTemp = clazzTemp.getSuperclass(); + } + return fieldList; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAllDeclaredFields"); + } + return null; + } + + /** + * 是静态常量或者内部结构属性 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isInvalid(final Field field) { + return field != null && ((Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) || field.isSynthetic()); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/FileIOUtils.java b/lib/DevApp/src/main/java/dev/utils/common/FileIOUtils.java new file mode 100644 index 0000000000..8948b6e1d5 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/FileIOUtils.java @@ -0,0 +1,706 @@ +package dev.utils.common; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 文件 (IO 流 ) 工具类 + * @author Ttt + */ +public final class FileIOUtils { + + private FileIOUtils() { + } + + // 日志 TAG + private static final String TAG = FileIOUtils.class.getSimpleName(); + // 换行符 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + // 缓存大小 + private static int sBufferSize = 8192; + + /** + * 设置缓冲区的大小, 默认大小等于 8192 字节 + * @param bufferSize 缓冲 Buffer 大小 + */ + public static void setBufferSize(final int bufferSize) { + sBufferSize = bufferSize; + } + + /** + * 通过输入流写入文件 + * @param filePath 文件路径 + * @param inputStream {@link InputStream} + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromIS(final String filePath, final InputStream inputStream) { + return writeFileFromIS(getFileByPath(filePath), inputStream, false); + } + + /** + * 通过输入流写入文件 + * @param filePath 文件路径 + * @param inputStream {@link InputStream} + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromIS(final String filePath, final InputStream inputStream, final boolean append) { + return writeFileFromIS(getFileByPath(filePath), inputStream, append); + } + + /** + * 通过输入流写入文件 + * @param file 文件 + * @param inputStream {@link InputStream} + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromIS(final File file, final InputStream inputStream) { + return writeFileFromIS(file, inputStream, false); + } + + /** + * 通过输入流写入文件 + * @param file 文件 + * @param inputStream {@link InputStream} + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromIS(final File file, final InputStream inputStream, final boolean append) { + if (!createOrExistsFile(file) || inputStream == null) return false; + OutputStream os = null; + try { + os = new BufferedOutputStream(new FileOutputStream(file, append)); + byte[] data = new byte[sBufferSize]; + int len; + while ((len = inputStream.read(data, 0, sBufferSize)) != -1) { + os.write(data, 0, len); + } + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromIS"); + return false; + } finally { + closeIOQuietly(inputStream, os); + } + } + + /** + * 通过字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes) { + return writeFileFromBytesByStream(getFileByPath(filePath), bytes, false); + } + + /** + * 通过字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes, final boolean append) { + return writeFileFromBytesByStream(getFileByPath(filePath), bytes, append); + } + + /** + * 通过字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes) { + return writeFileFromBytesByStream(file, bytes, false); + } + + /** + * 通过字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes, final boolean append) { + if (bytes == null || !createOrExistsFile(file)) return false; + BufferedOutputStream bos = null; + try { + bos = new BufferedOutputStream(new FileOutputStream(file, append)); + bos.write(bytes); + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromBytesByStream"); + return false; + } finally { + closeIOQuietly(bos); + } + } + + /** + * 通过 FileChannel 把字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByChannel(final String filePath, final byte[] bytes, final boolean isForce) { + return writeFileFromBytesByChannel(getFileByPath(filePath), bytes, false, isForce); + } + + /** + * 通过 FileChannel 把字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByChannel(final String filePath, final byte[] bytes, final boolean append, final boolean isForce) { + return writeFileFromBytesByChannel(getFileByPath(filePath), bytes, append, isForce); + } + + /** + * 通过 FileChannel 把字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByChannel(final File file, final byte[] bytes, final boolean isForce) { + return writeFileFromBytesByChannel(file, bytes, false, isForce); + } + + /** + * 通过 FileChannel 把字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByChannel(final File file, final byte[] bytes, final boolean append, final boolean isForce) { + if (bytes == null) return false; + FileChannel fc = null; + try { + fc = new FileOutputStream(file, append).getChannel(); + fc.position(fc.size()); + fc.write(ByteBuffer.wrap(bytes)); + if (isForce) fc.force(true); + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromBytesByChannel"); + return false; + } finally { + closeIOQuietly(fc); + } + } + + /** + * 通过 MappedByteBuffer 把字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByMap(final String filePath, final byte[] bytes, final boolean isForce) { + return writeFileFromBytesByMap(filePath, bytes, false, isForce); + } + + /** + * 通过 MappedByteBuffer 把字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByMap(final String filePath, final byte[] bytes, final boolean append, final boolean isForce) { + return writeFileFromBytesByMap(getFileByPath(filePath), bytes, append, isForce); + } + + /** + * 通过 MappedByteBuffer 把字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByMap(final File file, final byte[] bytes, final boolean isForce) { + return writeFileFromBytesByMap(file, bytes, false, isForce); + } + + /** + * 通过 MappedByteBuffer 把字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByMap(final File file, final byte[] bytes, final boolean append, final boolean isForce) { + if (bytes == null || !createOrExistsFile(file)) return false; + FileChannel fc = null; + try { + fc = new FileOutputStream(file, append).getChannel(); + MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, fc.size(), bytes.length); + mbb.put(bytes); + if (isForce) mbb.force(); + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromBytesByMap"); + return false; + } finally { + closeIOQuietly(fc); + } + } + + /** + * 通过字符串写入文件 + * @param filePath 文件路径 + * @param content 写入内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromString(final String filePath, final String content) { + return writeFileFromString(getFileByPath(filePath), content, false); + } + + /** + * 通过字符串写入文件 + * @param filePath 文件路径 + * @param content 写入内容 + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromString(final String filePath, final String content, final boolean append) { + return writeFileFromString(getFileByPath(filePath), content, append); + } + + /** + * 通过字符串写入文件 + * @param file 文件 + * @param content 写入内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromString(final File file, final String content) { + return writeFileFromString(file, content, false); + } + + /** + * 通过字符串写入文件 + * @param file 文件 + * @param content 写入内容 + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromString(final File file, final String content, final boolean append) { + if (file == null || content == null) return false; + if (!createOrExistsFile(file)) return false; + BufferedWriter bw = null; + try { + bw = new BufferedWriter(new FileWriter(file, append)); + bw.write(content); + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromString"); + return false; + } finally { + closeIOQuietly(bw); + } + } + + // ============== + // = 读写分界线 = + // ============== + + /** + * 读取文件内容, 返回换行 List + * @param filePath 文件路径 + * @return 换行 {@link List} + */ + public static List readFileToList(final String filePath) { + return readFileToList(getFileByPath(filePath), null); + } + + /** + * 读取文件内容, 返回换行 List + * @param filePath 文件路径 + * @param charsetName 字符编码 + * @return 换行 {@link List} + */ + public static List readFileToList(final String filePath, final String charsetName) { + return readFileToList(getFileByPath(filePath), charsetName); + } + + /** + * 读取文件内容, 返回换行 List + * @param file 文件 + * @return 换行 {@link List} + */ + public static List readFileToList(final File file) { + return readFileToList(file, 0, Integer.MAX_VALUE, null); + } + + /** + * 读取文件内容, 返回换行 List + * @param file 文件 + * @param charsetName 字符编码 + * @return 换行 {@link List} + */ + public static List readFileToList(final File file, final String charsetName) { + return readFileToList(file, 0, Integer.MAX_VALUE, charsetName); + } + + /** + * 读取文件内容, 返回换行 List + * @param filePath 文件路径 + * @param start 开始位置 + * @param end 结束位置 + * @return 换行 {@link List} + */ + public static List readFileToList(final String filePath, final int start, final int end) { + return readFileToList(getFileByPath(filePath), start, end, null); + } + + /** + * 读取文件内容, 返回换行 List + * @param filePath 文件路径 + * @param start 开始位置 + * @param end 结束位置 + * @param charsetName 字符编码 + * @return 换行 {@link List} + */ + public static List readFileToList(final String filePath, final int start, final int end, final String charsetName) { + return readFileToList(getFileByPath(filePath), start, end, charsetName); + } + + /** + * 读取文件内容, 返回换行 List + * @param file 文件 + * @param start 开始位置 + * @param end 结束位置 + * @return 换行 {@link List} + */ + public static List readFileToList(final File file, final int start, final int end) { + return readFileToList(file, start, end, null); + } + + /** + * 读取文件内容, 返回换行 List + * @param file 文件 + * @param start 开始位置 + * @param end 结束位置 + * @param charsetName 字符编码 + * @return 换行 {@link List} + */ + public static List readFileToList(final File file, final int start, final int end, final String charsetName) { + if (!isFileExists(file)) return null; + if (start > end) return null; + BufferedReader br = null; + try { + String line; + int curLine = 1; + List list = new ArrayList<>(); + if (isSpace(charsetName)) { + br = new BufferedReader(new InputStreamReader(new FileInputStream(file))); + } else { + br = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName)); + } + while ((line = br.readLine()) != null) { + if (curLine > end) break; + if (start <= curLine && curLine <= end) list.add(line); + ++curLine; + } + return list; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "readFileToList"); + return null; + } finally { + closeIOQuietly(br); + } + } + + // = + + /** + * 读取文件内容, 返回字符串 + * @param filePath 文件路径 + * @return 文件内容字符串 + */ + public static String readFileToString(final String filePath) { + return readFileToString(getFileByPath(filePath), null); + } + + /** + * 读取文件内容, 返回字符串 + * @param filePath 文件路径 + * @param charsetName 字符编码 + * @return 文件内容字符串 + */ + public static String readFileToString(final String filePath, final String charsetName) { + return readFileToString(getFileByPath(filePath), charsetName); + } + + /** + * 读取文件内容, 返回字符串 + * @param file 文件 + * @return 文件内容字符串 + */ + public static String readFileToString(final File file) { + return readFileToString(file, null); + } + + /** + * 读取文件内容, 返回字符串 + * @param file 文件 + * @param charsetName 字符编码 + * @return 文件内容字符串 + */ + public static String readFileToString(final File file, final String charsetName) { + if (!isFileExists(file)) return null; + BufferedReader br = null; + try { + StringBuilder builder = new StringBuilder(); + if (isSpace(charsetName)) { + br = new BufferedReader(new InputStreamReader(new FileInputStream(file))); + } else { + br = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName)); + } + String line; + if ((line = br.readLine()) != null) { + builder.append(line); + while ((line = br.readLine()) != null) { + builder.append(NEW_LINE_STR).append(line); + } + } + return builder.toString(); + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "readFileToString"); + return null; + } finally { + closeIOQuietly(br); + } + } + + /** + * 读取文件内容, 返回 byte[] + * @param filePath 文件路径 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByStream(final String filePath) { + return readFileToBytesByStream(getFileByPath(filePath)); + } + + /** + * 读取文件内容, 返回 byte[] + * @param file 文件 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByStream(final File file) { + if (!isFileExists(file)) return null; + FileInputStream fis = null; + ByteArrayOutputStream baos = null; + try { + fis = new FileInputStream(file); + baos = new ByteArrayOutputStream(); + byte[] b = new byte[sBufferSize]; + int len; + while ((len = fis.read(b, 0, sBufferSize)) != -1) { + baos.write(b, 0, len); + } + return baos.toByteArray(); + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "readFileToBytesByStream"); + return null; + } finally { + closeIOQuietly(fis, baos); + } + } + + /** + * 通过 FileChannel, 读取文件内容, 返回 byte[] + * @param filePath 文件路径 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByChannel(final String filePath) { + return readFileToBytesByChannel(getFileByPath(filePath)); + } + + /** + * 通过 FileChannel, 读取文件内容, 返回 byte[] + * @param file 文件 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByChannel(final File file) { + if (!isFileExists(file)) return null; + FileChannel fc = null; + try { + fc = new RandomAccessFile(file, "r").getChannel(); + ByteBuffer byteBuffer = ByteBuffer.allocate((int) fc.size()); + while (true) { + if (!((fc.read(byteBuffer)) > 0)) break; + } + return byteBuffer.array(); + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "readFileToBytesByChannel"); + return null; + } finally { + closeIOQuietly(fc); + } + } + + /** + * 通过 MappedByteBuffer, 读取文件内容, 返回 byte[] + * @param filePath 文件路径 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByMap(final String filePath) { + return readFileToBytesByMap(getFileByPath(filePath)); + } + + /** + * 通过 MappedByteBuffer, 读取文件内容, 返回 byte[] + * @param file 文件 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByMap(final File file) { + if (!isFileExists(file)) return null; + FileChannel fc = null; + try { + fc = new RandomAccessFile(file, "r").getChannel(); + int size = (int) fc.size(); + MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, size).load(); + byte[] result = new byte[size]; + mbb.get(result, 0, size); + return result; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "readFileToBytesByMap"); + return null; + } finally { + closeIOQuietly(fc); + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============== + // = CloseUtils = + // ============== + + /** + * 安静关闭 IO + * @param closeables Closeable[] + */ + private static void closeIOQuietly(final Closeable... closeables) { + if (closeables == null) return; + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception ignore) { + } + } + } + } + + // ============= + // = FileUtils = + // ============= + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 判断文件是否存在, 不存在则判断是否创建成功 + * @param filePath 文件路径 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + private static boolean createOrExistsFile(final String filePath) { + return createOrExistsFile(getFileByPath(filePath)); + } + + /** + * 判断文件是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + private static boolean createOrExistsFile(final File file) { + if (file == null) return false; + // 如果存在, 是文件则返回 true, 是目录则返回 false + if (file.exists()) return file.isFile(); + // 判断文件是否存在, 不存在则直接返回 + if (!createOrExistsDir(file.getParentFile())) return false; + try { + // 存在, 则返回新的路径 + return file.createNewFile(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "createOrExistsFile"); + return false; + } + } + + /** + * 判断目录是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + private static boolean createOrExistsDir(final File file) { + // 如果存在, 是目录则返回 true, 是文件则返回 false, 不存在则返回是否创建成功 + return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); + } + + /** + * 检查是否存在某个文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + private static boolean isFileExists(final File file) { + return file != null && file.exists(); + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/FileUtils.java b/lib/DevApp/src/main/java/dev/utils/common/FileUtils.java new file mode 100644 index 0000000000..d37d524f90 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/FileUtils.java @@ -0,0 +1,2073 @@ +package dev.utils.common; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 文件操作工具类 + * @author Ttt + */ +public final class FileUtils { + + private FileUtils() { + } + + // 日志 TAG + private static final String TAG = FileUtils.class.getSimpleName(); + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + public static File getFile(final String filePath) { + return getFileByPath(filePath); + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @param fileName 文件名 + * @return 文件 {@link File} + */ + public static File getFile(final String filePath, final String fileName) { + return (filePath != null && fileName != null) ? new File(filePath, fileName) : null; + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + public static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 获取路径, 并且进行创建目录 + * @param filePath 保存目录 + * @param fileName 文件名 + * @return 文件 {@link File} + */ + public static File getFileCreateFolder(final String filePath, final String fileName) { + // 防止不存在目录文件, 自动创建 + createFolder(filePath); + // 返回处理过后的 File + return getFile(filePath, fileName); + } + + /** + * 获取路径, 并且进行创建目录 + * @param filePath 保存目录 + * @param fileName 文件名 + * @return 文件 {@link File} + */ + public static String getFilePathCreateFolder(final String filePath, final String fileName) { + // 防止不存在目录文件, 自动创建 + createFolder(filePath); + // 返回处理过后的 File + File file = getFile(filePath, fileName); + // 返回文件路径 + return getAbsolutePath(file); + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param dirPath 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean createFolder(final String dirPath) { + return createFolder(getFileByPath(dirPath)); + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param file 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean createFolder(final File file) { + if (file != null) { + try { + // 当这个文件夹不存在的时候则创建文件夹 + if (!file.exists()) { + // 允许创建多级目录 + return file.mkdirs(); + // 这个无法创建多级目录 + // rootFile.mkdir(); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "createFolder"); + } + } + return false; + } + + /** + * 创建文件夹目录 - 可以传入文件名 + * @param filePath 文件路径 + 文件名 + * @return {@code true} success, {@code false} fail + */ + public static boolean createFolderByPath(final String filePath) { + return createFolderByPath(getFileByPath(filePath)); + } + + /** + * 创建文件夹目录 - 可以传入文件名 + * @param file 文件 + * @return {@code true} success, {@code false} fail + */ + public static boolean createFolderByPath(final File file) { + // 创建文件夹 - 如果失败才创建 + if (file != null) { + if (file.exists()) { + return true; + } else if (!file.getParentFile().mkdirs()) { + return createFolder(file.getParent()); + } + } + return false; + } + + /** + * 创建多个文件夹, 如果不存在则创建 + * @param filePaths 文件路径数组 + */ + public static void createFolderByPaths(final String... filePaths) { + if (filePaths != null && filePaths.length != 0) { + for (int i = 0, len = filePaths.length; i < len; i++) { + createFolder(filePaths[i]); + } + } + } + + /** + * 创建多个文件夹, 如果不存在则创建 + * @param files 文件数组 + */ + public static void createFolderByPaths(final File... files) { + if (files != null && files.length != 0) { + for (int i = 0, len = files.length; i < len; i++) { + createFolder(files[i]); + } + } + } + + // = + + /** + * 判断目录是否存在, 不存在则判断是否创建成功 + * @param dirPath 目录路径 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + public static boolean createOrExistsDir(final String dirPath) { + return createOrExistsDir(getFileByPath(dirPath)); + } + + /** + * 判断目录是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + public static boolean createOrExistsDir(final File file) { + // 如果存在, 是目录则返回 true, 是文件则返回 false, 不存在则返回是否创建成功 + return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); + } + + /** + * 判断文件是否存在, 不存在则判断是否创建成功 + * @param filePath 文件路径 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + public static boolean createOrExistsFile(final String filePath) { + return createOrExistsFile(getFileByPath(filePath)); + } + + /** + * 判断文件是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + public static boolean createOrExistsFile(final File file) { + if (file == null) return false; + // 如果存在, 是文件则返回 true, 是目录则返回 false + if (file.exists()) return file.isFile(); + // 判断文件是否存在, 不存在则直接返回 + if (!createOrExistsDir(file.getParentFile())) return false; + try { + // 存在, 则返回新的路径 + return file.createNewFile(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "createOrExistsFile"); + return false; + } + } + + /** + * 判断文件是否存在, 存在则在创建之前删除 + * @param filePath 文件路径 + * @return {@code true} 创建成功, {@code false} 创建失败 + */ + public static boolean createFileByDeleteOldFile(final String filePath) { + return createFileByDeleteOldFile(getFileByPath(filePath)); + } + + /** + * 判断文件是否存在, 存在则在创建之前删除 + * @param file 文件 + * @return {@code true} 创建成功, {@code false} 创建失败 + */ + public static boolean createFileByDeleteOldFile(final File file) { + if (file == null) return false; + // 文件存在并且删除失败返回 false + if (file.exists() && !file.delete()) return false; + // 创建目录失败返回 false + if (!createOrExistsDir(file.getParentFile())) return false; + try { + return file.createNewFile(); + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "createFileByDeleteOldFile"); + return false; + } + } + + /** + * 获取文件路径 + * @param file 文件 + * @return 文件路径 + */ + public static String getPath(final File file) { + return file != null ? file.getPath() : null; + } + + /** + * 获取文件绝对路径 + * @param file 文件 + * @return 文件绝对路径 + */ + public static String getAbsolutePath(final File file) { + return file != null ? file.getAbsolutePath() : null; + } + + // = + + /** + * 获取文件名 + * @param file 文件 + * @return 文件名 + */ + public static String getName(final File file) { + return file != null ? file.getName() : null; + } + + /** + * 获取文件名 + * @param filePath 文件路径 + * @return 文件名 + */ + public static String getName(final String filePath) { + return getName(filePath, ""); + } + + /** + * 获取文件名 + * @param filePath 文件路径 + * @param defaultStr 默认字符串 + * @return 文件名, 如果文件路径为 null 时, 返回默认字符串 + */ + public static String getName(final String filePath, final String defaultStr) { + return isSpace(filePath) ? defaultStr : new File(filePath).getName(); + } + + /** + * 获取文件后缀名 ( 无 "." 单独后缀 ) + * @param file 文件 + * @return 文件后缀名 ( 无 "." 单独后缀 ) + */ + public static String getFileSuffix(final File file) { + return getFileSuffix(getAbsolutePath(file)); + } + + /** + * 获取文件后缀名 ( 无 "." 单独后缀 ) + * @param filePath 文件路径或文件名 + * @return 文件后缀名 ( 无 "." 单独后缀 ) + */ + public static String getFileSuffix(final String filePath) { + // 获取最后的索引 + int lastIndexOf; + // 判断是否存在 + if (filePath != null && (lastIndexOf = filePath.lastIndexOf('.')) != -1) { + String result = filePath.substring(lastIndexOf); + if (result.startsWith(".")) { + return result.substring(1); + } + return result; + } + return null; + } + + /** + * 获取文件名 ( 无后缀 ) + * @param file 文件 + * @return 文件名 ( 无后缀 ) + */ + public static String getFileNotSuffix(final File file) { + return getFileNotSuffix(getName(file)); + } + + /** + * 获取文件名 ( 无后缀 ) + * @param filePath 文件路径 + * @return 文件名 ( 无后缀 ) + */ + public static String getFileNotSuffixToPath(final String filePath) { + return getFileNotSuffix(getName(filePath)); + } + + /** + * 获取文件名 ( 无后缀 ) + * @param fileName 文件名 + * @return 文件名 ( 无后缀 ) + */ + public static String getFileNotSuffix(final String fileName) { + if (fileName != null) { + if (fileName.lastIndexOf('.') != -1) { + return fileName.substring(0, fileName.lastIndexOf('.')); + } else { + return fileName; + } + } + return null; + } + + /** + * 获取路径中的不带拓展名的文件名 + * @param file 文件 + * @return 不带拓展名的文件名 + */ + public static String getFileNameNoExtension(final File file) { + if (file == null) return null; + return getFileNameNoExtension(file.getPath()); + } + + /** + * 获取路径中的不带拓展名的文件名 + * @param filePath 文件路径 + * @return 不带拓展名的文件名 + */ + public static String getFileNameNoExtension(final String filePath) { + if (isSpace(filePath)) return filePath; + int lastPoi = filePath.lastIndexOf('.'); + int lastSep = filePath.lastIndexOf(File.separator); + if (lastSep == -1) { + return (lastPoi == -1 ? filePath : filePath.substring(0, lastPoi)); + } + if (lastPoi == -1 || lastSep > lastPoi) { + return filePath.substring(lastSep + 1); + } + return filePath.substring(lastSep + 1, lastPoi); + } + + /** + * 获取路径中的文件拓展名 + * @param file 文件 + * @return 文件拓展名 + */ + public static String getFileExtension(final File file) { + if (file == null) return null; + return getFileExtension(file.getPath()); + } + + /** + * 获取路径中的文件拓展名 + * @param filePath 文件路径 + * @return 文件拓展名 + */ + public static String getFileExtension(final String filePath) { + if (isSpace(filePath)) return filePath; + int lastPoi = filePath.lastIndexOf('.'); + int lastSep = filePath.lastIndexOf(File.separator); + if (lastPoi == -1 || lastSep >= lastPoi) return ""; + return filePath.substring(lastPoi + 1); + } + + // = + + /** + * 检查是否存在某个文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFileExists(final File file) { + return file != null && file.exists(); + } + + /** + * 检查是否存在某个文件 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFileExists(final String filePath) { + return isFileExists(getFileByPath(filePath)); + } + + /** + * 检查是否存在某个文件 + * @param filePath 文件路径 + * @param fileName 文件名 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFileExists(final String filePath, final String fileName) { + return filePath != null && fileName != null && new File(filePath, fileName).exists(); + } + + /** + * 判断是否文件 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFile(final String filePath) { + return isFile(getFileByPath(filePath)); + } + + /** + * 判断是否文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFile(final File file) { + return file != null && file.exists() && file.isFile(); + } + + /** + * 判断是否文件夹 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + public static boolean isDirectory(final String filePath) { + return isDirectory(getFileByPath(filePath)); + } + + /** + * 判断是否文件夹 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + public static boolean isDirectory(final File file) { + return file != null && file.exists() && file.isDirectory(); + } + + /** + * 判断是否隐藏文件 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + public static boolean isHidden(final String filePath) { + return isHidden(getFileByPath(filePath)); + } + + /** + * 判断是否隐藏文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + public static boolean isHidden(final File file) { + return file != null && file.exists() && file.isHidden(); + } + + // = + + /** + * 获取文件最后修改的毫秒时间戳 + * @param filePath 文件路径 + * @return 文件最后修改的毫秒时间戳 + */ + public static long getFileLastModified(final String filePath) { + return getFileLastModified(getFileByPath(filePath)); + } + + /** + * 获取文件最后修改的毫秒时间戳 + * @param file 文件 + * @return 文件最后修改的毫秒时间戳 + */ + public static long getFileLastModified(final File file) { + if (file == null) return 0L; + return file.lastModified(); + } + + /** + * 获取文件编码格式 + * @param filePath 文件路径 + * @return 文件编码格式 + */ + public static String getFileCharsetSimple(final String filePath) { + return getFileCharsetSimple(getFileByPath(filePath)); + } + + /** + * 获取文件编码格式 + * @param file 文件 + * @return 文件编码格式 + */ + public static String getFileCharsetSimple(final File file) { + if (file == null) return null; + int pos = 0; + InputStream is = null; + try { + is = new BufferedInputStream(new FileInputStream(file)); + pos = (is.read() << 8) + is.read(); + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "getFileCharsetSimple"); + } finally { + closeIOQuietly(is); + } + switch (pos) { + case 0xefbb: + return "UTF-8"; + case 0xfffe: + return "Unicode"; + case 0xfeff: + return "UTF-16BE"; + default: + return "GBK"; + } + } + + /** + * 获取文件行数 + * @param filePath 文件路径 + * @return 文件行数 + */ + public static int getFileLines(final String filePath) { + return getFileLines(getFileByPath(filePath)); + } + + /** + * 获取文件行数 ( 比 readLine 要快很多 ) + * @param file 文件 + * @return 文件行数 + */ + public static int getFileLines(final File file) { + if (file == null) return 0; + int lineCount = 1; + InputStream is = null; + try { + is = new BufferedInputStream(new FileInputStream(file)); + byte[] buffer = new byte[1024]; + int readChars; + if (NEW_LINE_STR.endsWith("\n")) { + while ((readChars = is.read(buffer, 0, 1024)) != -1) { + for (int i = 0; i < readChars; ++i) { + if (buffer[i] == '\n') ++lineCount; + } + } + } else { + while ((readChars = is.read(buffer, 0, 1024)) != -1) { + for (int i = 0; i < readChars; ++i) { + if (buffer[i] == '\r') ++lineCount; + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileLines"); + } finally { + closeIOQuietly(is); + } + return lineCount; + } + + // = + + /** + * 获取文件大小 + * @param filePath 文件路径 + * @return 文件大小 + */ + public static String getFileSize(final String filePath) { + return getFileSize(getFileByPath(filePath)); + } + + /** + * 获取文件大小 + * @param file 文件 + * @return 文件大小 + */ + public static String getFileSize(final File file) { + return formatByteMemorySize(getFileLength(file)); + } + + /** + * 获取目录大小 + * @param dirPath 目录路径 + * @return 文件大小 + */ + public static String getDirSize(final String dirPath) { + return getDirSize(getFileByPath(dirPath)); + } + + /** + * 获取目录大小 + * @param dir 目录 + * @return 文件大小 + */ + public static String getDirSize(final File dir) { + return formatByteMemorySize(getDirLength(dir)); + } + + /** + * 获取文件大小 + * @param filePath 文件路径 + * @return 文件大小 + */ + public static long getFileLength(final String filePath) { + return getFileLength(getFileByPath(filePath)); + } + + /** + * 获取文件大小 + * @param file 文件 + * @return 文件大小 + */ + public static long getFileLength(final File file) { + return file != null ? file.length() : 0L; + } + + /** + * 获取目录全部文件大小 + * @param dirPath 目录路径 + * @return 目录全部文件大小 + */ + public static long getDirLength(final String dirPath) { + return getDirLength(getFileByPath(dirPath)); + } + + /** + * 获取目录全部文件大小 + * @param dir 目录 + * @return 目录全部文件大小 + */ + public static long getDirLength(final File dir) { + if (!isDirectory(dir)) return 0; + long len = 0; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (file.isDirectory()) { + len += getDirLength(file); + } else { + len += file.length(); + } + } + } + return len; + } + + /** + * 获取文件大小 - 网络资源 + * @param httpUri 文件网络链接 + * @return 文件大小 + */ + public static long getFileLengthNetwork(final String httpUri) { + if (isSpace(httpUri)) return 0L; + boolean isURL = httpUri.toLowerCase().matches("http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?"); + if (isURL) { + try { + HttpURLConnection conn = (HttpURLConnection) new URL(httpUri).openConnection(); + conn.setRequestProperty("Accept-Encoding", "identity"); + conn.connect(); + if (conn.getResponseCode() == 200) { + return conn.getContentLength(); + } + return 0L; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileLengthNetwork"); + } + } + return 0L; + } + + /** + * 获取路径中的文件名 + * @param file 文件 + * @return 文件名 + */ + public static String getFileName(final File file) { + if (file == null) return null; + return getFileName(file.getPath()); + } + + /** + * 获取路径中的文件名 + * @param filePath 文件路径 + * @return 文件名 + */ + public static String getFileName(final String filePath) { + if (isSpace(filePath)) return filePath; + int lastSep = filePath.lastIndexOf(File.separator); + return lastSep == -1 ? filePath : filePath.substring(lastSep + 1); + } + + /** + * 获取路径中的最长目录地址 + * @param file 文件 + * @return 最长目录地址 + */ + public static String getDirName(final File file) { + if (file == null) return null; + return getDirName(file.getPath()); + } + + /** + * 获取全路径中的最长目录地址 + * @param filePath 文件路径 + * @return 最长目录地址 + */ + public static String getDirName(final String filePath) { + if (isSpace(filePath)) return filePath; + int lastSep = filePath.lastIndexOf(File.separator); + return lastSep == -1 ? "" : filePath.substring(0, lastSep + 1); + } + + // = + + /** + * 重命名文件 - 同个目录下, 修改文件名 + * @param filePath 文件路径 + * @param newFileName 文件新名称 + * @return {@code true} yes, {@code false} no + */ + public static boolean rename(final String filePath, final String newFileName) { + return rename(getFileByPath(filePath), newFileName); + } + + /** + * 重命名文件 - 同个目录下, 修改文件名 + * @param file 文件 + * @param newFileName 文件新名称 + * @return {@code true} yes, {@code false} no + */ + public static boolean rename(final File file, final String newFileName) { + // 文件为空返回 false + if (file == null) return false; + // 文件不存在返回 false + if (!file.exists()) return false; + // 如果文件名没有改变返回 true + if (newFileName.equals(file.getName())) return true; + // 拼接新的文件路径 + File newFile = new File(file.getParent() + File.separator + newFileName); + // 如果重命名的文件已存在返回 false + return !newFile.exists() && file.renameTo(newFile); + } + + // ================ + // = 文件大小处理 = + // ================ + + /** + * 传入文件路径, 返回对应的文件大小 + * @param filePath 文件路径 + * @return 文件大小转换字符串 + */ + public static String formatFileSize(final String filePath) { + File file = getFileByPath(filePath); + return formatFileSize(file != null ? file.length() : 0); + } + + /** + * 传入文件路径, 返回对应的文件大小 + * @param file 文件 + * @return 文件大小转换字符串 + */ + public static String formatFileSize(final File file) { + return formatFileSize(file != null ? file.length() : 0); + } + + /** + * 传入对应的文件大小, 返回转换后文件大小 + * @param fileSize 文件大小 + * @return 文件大小转换字符串 + */ + public static String formatFileSize(final double fileSize) { + // 转换文件大小 + DecimalFormat df = new DecimalFormat("#.00"); + String fileSizeStr; + if (fileSize <= 0) { + fileSizeStr = "0B"; + } else if (fileSize < 1024) { + fileSizeStr = df.format(fileSize) + "B"; + } else if (fileSize < 1048576) { + fileSizeStr = df.format(fileSize / 1024) + "KB"; + } else if (fileSize < 1073741824) { + fileSizeStr = df.format(fileSize / 1048576) + "MB"; + } else if (fileSize < 1099511627776d) { + fileSizeStr = df.format(fileSize / 1073741824) + "GB"; + } else { + fileSizeStr = df.format(fileSize / 1099511627776d) + "TB"; + } + return fileSizeStr; + } + + /** + * 字节数转合适内存大小 保留 3 位小数 (%.位数f) + * @param byteSize 字节数 + * @return 合适内存大小字符串 + */ + public static String formatByteMemorySize(final double byteSize) { + return formatByteMemorySize(3, byteSize); + } + + /** + * 字节数转合适内存大小 保留 number 位小数 (%.位数f) + * @param number 保留小数位数 + * @param byteSize 字节数 + * @return 合适内存大小字符串 + */ + public static String formatByteMemorySize(final int number, final double byteSize) { + if (byteSize < 0d) { + return "0B"; + } else if (byteSize < 1024d) { + return String.format("%." + number + "fB", byteSize); + } else if (byteSize < 1048576d) { + return String.format("%." + number + "fKB", byteSize / 1024d); + } else if (byteSize < 1073741824d) { + return String.format("%." + number + "fMB", byteSize / 1048576d); + } else if (byteSize < 1099511627776d) { + return String.format("%." + number + "fGB", byteSize / 1073741824d); + } else { + return String.format("%." + number + "fTB", byteSize / 1099511627776d); + } + } + + // ============ + // = 文件操作 = + // ============ + + /** + * 删除文件 + * @param filePath 文件路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean deleteFile(final String filePath) { + return deleteFile(getFileByPath(filePath)); + } + + /** + * 删除文件 + * @param file 文件 + * @return {@code true} success, {@code false} fail + */ + public static boolean deleteFile(final File file) { + // 文件存在, 并且不是目录文件, 则直接删除 + if (file != null && file.exists() && !file.isDirectory()) { + return file.delete(); + } + return false; + } + + /** + * 删除多个文件 + * @param filePaths 文件路径数组 + */ + public static void deleteFiles(final String... filePaths) { + if (filePaths != null && filePaths.length != 0) { + for (int i = 0, len = filePaths.length; i < len; i++) { + deleteFile(filePaths[i]); + } + } + } + + /** + * 删除多个文件 + * @param files 文件数组 + */ + public static void deleteFiles(final File... files) { + if (files != null && files.length != 0) { + for (int i = 0, len = files.length; i < len; i++) { + deleteFile(files[i]); + } + } + } + + // = + + /** + * 删除文件夹 + * @param filePath 文件路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean deleteFolder(final String filePath) { + return deleteFolder(getFileByPath(filePath)); + } + + /** + * 删除文件夹 + * @param file 文件 + * @return {@code true} success, {@code false} fail + */ + public static boolean deleteFolder(final File file) { + if (file != null) { + try { + // 文件存在, 并且不是目录文件, 则直接删除 + if (file.exists()) { + if (file.isDirectory()) { // 属于文件目录 + File[] files = file.listFiles(); + if (null == files || files.length == 0) { + file.delete(); + } + for (File f : files) { + deleteFolder(f.getPath()); + } + return file.delete(); + } else { // 属于文件 + return deleteFile(file); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "deleteFolder"); + } + } + return false; + } + + /** + * 保存文件 + * @param file 保存文件 + * @param data 保存内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveFile(final File file, final byte[] data) { + if (file != null && data != null) { + try { + // 防止文件没创建 + createFolder(getDirName(file)); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + BufferedOutputStream bos = new BufferedOutputStream(fos); + bos.write(data); + bos.close(); + fos.close(); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 保存文件 + * @param filePath 保存路径 + * @param fileName 文件名. 后缀 + * @param data 保存内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveFile(final String filePath, final String fileName, final byte[] data) { + if (filePath != null && fileName != null && data != null) { + try { + // 防止文件没创建 + createFolder(filePath); + // 保存路径 + File file = new File(filePath, fileName); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + BufferedOutputStream bos = new BufferedOutputStream(fos); + bos.write(data); + bos.close(); + fos.close(); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 保存文件 + * @param filePath 保存路径 + * @param fileName 文件名. 后缀 + * @param content 保存内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveFile(final String filePath, final String fileName, final String content) { + if (filePath != null && fileName != null && content != null) { + try { + // 防止文件没创建 + createFolder(filePath); + // 保存路径 + File file = new File(filePath, fileName); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + fos.write(content.getBytes()); + fos.close(); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 保存文件 + * @param filePath 保存路径 + * @param fileName 文件名. 后缀 + * @param content 保存内容 + * @param coding 编码格式 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveFile(final String filePath, final String fileName, final String content, final String coding) { + if (filePath != null && fileName != null && content != null) { + try { + // 防止文件没创建 + createFolder(filePath); + // 保存路径 + File file = new File(filePath, fileName); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + Writer out; + if (coding != null) { + out = new OutputStreamWriter(fos, coding); + } else { + out = new OutputStreamWriter(fos); + } + out.write(content); + out.close(); + fos.close(); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 追加文件 ( 使用 FileWriter) + * @param filePath 文件路径 + * @param content 追加内容 + */ + public static void appendFile(final String filePath, final String content) { + if (filePath == null || content == null) return; + File file = new File(filePath); + // 如果文件不存在, 则跳过 + if (!file.exists()) return; + FileWriter writer = null; + try { + // 打开一个写文件器, 构造函数中的第二个参数 true 表示以追加形式写文件 + writer = new FileWriter(file, true); + writer.write(content); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "appendFile"); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + } + } + } + } + + // = + + /** + * 读取文件 + * @param filePath 文件路径 + * @return 文件内容 byte[] + */ + public static byte[] readFileBytes(final String filePath) { + return readFileBytes(getFileByPath(filePath)); + } + + /** + * 读取文件 + * @param file 文件 + * @return 文件内容 byte[] + */ + public static byte[] readFileBytes(final File file) { + if (file != null && file.exists()) { + try { + FileInputStream fis = new FileInputStream(file); + int length = fis.available(); + byte[] buffer = new byte[length]; + fis.read(buffer); + fis.close(); + return buffer; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "readFileBytes"); + } + } + return null; + } + + /** + * 读取文件 + * @param inputStream {@link InputStream} + * @return 文件内容 byte[] + */ + public static byte[] readFileBytes(final InputStream inputStream) { + if (inputStream != null) { + try { + int length = inputStream.available(); + byte[] buffer = new byte[length]; + inputStream.read(buffer); + inputStream.close(); + return buffer; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "readFileBytes"); + } + } + return null; + } + + /** + * 读取文件 + * @param filePath 文件路径 + * @return 文件内容字符串 + */ + public static String readFile(final String filePath) { + return readFile(getFileByPath(filePath)); + } + + /** + * 读取文件 + * @param file 文件 + * @return 文件内容字符串 + */ + public static String readFile(final File file) { + if (file != null && file.exists()) { + try { + return readFile(new FileInputStream(file)); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "readFile"); + } + } + return null; + } + + /** + * 读取文件 + * @param inputStream {@link InputStream} new FileInputStream(path) + * @return 文件内容字符串 + */ + public static String readFile(final InputStream inputStream) { + if (inputStream != null) { + try { + InputStreamReader isr = new InputStreamReader(inputStream); + BufferedReader br = new BufferedReader(isr); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + builder.append(line); + } + isr.close(); + br.close(); + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "readFile"); + } + } + return null; + } + + /** + * 读取文件 + * @param inputStream {@link InputStream} new FileInputStream(path) + * @param encode 编码格式 + * @return 文件内容字符串 + */ + public static String readFile(final InputStream inputStream, final String encode) { + if (inputStream != null) { + try { + InputStreamReader isr = null; + if (encode != null) { + new InputStreamReader(inputStream, encode); + } else { + new InputStreamReader(inputStream); + } + BufferedReader br = new BufferedReader(isr); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + builder.append(line); + } + isr.close(); + br.close(); + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "readFile"); + } + } + return null; + } + + // = + + /** + * 复制单个文件 + * @param inputStream 文件流 ( 被复制 ) + * @param destFilePath 目标文件地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + public static boolean copyFile(final InputStream inputStream, final String destFilePath, final boolean overlay) { + if (inputStream == null || destFilePath == null) { + return false; + } + File destFile = new File(destFilePath); + // 如果属于文件夹则跳过 + if (destFile.isDirectory()) { + return false; + } + if (destFile.exists()) { + // 如果目标文件存在并允许覆盖 + if (overlay) { + // 删除已经存在的目标文件, 无论目标文件是目录还是单个文件 + destFile.delete(); + } + } else { + // 如果目标文件所在目录不存在, 则创建目录 + if (!destFile.getParentFile().exists()) { + // 目标文件所在目录不存在 + if (!destFile.getParentFile().mkdirs()) { + // 复制文件失败: 创建目标文件所在目录失败 + return false; + } + } + } + // 复制文件 + int byteread = 0; // 读取的字节数 + InputStream is = inputStream; + OutputStream os = null; + try { + os = new FileOutputStream(destFile); + byte[] buffer = new byte[1024]; + while ((byteread = is.read(buffer)) != -1) { + os.write(buffer, 0, byteread); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "copyFile"); + return false; + } finally { + try { + if (os != null) + os.close(); + if (is != null) + is.close(); + } catch (IOException e) { + } + } + } + + /** + * 复制单个文件 + * @param srcFilePath 待复制的文件地址 + * @param destFilePath 目标文件地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + public static boolean copyFile(final String srcFilePath, final String destFilePath, final boolean overlay) { + if (srcFilePath == null || destFilePath == null) { + return false; + } + File srcFile = new File(srcFilePath); + // 判断源文件是否存在 + if (!srcFile.exists()) { + return false; + } else if (!srcFile.isFile()) { // srcFile.isDirectory(); + return false; + } + // 判断目标文件是否存在 + File destFile = new File(destFilePath); + // 如果属于文件夹则跳过 + if (destFile.isDirectory()) { + return false; + } + if (destFile.exists()) { + // 如果目标文件存在并允许覆盖 + if (overlay) { + // 删除已经存在的目标文件, 无论目标文件是目录还是单个文件 + new File(destFilePath).delete(); + } + } else { + // 如果目标文件所在目录不存在, 则创建目录 + if (!destFile.getParentFile().exists()) { + // 目标文件所在目录不存在 + if (!destFile.getParentFile().mkdirs()) { + // 复制文件失败: 创建目标文件所在目录失败 + return false; + } + } + } + // 复制文件 + int byteread = 0; // 读取的字节数 + InputStream is = null; + OutputStream os = null; + try { + is = new FileInputStream(srcFile); + os = new FileOutputStream(destFile); + byte[] buffer = new byte[1024]; + while ((byteread = is.read(buffer)) != -1) { + os.write(buffer, 0, byteread); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "copyFile"); + return false; + } finally { + try { + if (os != null) + os.close(); + if (is != null) + is.close(); + } catch (IOException e) { + } + } + } + + /** + * 复制文件夹 + * @param srcFolderPath 待复制的文件夹地址 + * @param destFolderPath 目标文件夹地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + public static boolean copyFolder(final String srcFolderPath, final String destFolderPath, final boolean overlay) { + return copyFolder(srcFolderPath, destFolderPath, srcFolderPath, overlay); + } + + /** + * 复制文件夹 + * @param srcFolderPath 待复制的文件夹地址 + * @param destFolderPath 目标文件夹地址 + * @param sourcePath 源文件地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + private static boolean copyFolder(final String srcFolderPath, final String destFolderPath, final String sourcePath, final boolean overlay) { + if (srcFolderPath == null || destFolderPath == null || sourcePath == null) { + return false; + } + File srcFile = new File(srcFolderPath); + // 判断源文件是否存在 + if (!srcFile.exists()) { + return false; + } else if (!srcFile.isDirectory()) { // 不属于文件夹则跳过 + return false; + } + // 判断目标文件是否存在 + File destFile = new File(destFolderPath); + // 如果文件夹没创建, 则创建 + if (!destFile.exists()) { + // 允许创建多级目录 + destFile.mkdirs(); + } + // 判断是否属于文件夹 + if (!destFile.isDirectory()) { // 不属于文件夹则跳过 + return false; + } + // 判断是否存在 + if (srcFile.exists()) { + // 获取文件路径 + File[] files = srcFile.listFiles(); + // 防止不存在文件 + if (files != null && files.length != 0) { + // 进行遍历 + for (File file : files) { + // 文件存在才进行处理 + if (file.exists()) { + // 属于文件夹 + if (file.isDirectory()) { + copyFolder(file.getAbsolutePath(), destFolderPath, sourcePath, overlay); + } else { // 属于文件 + // 复制的文件地址 + String filePath = file.getAbsolutePath(); + // 获取源文件地址 - 并且进行判断 + String dealSource = new File(sourcePath).getAbsolutePath(); + // 属于最前才进行处理 + if (filePath.indexOf(dealSource) == 0) { + // 获取处理后的地址 + dealSource = filePath.substring(dealSource.length()); + // 获取需要复制保存的地址 + String savePath = new File(destFolderPath, dealSource).getAbsolutePath(); + // 进行复制文件 + boolean isResult = copyFile(filePath, savePath, overlay); + } + } + } + } + } + } + return true; + } + + // = + + /** + * 移动 ( 剪切 ) 文件 + * @param srcFilePath 待移动的文件地址 + * @param destFilePath 目标文件地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + public static boolean moveFile(final String srcFilePath, final String destFilePath, final boolean overlay) { + // 复制文件 + if (copyFile(srcFilePath, destFilePath, overlay)) { + // 删除文件 + return deleteFile(srcFilePath); + } + return false; + } + + /** + * 移动 ( 剪切 ) 文件夹 + * @param srcFilePath 待移动的文件夹地址 + * @param destFilePath 目标文件夹地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + public static boolean moveFolder(final String srcFilePath, final String destFilePath, final boolean overlay) { + // 复制文件夹 + if (copyFolder(srcFilePath, destFilePath, overlay)) { + // 删除文件夹 + return deleteFolder(srcFilePath); + } + return false; + } + + // ============= + // = FileUtils = + // ============= + + /** + * 复制或移动目录 + * @param srcDirPath 源目录路径 + * @param destDirPath 目标目录路径 + * @param listener 是否覆盖监听器 + * @param isMove 是否移动 + * @return {@code true} 复制或移动成功, {@code false} 复制或移动失败 + */ + public static boolean copyOrMoveDir(final String srcDirPath, final String destDirPath, final OnReplaceListener listener, final boolean isMove) { + return copyOrMoveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener, isMove); + } + + /** + * 复制或移动目录 + * @param srcDir 源目录 + * @param destDir 目标目录 + * @param listener 是否覆盖监听器 + * @param isMove 是否移动 + * @return {@code true} 复制或移动成功, {@code false} 复制或移动失败 + */ + public static boolean copyOrMoveDir(final File srcDir, final File destDir, final OnReplaceListener listener, final boolean isMove) { + if (srcDir == null || destDir == null || listener == null) return false; + // 为防止以上这种情况出现出现误判, 须分别在后面加个路径分隔符 + String srcPath = srcDir.getPath() + File.separator; + String destPath = destDir.getPath() + File.separator; + if (destPath.contains(srcPath)) return false; + // 源文件不存在或者不是目录则返回 false + if (!srcDir.exists() || !srcDir.isDirectory()) return false; + if (destDir.exists()) { + if (listener.onReplace()) { // 需要覆盖则删除旧目录 + if (!deleteAllInDir(destDir)) { // 删除文件失败的话返回 false + return false; + } + } else { // 不需要覆盖直接返回即可 true + return true; + } + } + // 目标目录不存在返回 false + if (!createOrExistsDir(destDir)) return false; + File[] files = srcDir.listFiles(); + for (File file : files) { + File oneDestFile = new File(destPath + file.getName()); + if (file.isFile()) { + // 如果操作失败返回 false + if (!copyOrMoveFile(file, oneDestFile, listener, isMove)) return false; + } else if (file.isDirectory()) { + // 如果操作失败返回 false + if (!copyOrMoveDir(file, oneDestFile, listener, isMove)) return false; + } + } + return !isMove || deleteDir(srcDir); + } + + /** + * 复制或移动文件 + * @param srcFilePath 源文件路径 + * @param destFilePath 目标文件路径 + * @param listener 是否覆盖监听器 + * @param isMove 是否移动 + * @return {@code true} 复制或移动成功, {@code false} 复制或移动失败 + */ + public static boolean copyOrMoveFile(final String srcFilePath, final String destFilePath, final OnReplaceListener listener, final boolean isMove) { + return copyOrMoveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener, isMove); + } + + /** + * 复制或移动文件 + * @param srcFile 源文件 + * @param destFile 目标文件 + * @param listener 是否覆盖监听器 + * @param isMove 是否移动 + * @return {@code true} 复制或移动成功, {@code false} 复制或移动失败 + */ + public static boolean copyOrMoveFile(final File srcFile, final File destFile, final OnReplaceListener listener, final boolean isMove) { + if (srcFile == null || destFile == null || listener == null) return false; + // 如果源文件和目标文件相同则返回 false + if (srcFile.equals(destFile)) return false; + // 源文件不存在或者不是文件则返回 false + if (!srcFile.exists() || !srcFile.isFile()) return false; + if (destFile.exists()) { // 目标文件存在 + if (listener.onReplace()) { // 需要覆盖则删除旧文件 + if (!destFile.delete()) { // 删除文件失败的话返回 false + return false; + } + } else { // 不需要覆盖直接返回即可 true + return true; + } + } + // 目标目录不存在返回 false + if (!createOrExistsDir(destFile.getParentFile())) return false; + try { + return writeFileFromIS(destFile, new FileInputStream(srcFile), false) && !(isMove && !deleteFile(srcFile)); + } catch (FileNotFoundException e) { + JCLogUtils.eTag(TAG, e, "copyOrMoveFile"); + return false; + } + } + + /** + * 复制目录 + * @param srcDirPath 源目录路径 + * @param destDirPath 目标目录路径 + * @param listener 是否覆盖监听器 + * @return {@code true} 复制成功, {@code false} 复制失败 + */ + public static boolean copyDir(final String srcDirPath, final String destDirPath, final OnReplaceListener listener) { + return copyDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener); + } + + /** + * 复制目录 + * @param srcDir 源目录 + * @param destDir 目标目录 + * @param listener 是否覆盖监听器 + * @return {@code true} 复制成功, {@code false} 复制失败 + */ + public static boolean copyDir(final File srcDir, final File destDir, final OnReplaceListener listener) { + return copyOrMoveDir(srcDir, destDir, listener, false); + } + + /** + * 复制文件 + * @param srcFilePath 源文件路径 + * @param destFilePath 目标文件路径 + * @param listener 是否覆盖监听器 + * @return {@code true} 复制成功, {@code false} 复制失败 + */ + public static boolean copyFile(final String srcFilePath, final String destFilePath, final OnReplaceListener listener) { + return copyFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener); + } + + /** + * 复制文件 + * @param srcFile 源文件 + * @param destFile 目标文件 + * @param listener 是否覆盖监听器 + * @return {@code true} 复制成功, {@code false} 复制失败 + */ + public static boolean copyFile(final File srcFile, final File destFile, final OnReplaceListener listener) { + return copyOrMoveFile(srcFile, destFile, listener, false); + } + + /** + * 移动目录 + * @param srcDirPath 源目录路径 + * @param destDirPath 目标目录路径 + * @param listener 是否覆盖监听器 + * @return {@code true} 移动成功, {@code false} 移动失败 + */ + public static boolean moveDir(final String srcDirPath, final String destDirPath, final OnReplaceListener listener) { + return moveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener); + } + + /** + * 移动目录 + * @param srcDir 源目录 + * @param destDir 目标目录 + * @param listener 是否覆盖监听器 + * @return {@code true} 移动成功, {@code false} 移动失败 + */ + public static boolean moveDir(final File srcDir, final File destDir, final OnReplaceListener listener) { + return copyOrMoveDir(srcDir, destDir, listener, true); + } + + /** + * 移动文件 + * @param srcFilePath 源文件路径 + * @param destFilePath 目标文件路径 + * @param listener 是否覆盖监听器 + * @return {@code true} 移动成功, {@code false} 移动失败 + */ + public static boolean moveFile(final String srcFilePath, final String destFilePath, final OnReplaceListener listener) { + return moveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener); + } + + /** + * 移动文件 + * @param srcFile 源文件 + * @param destFile 目标文件 + * @param listener 是否覆盖监听器 + * @return {@code true} 移动成功, {@code false} 移动失败 + */ + public static boolean moveFile(final File srcFile, final File destFile, final OnReplaceListener listener) { + return copyOrMoveFile(srcFile, destFile, listener, true); + } + + /** + * 删除目录 + * @param dirPath 目录路径 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteDir(final String dirPath) { + return deleteDir(getFileByPath(dirPath)); + } + + /** + * 删除目录 + * @param dir 目录 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteDir(final File dir) { + if (dir == null) return false; + // dir doesn't exist then return true + if (!dir.exists()) return true; + // dir isn't a directory then return false + if (!dir.isDirectory()) return false; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (file.isFile()) { + if (!file.delete()) return false; + } else if (file.isDirectory()) { + if (!deleteDir(file)) return false; + } + } + } + return dir.delete(); + } + + /** + * 删除目录下所有东西 + * @param dirPath 目录路径 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteAllInDir(final String dirPath) { + return deleteAllInDir(getFileByPath(dirPath)); + } + + /** + * 删除目录下所有东西 + * @param dir 目录 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteAllInDir(final File dir) { + return deleteFilesInDirWithFilter(dir, new FileFilter() { + @Override + public boolean accept(File pathname) { + return true; + } + }); + } + + /** + * 删除目录下所有文件 + * @param dirPath 目录路径 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteFilesInDir(final String dirPath) { + return deleteFilesInDir(getFileByPath(dirPath)); + } + + /** + * 删除目录下所有文件 + * @param dir 目录 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteFilesInDir(final File dir) { + return deleteFilesInDirWithFilter(dir, new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.isFile(); + } + }); + } + + /** + * 删除目录下所有过滤的文件 + * @param dirPath 目录路径 + * @param filter 过滤器 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteFilesInDirWithFilter(final String dirPath, final FileFilter filter) { + return deleteFilesInDirWithFilter(getFileByPath(dirPath), filter); + } + + /** + * 删除目录下所有过滤的文件 + * @param dir 目录 + * @param filter 过滤器 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteFilesInDirWithFilter(final File dir, final FileFilter filter) { + if (filter == null) return false; + // dir is null then return false + if (dir == null) return false; + // dir doesn't exist then return true + if (!dir.exists()) return true; + // dir isn't a directory then return false + if (!dir.isDirectory()) return false; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (filter.accept(file)) { + if (file.isFile()) { + if (!file.delete()) return false; + } else if (file.isDirectory()) { + if (!deleteDir(file)) return false; + } + } + } + } + return true; + } + + /** + * 获取目录下所有文件 - 不递归进子目录 + * @param dirPath 目录路径 + * @return 文件链表 + */ + public static List listFilesInDir(final String dirPath) { + return listFilesInDir(dirPath, false); + } + + /** + * 获取目录下所有文件 - 不递归进子目录 + * @param dir 目录 + * @return 文件链表 + */ + public static List listFilesInDir(final File dir) { + return listFilesInDir(dir, false); + } + + /** + * 获取目录下所有文件 + * @param dirPath 目录路径 + * @param isRecursive 是否递归进子目录 + * @return 文件链表 + */ + public static List listFilesInDir(final String dirPath, final boolean isRecursive) { + return listFilesInDir(getFileByPath(dirPath), isRecursive); + } + + /** + * 获取目录下所有文件 + * @param dir 目录 + * @param isRecursive 是否递归进子目录 + * @return 文件链表 + */ + public static List listFilesInDir(final File dir, final boolean isRecursive) { + return listFilesInDirWithFilter(dir, new FileFilter() { + @Override + public boolean accept(File pathname) { + return true; + } + }, isRecursive); + } + + /** + * 获取目录下所有过滤的文件 - 不递归进子目录 + * @param dirPath 目录路径 + * @param filter 过滤器 + * @return 文件链表 + */ + public static List listFilesInDirWithFilter(final String dirPath, final FileFilter filter) { + return listFilesInDirWithFilter(getFileByPath(dirPath), filter, false); + } + + /** + * 获取目录下所有过滤的文件 - 不递归进子目录 + * @param dir 目录 + * @param filter 过滤器 + * @return 文件链表 + */ + public static List listFilesInDirWithFilter(final File dir, final FileFilter filter) { + return listFilesInDirWithFilter(dir, filter, false); + } + + /** + * 获取目录下所有过滤的文件 + * @param dirPath 目录路径 + * @param filter 过滤器 + * @param isRecursive 是否递归进子目录 + * @return 文件链表 + */ + public static List listFilesInDirWithFilter(final String dirPath, final FileFilter filter, final boolean isRecursive) { + return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive); + } + + /** + * 获取目录下所有过滤的文件 + * @param dir 目录 + * @param filter 过滤器 + * @param isRecursive 是否递归进子目录 + * @return 文件链表 + */ + public static List listFilesInDirWithFilter(final File dir, final FileFilter filter, final boolean isRecursive) { + if (!isDirectory(dir) || filter == null) return null; + List list = new ArrayList<>(); + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (filter.accept(file)) { + list.add(file); + } + if (isRecursive && file.isDirectory()) { + // noinspection ConstantConditions + list.addAll(listFilesInDirWithFilter(file, filter, true)); + } + } + } + return list; + } + + /** + * detail: 覆盖 / 替换事件 + * @author Ttt + */ + public interface OnReplaceListener { + + /** + * 是否覆盖 / 替换文件 + * @return {@code true} yes, {@code false} no + */ + boolean onReplace(); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============ + // = MD5Utils = + // ============ + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值 + */ + public static byte[] getFileMD5(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileMD5(file); + } + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String getFileMD5ToHexString(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileMD5ToHexString(file); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String getFileMD5ToHexString(final File file) { + return toHexString(getFileMD5(file)); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值 byte[] + */ + public static byte[] getFileMD5(final File file) { + if (file == null) return null; + DigestInputStream dis = null; + try { + FileInputStream fis = new FileInputStream(file); + MessageDigest digest = MessageDigest.getInstance("MD5"); + dis = new DigestInputStream(fis, digest); + byte[] buffer = new byte[256 * 1024]; + while (true) { + if (!(dis.read(buffer) > 0)) break; + } + digest = dis.getMessageDigest(); + return digest.digest(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileMD5"); + return null; + } finally { + if (dis != null) { + try { + dis.close(); + } catch (Exception e) { + } + } + } + } + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制 String + */ + private static String toHexString(final byte[] data) { + return toHexString(data, HEX_DIGITS); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // ============== + // = CloseUtils = + // ============== + + /** + * 安静关闭 IO + * @param closeables Closeable[] + */ + private static void closeIOQuietly(final Closeable... closeables) { + if (closeables == null) return; + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception ignore) { + } + } + } + } + + // =============== + // = FileIOUtils = + // =============== + + // 缓存大小 + private static final int sBufferSize = 8192; + + /** + * 通过输入流写入文件 + * @param file 文件 + * @param inputStream {@link InputStream} + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + private static boolean writeFileFromIS(final File file, final InputStream inputStream, final boolean append) { + if (!createOrExistsFile(file) || inputStream == null) return false; + OutputStream os = null; + try { + os = new BufferedOutputStream(new FileOutputStream(file, append)); + byte[] data = new byte[sBufferSize]; + int len; + while ((len = inputStream.read(data, 0, sBufferSize)) != -1) { + os.write(data, 0, len); + } + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromIS"); + return false; + } finally { + closeIOQuietly(inputStream, os); + } + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/HttpParamsUtils.java b/lib/DevApp/src/main/java/dev/utils/common/HttpParamsUtils.java new file mode 100644 index 0000000000..25fd3c7b30 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/HttpParamsUtils.java @@ -0,0 +1,229 @@ +package dev.utils.common; + +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: Http 参数工具类 + * @author Ttt + */ +public final class HttpParamsUtils { + + private HttpParamsUtils() { + } + + // 日志 TAG + private static final String TAG = HttpParamsUtils.class.getSimpleName(); + + /** + * 拆分参数 + * @param params 请求参数字符串 + * @return 拆分后的参数 Map + */ + public static Map splitParams(final String params) { + return splitParams(params, false); + } + + /** + * 拆分参数 + * @param params 请求参数字符串 + * @param urlEncode 是否需要 URL 编码 + * @return 拆分后的参数 Map + */ + public static Map splitParams(final String params, final boolean urlEncode) { + Map mapParams = new HashMap<>(); + if (params != null) { + // 拆分数据 + String[] keyValues = params.split("&"); + // 数据长度 + int valLength = 0; + // 进行循环遍历 + for (String val : keyValues) { + // 数据不为 null + if (val != null && (valLength = val.length()) != 0) { + // 获取首位 = 索引 + int indexOf = val.indexOf('='); + // 不存在则不处理 + if (indexOf != -1) { + // 获取 key + String key = val.substring(0, indexOf); + // 获取 value + String value = null; + // 防止资源浪费 + if (indexOf + 1 == valLength) { + value = ""; + } else { + value = val.substring(indexOf + 1, valLength); + } + // 判断是否编码 + if (urlEncode) { + mapParams.put(key, toUrlEncode(value)); + } else { + mapParams.put(key, value); + } + } + } + } + } + return mapParams; + } + + // = + + /** + * 拼接请求参数 - value(String) + * @param mapParams Map 请求参数 + * @return 拼接后的参数 + */ + public static String joinParams(final Map mapParams) { + return joinParams(mapParams, false); + } + + /** + * 拼接请求参数 - value(String) + * @param mapParams Map 请求参数 + * @param urlEncode 是否需要 URL 编码 + * @return 拼接后的参数 + */ + public static String joinParams(final Map mapParams, final boolean urlEncode) { + if (mapParams != null) { + int index = 0; + // = + StringBuilder builder = new StringBuilder(); + Iterator> iterator = mapParams.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + // = + if (index > 0) builder.append('&'); + builder.append(entry.getKey()); + builder.append('='); + builder.append(urlEncode ? toUrlEncode(entry.getValue()) : entry.getValue()); + index++; + } + return builder.toString(); + } + return null; + } + + // = + + /** + * 拼接请求参数 - value(Object) + * @param mapParams Map 请求参数 + * @return 拼接后的参数 + */ + public static String joinParamsObj(final Map mapParams) { + return joinParamsObj(mapParams, false); + } + + /** + * 拼接请求参数 - value(Object) + * @param mapParams Map 请求参数 + * @param urlEncode 是否需要 URL 编码 + * @return 拼接后的参数 + */ + public static String joinParamsObj(final Map mapParams, final boolean urlEncode) { + if (mapParams != null) { + int index = 0; + // = + StringBuilder builder = new StringBuilder(); + Iterator> iterator = mapParams.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + // = + if (index > 0) builder.append('&'); + builder.append(entry.getKey()); + builder.append('='); + if (urlEncode) { + if (entry.getValue() instanceof String) { + builder.append(toUrlEncode((String) entry.getValue())); + } + } else { + builder.append(entry.getValue()); + } + index++; + } + return builder.toString(); + } + return null; + } + + // ============================================ + // = 拼接成, 模拟 JavaScript 传递对象数组格式 = + // ============================================ + + /** + * 进行转换对象处理 ( 请求发送对象 ) + * @param mapParams Map 请求参数 + * @param objStr 数组名 + * @param key 数组 key + * @param value 数组 [key] 保存值 + */ + public static void toConvertObjToMS(final Map mapParams, final String objStr, final String key, final String value) { + if (mapParams != null) { + String data = null; + try { + data = URLEncoder.encode(value, "UTF-8"); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toConvertObjToMS"); + } + mapParams.put(objStr + "[" + key + "]", data); + } + } + + /** + * 进行转换对象处理 ( 请求发送对象 ) + * @param mapParams Map 请求参数 + * @param objStr 数组名 + * @param key 数组 key + * @param value 数组 [key] 保存值 + */ + public static void toConvertObjToMO(final Map mapParams, final String objStr, final String key, final Object value) { + if (mapParams != null) { + Object data = null; + try { + data = URLEncoder.encode(value.toString(), "UTF-8"); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toConvertObjToMO"); + } + mapParams.put(objStr + "[" + key + "]", data); + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 进行 URL 编码, 默认 UTF-8 + * @param str 待处理字符串 + * @return UTF-8 编码格式 URL 编码后的字符串 + */ + public static String toUrlEncode(final String str) { + return toUrlEncode(str, "UTF-8"); + } + + /** + * 进行 URL 编码 + * @param str 待处理字符串 + * @param enc 编码格式 + * @return 指定编码格式 URL 编码后的字符串 + */ + public static String toUrlEncode(final String str, final String enc) { + if (str == null || enc == null) return null; + try { + return URLEncoder.encode(str, enc); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toUrlEncode"); + } + return null; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/HttpURLConnectionUtils.java b/lib/DevApp/src/main/java/dev/utils/common/HttpURLConnectionUtils.java similarity index 61% rename from DevLibUtils/src/main/java/dev/utils/common/HttpURLConnectionUtils.java rename to lib/DevApp/src/main/java/dev/utils/common/HttpURLConnectionUtils.java index 270ee578df..b3b2641929 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/HttpURLConnectionUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/common/HttpURLConnectionUtils.java @@ -5,7 +5,6 @@ import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -13,39 +12,42 @@ /** * detail: HttpURLConnection 网络工具类 - * Created by Ttt + * @author Ttt */ public final class HttpURLConnectionUtils { - private HttpURLConnectionUtils(){ + private HttpURLConnectionUtils() { } - // 日志TAG + // 日志 TAG private static final String TAG = HttpURLConnectionUtils.class.getSimpleName(); - // 请求超时时间 private static final int TIMEOUT_IN_MILLIONS = 5000; - // 请求回调 + /** + * detail: 请求回调 + * @author Ttt + */ public interface CallBack { /** * 请求响应回调 - * @param result + * @param result 请求结果 + * @param response 请求响应时间 */ - void onResponse(String result); + void onResponse(String result, long response); /** * 请求失败 - * @param e + * @param e 失败异常 */ void onFail(Exception e); } /** - * 异步的Get请求 - * @param urlStr - * @param callBack + * 异步的 Get 请求 + * @param urlStr 请求地址 + * @param callBack 请求回调接口 */ public static void doGetAsyn(final String urlStr, final CallBack callBack) { new Thread() { @@ -60,10 +62,10 @@ public void run() { } /** - * 异步的Post请求 - * @param urlStr - * @param params - * @param callBack + * 异步的 Post 请求 + * @param urlStr 请求地址 + * @param params 请求参数 + * @param callBack 请求回调接口 */ public static void doPostAsyn(final String urlStr, final String params, final CallBack callBack) { new Thread() { @@ -79,18 +81,17 @@ public void run() { /** * 发送请求 - * @param method - * @param urlStr - * @param headers - * @param params - * @param callBack - * @return + * @param method 请求方法 + * @param urlStr 请求地址字符串 + * @param headers 请求头信息 + * @param params 请求参数 + * @param callBack 请求回调接口 */ - public static void request(final String method, final String urlStr, final HashMap headers, final String params, final CallBack callBack) { + public static void request(final String method, final String urlStr, final Map headers, final String params, final CallBack callBack) { // 获取连接对象 HttpURLConnection connection = null; - InputStream inputStream = null; - ByteArrayOutputStream bout = null; + InputStream is = null; + ByteArrayOutputStream baos = null; try { // 请求路径 URL url = new URL(urlStr); @@ -99,122 +100,123 @@ public static void request(final String method, final String urlStr, final HashM // 设置请求方法 connection.setRequestMethod(method); // 设置请求头信息 - if (headers != null){ + if (headers != null) { Iterator> iterator = headers.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); connection.setRequestProperty(entry.getKey(), entry.getValue()); } } - // 获取请求时间 - JCLogUtils.dTag(TAG, "response time: " + connection.getDate()); // 判断是否需要写入数据 - if(params != null && params.length() != 0) { + if (params != null && params.length() != 0) { // 允许写入 connection.setDoInput(true); - // 设置是否向connection输出,如果是post请求,参数要放在http正文内,因此需要设为true + // 设置是否向 connection 输出, 如果是 post 请求, 参数要放在 http 正文内, 因此需要设为 true connection.setDoOutput(true); - // Post 请求不能使用缓存 + // post 请求不能使用缓存 connection.setUseCaches(false); // 写入数据 - OutputStream ot = connection.getOutputStream(); - ot.write(params.getBytes()); - ot.flush(); - ot.close(); + OutputStream os = connection.getOutputStream(); + os.write(params.getBytes()); + os.flush(); + os.close(); } // 单位是毫秒 connection.setConnectTimeout(TIMEOUT_IN_MILLIONS); // 设置连接超时 connection.setReadTimeout(TIMEOUT_IN_MILLIONS); // 设置读取超时 // 获取请求状态码 int responseCode = connection.getResponseCode(); - // 判断请求码是否是200 + // 判断请求码是否是 200 if (responseCode >= 200 && responseCode < 300) { // 输入流 - inputStream = connection.getInputStream(); - bout = new ByteArrayOutputStream(); + is = connection.getInputStream(); + baos = new ByteArrayOutputStream(); // 设置缓存流大小 byte[] buffer = new byte[1024]; int len = 0; - while (((len = inputStream.read(buffer)) != -1)) { - bout.write(buffer, 0, len); + while (((len = is.read(buffer)) != -1)) { + baos.write(buffer, 0, len); } // 获取请求结果 - String result = new String(bout.toByteArray()); + String result = new String(baos.toByteArray()); // 判断是否回调 - if (callBack != null){ + if (callBack != null) { // 请求成功, 触发回调 - callBack.onResponse(result); + callBack.onResponse(result, connection.getDate()); } } else { - // 响应成功,非200直接返回null - if (callBack != null){ - callBack.onFail(new Exception("responseCode not >= 200 or < 300 , code: " + responseCode)); + // 响应成功, 非 200 直接返回 null + if (callBack != null) { + callBack.onFail(new Exception("responseCode not >= 200 or < 300, code: " + responseCode)); } } } catch (Exception e) { JCLogUtils.eTag(TAG, e, "request"); - if (callBack != null){ + if (callBack != null) { callBack.onFail(e); } } finally { - if (bout != null) { + if (baos != null) { try { - bout.close(); - } catch(Exception ignore){ + baos.close(); + } catch (Exception ignore) { } } - if (inputStream != null) { + if (is != null) { try { - inputStream.close(); - } catch(Exception ignore){ + is.close(); + } catch (Exception ignore) { } } if (connection != null) { try { - // 关闭底层连接Socket + // 关闭底层连接 Socket connection.disconnect(); - } catch(Exception ignore){ + } catch (Exception ignore) { } } } } - // == 获取网络时间处理 == + // ==================== + // = 获取网络时间处理 = + // ==================== public static final String BAIDU_URL = "https://www.baidu.com"; /** - * 时间回调 + * detail: 时间回调 + * @author Ttt */ public interface TimeCallBack { /** - * 请求相应回调 - * @param time 毫秒 + * 请求响应回调 + * @param time 响应时间 ( 毫秒 ) */ void onResponse(long time); /** * 请求失败 - * @param e + * @param e 失败异常 */ void onFail(Exception e); } /** * 获取网络时间 - 默认使用百度链接 - * @param timeCallBack + * @param timeCallBack 请求时间回调接口 */ - public static void getNetTime(TimeCallBack timeCallBack){ + public static void getNetTime(final TimeCallBack timeCallBack) { getNetTime(BAIDU_URL, timeCallBack); } /** * 获取网络时间 - * @param urlStr - * @param timeCallBack + * @param urlStr 请求地址 + * @param timeCallBack 请求时间回调接口 */ - public static void getNetTime(final String urlStr, final TimeCallBack timeCallBack){ + public static void getNetTime(final String urlStr, final TimeCallBack timeCallBack) { new Thread(new Runnable() { @Override public void run() { @@ -224,11 +226,11 @@ public void run() { } /** - * 请求网络时间(内部私有) - * @param urlStr - * @param timeCallBack + * 请求网络时间 ( 内部私有 ) + * @param urlStr 请求地址 + * @param timeCallBack 请求时间回调接口 */ - private static void reqNetTime(String urlStr, TimeCallBack timeCallBack){ + private static void reqNetTime(final String urlStr, final TimeCallBack timeCallBack) { // 获取连接对象 HttpURLConnection connection = null; try { @@ -239,25 +241,25 @@ private static void reqNetTime(String urlStr, TimeCallBack timeCallBack){ // 获取时间 long date = connection.getDate(); // 获取失败, 则进行修改 - if (date <= 0){ - date = -1l; + if (date <= 0) { + date = -1L; } // 触发回调 - if (timeCallBack != null){ + if (timeCallBack != null) { timeCallBack.onResponse(date); } - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "getNetTime"); // 触发回调 - if (timeCallBack != null){ + if (timeCallBack != null) { timeCallBack.onFail(e); } } finally { if (connection != null) { try { - // 关闭底层连接Socket + // 关闭底层连接 Socket connection.disconnect(); - } catch(Exception ignore){ + } catch (Exception ignore) { } } } diff --git a/lib/DevApp/src/main/java/dev/utils/common/MapUtils.java b/lib/DevApp/src/main/java/dev/utils/common/MapUtils.java new file mode 100644 index 0000000000..bccd410984 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/MapUtils.java @@ -0,0 +1,1162 @@ +package dev.utils.common; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: Map 工具类 + * @author Ttt + *
+ *     @see 
+ * 
+ */ +public final class MapUtils { + + private MapUtils() { + } + + // 日志 TAG + private static final String TAG = MapUtils.class.getSimpleName(); + + // ======= + // = Map = + // ======= + + /** + * 判断 Map 是否为 null + * @param map {@link Map} + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Map map) { + return (map == null || map.size() == 0); + } + + /** + * 判断 Map 是否不为 null + * @param map {@link Map} + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Map map) { + return (map != null && map.size() != 0); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取 Map 长度 + * @param map {@link Map} + * @return 如果 Map 为 null, 则返回默认长度, 如果不为 null, 则返回 map.size() + */ + public static int length(final Map map) { + return length(map, 0); + } + + /** + * 获取 Map 长度 + * @param map {@link Map} + * @param defaultLength 集合为 null 默认长度 + * @return 如果 Map 为 null, 则返回 defaultLength, 如果不为 null, 则返回 map.size() + */ + public static int length(final Map map, final int defaultLength) { + return map != null ? map.size() : defaultLength; + } + + // = + + /** + * 获取长度 to Map 是否等于期望长度 + * @param map {@link Map} + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Map map, final int length) { + return map != null && map.size() == length; + } + + // = + + /** + * 判断 Map 长度是否大于指定长度 + * @param map {@link Map} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean greaterThan(final Map map, final int length) { + return map != null && map.size() > length; + } + + /** + * 判断 Map 长度是否大于等于指定长度 + * @param map {@link Map} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean greaterThanOrEqual(final Map map, final int length) { + return map != null && map.size() >= length; + } + + // = + + /** + * 判断 Map 长度是否小于指定长度 + * @param map {@link Map} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean lessThan(final Map map, final int length) { + return map != null && map.size() < length; + } + + /** + * 判断 Map 长度是否小于等于指定长度 + * @param map {@link Map} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean lessThanOrEqual(final Map map, final int length) { + return map != null && map.size() <= length; + } + + // ============ + // = 数据获取 = + // ============ + + /** + * 获取 value + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return 指定 key 的 value + */ + public static V get(final Map map, final K key) { + if (map != null) { + try { + return map.get(key); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return null; + } + + /** + * 获取 value 如果 value 为 null, 则返回 defaultValue + * @param map {@link Map} + * @param key key + * @param defaultValue 默认 value + * @param key + * @param value + * @return 指定 key 的 value 如果 value 为 null, 则返回 defaultValue + */ + public static V get(final Map map, final K key, final V defaultValue) { + if (map != null) { + try { + V value = map.get(key); + if (value == null) { + return defaultValue; + } else { + return value; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return null; + } + + /** + * 通过 value 获取 key + * @param map {@link Map} + * @param value Value + * @param key + * @param value + * @return 等于 value 的 key + */ + public static K getKeyByValue(final Map map, final V value) { + if (map != null) { + try { + List lists = new ArrayList<>(); + // 进行遍历判断 + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + V v = entry.getValue(); + if (equals(v, value)) { + return entry.getKey(); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getKeyByValue"); + } + } + return null; + } + + /** + * 通过 value 获取 key 集合 ( 返回等于 value 的 key 集合 ) + * @param map {@link Map} + * @param value Value + * @param key + * @param value + * @return 等于 value 的 key 集合 + */ + public static List getKeysByValue(final Map map, final V value) { + if (map != null) { + try { + List lists = new ArrayList<>(); + // 进行遍历判断 + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + V v = entry.getValue(); + if (equals(v, value)) { + // 保存 key + lists.add(entry.getKey()); + } + } + // 返回 key 集合 + return lists; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getKeysByValue"); + } + } + return null; + } + + // = + + /** + * 通过 Map 获取 key 集合 + * @param map {@link Map} + * @param key + * @param value + * @return 全部存储 key 集合 + */ + public static List getKeys(final Map map) { + if (map != null) { + try { + return new ArrayList<>(map.keySet()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getKeys"); + } + } + return null; + } + + /** + * 通过 Map 获取 key 数组 + * @param map {@link Map} + * @param key + * @param value + * @return 全部存储 key 数组 + */ + public static K[] getKeysToArrays(final Map map) { + if (map != null) { + try { + return (K[]) getKeys(map).toArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getKeysToArrays"); + } + } + return null; + } + + /** + * 通过 Map 获取 value 集合 + * @param map {@link Map} + * @param key + * @param value + * @return 全部存储 value 数组 + */ + public static List getValues(final Map map) { + if (map != null) { + try { + return new ArrayList<>(map.values()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getValues"); + } + } + return null; + } + + /** + * 通过 Map 获取 value 数组 + * @param map {@link Map} + * @param key + * @param value + * @return 全部存储 value 数组 + */ + public static V[] getValuesToArrays(final Map map) { + if (map != null) { + try { + return (V[]) getValues(map).toArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getValuesToArrays"); + } + } + return null; + } + + // = + + /** + * 获取第一条数据 + * @param map {@link Map} + * @param key + * @param value + * @return 第一条数据 {@link Map.Entry} + */ + public static Map.Entry getFirst(final LinkedHashMap map) { + if (map != null) { + try { + return map.entrySet().iterator().next(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFirst"); + } + } + return null; + } + + /** + * 获取最后一条数据 + * @param map {@link Map} + * @param key + * @param value + * @return 最后一条数据 {@link Map.Entry} + */ + public static Map.Entry getLast(final LinkedHashMap map) { + return getLast(map, true); + } + + /** + * 获取最后一条数据 + * @param map {@link Map} + * @param isReflection 是否通过反射获取 + * @param key + * @param value + * @return 最后一条数据 {@link Map.Entry} + */ + public static Map.Entry getLast(final LinkedHashMap map, final boolean isReflection) { + if (map != null) { + if (isReflection) { + try { + // 反射方式 + Field tail = map.getClass().getDeclaredField("tail"); + tail.setAccessible(true); + return (Map.Entry) tail.get(map); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getLast"); + } + } else { + try { + // 遍历方式 + Iterator> iterator = map.entrySet().iterator(); + Map.Entry tail = null; + while (iterator.hasNext()) { + tail = iterator.next(); + } + return tail; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getLast"); + } + } + } + return null; + } + + // = + + /** + * 根据指定 key 获取 key 所在位置的下一条数据 + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return 指定 key 下一条数据 {@link Map.Entry} + */ + public static Map.Entry getNext(final LinkedHashMap map, final K key) { + if (map != null) { + try { + // 进行遍历判断 + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + K k = entry.getKey(); + // 判断 key 是否相同 + if (equals(k, key)) { + return entry; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getNext"); + } + } + return null; + } + + /** + * 根据指定 key 获取 key 所在位置的上一条数据 + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return 指定 key 上一条数据 {@link Map.Entry} + */ + public static Map.Entry getPrevious(final LinkedHashMap map, final K key) { + if (map != null) { + try { + // 进行遍历判断 + Iterator> iterator = map.entrySet().iterator(); + // 临时保存处理 + Map.Entry temp = null; + // 循环处理 + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + K k = entry.getKey(); + // 判断 key 是否相同 + if (equals(k, key)) { + return temp; + } + // 赋值上一条数据 + temp = entry; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPrevious"); + } + } + return null; + } + + // ============ + // = 添加数据 = + // ============ + + /** + * 添加一条数据 + * @param map {@link Map} + * @param key key + * @param value value + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean put(final Map map, final K key, final V value) { + return put(map, key, value, false); + } + + /** + * 添加一条数据 + * @param map {@link Map} + * @param key key + * @param value value + * @param notNull 是否不允许 key 为 null + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean put(final Map map, final K key, final V value, final boolean notNull) { + if (map != null) { + if (notNull && key == null) { + return false; + } + try { + map.put(key, value); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "put"); + } + } + return false; + } + + /** + * 添加一条数据, 不允许 key 为 null + * @param map {@link Map} + * @param key key + * @param value value + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putNotNull(final Map map, final K key, final V value) { + return put(map, key, value, true); + } + + // = + + /** + * 添加一条数据 + * @param map {@link Map} + * @param entry entry + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean put(final Map map, final Map.Entry entry) { + return put(map, entry, false); + } + + /** + * 添加一条数据 + * @param map {@link Map} + * @param entry entry + * @param notNull 是否不允许 key 为 null + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean put(final Map map, final Map.Entry entry, final boolean notNull) { + if (map != null && entry != null) { + if (notNull && entry.getKey() == null) { + return false; + } + try { + map.put(entry.getKey(), entry.getValue()); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "put"); + } + } + return false; + } + + /** + * 添加一条数据, 不允许 key 为 null + * @param map {@link Map} + * @param entry entry + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putNotNull(final Map map, final Map.Entry entry) { + return put(map, entry, true); + } + + // = + + /** + * 添加多条数据 + * @param map {@link Map} + * @param listKeys keys + * @param listValues values + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final List listKeys, final List listValues) { + return putAll(map, listKeys, listValues, false); + } + + /** + * 添加多条数据 + * @param map {@link Map} + * @param listKeys keys + * @param listValues values + * @param notNull 是否不允许 key 为 null + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final List listKeys, final List listValues, final boolean notNull) { + if (map != null && listKeys != null && listValues != null && listKeys.size() == listValues.size()) { + try { + // 循环保存 + for (int i = 0, len = listKeys.size(); i < len; i++) { + K key = listKeys.get(i); + if (notNull && key == null) { + continue; // 忽略进行下一个 + } + // 添加数据 + map.put(key, listValues.get(i)); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putAll"); + } + } + return false; + } + + /** + * 添加多条数据, 不允许 key 为 null + * @param map {@link Map} + * @param listKeys keys + * @param listValues values + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAllNotNull(final Map map, final List listKeys, final List listValues) { + return putAll(map, listKeys, listValues, true); + } + + // = + + /** + * 添加多条数据 + * @param map {@link Map} + * @param keys keys + * @param values values + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final K[] keys, final V[] values) { + return putAll(map, keys, values, false); + } + + /** + * 添加多条数据 + * @param map {@link Map} + * @param keys keys + * @param values values + * @param notNull 是否不允许 key 为 null + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final K[] keys, final V[] values, final boolean notNull) { + if (map != null && keys != null && values != null && keys.length == values.length) { + try { + // 循环保存 + for (int i = 0, len = keys.length; i < len; i++) { + K key = keys[i]; + if (notNull && key == null) { + continue; // 忽略进行下一个 + } + // 添加数据 + map.put(key, values[i]); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putAll"); + } + } + return false; + } + + /** + * 添加多条数据, 不允许 key 为 null + * @param map {@link Map} + * @param keys keys + * @param values values + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAllNotNull(final Map map, final K[] keys, final V[] values) { + return putAll(map, keys, values, true); + } + + // = + + /** + * 添加多条数据 + * @param map {@link Map} + * @param mapData map 数据 + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final Map mapData) { + return putAll(map, mapData, false); + } + + /** + * 添加多条数据 + * @param map {@link Map} + * @param mapData map 数据 + * @param notNull 是否不允许 key 为 null + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final Map mapData, final boolean notNull) { + if (map != null && mapData != null) { + if (notNull) { + try { + // 进行遍历判断 + Iterator> iterator = mapData.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + K key = entry.getKey(); + if (key != null) { + // 保存数据 + map.put(key, entry.getValue()); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putAll"); + } + } else { + try { + map.putAll(mapData); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putAll"); + } + } + } + return false; + } + + /** + * 添加多条数据, 不允许 key 为 null + * @param map {@link Map} + * @param mapData map 数据 + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAllNotNull(final Map map, final Map mapData) { + return putAll(map, mapData, true); + } + + // ============ + // = 删除数据 = + // ============ + + /** + * 移除一条数据 + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean remove(final Map map, final K key) { + if (map != null) { + try { + map.remove(key); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remove"); + } + } + return false; + } + + /** + * 移除一条数据 + * @param map {@link Map} + * @param key key + * @param value value + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean remove(final Map map, final K key, final V value) { + if (map != null) { + try { + // 判断值是否一样, 一样则移除 key + if (equals(value, map.get(key))) { + map.remove(key); + } + // return map.remove(key, value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remove"); + } + } + return false; + } + + /** + * 移除多条数据 + * @param map {@link Map} + * @param keys keys + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToKeys(final Map map, final Collection keys) { + if (map != null && keys != null) { + try { + for (K key : keys) { + map.remove(key); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToKeys"); + } + } + return false; + } + + /** + * 移除等于 value 的所有数据 + * @param map {@link Map} + * @param value value + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToValue(final Map map, final V value) { + if (map != null) { + try { + // 进行遍历判断 + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + V v = entry.getValue(); + if (equals(v, value)) { + iterator.remove(); + } + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToValue"); + } + } + return false; + } + + /** + * 移除等于 value 的所有数据 (Collection) + * @param map {@link Map} + * @param values values + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToValues(final Map map, final Collection values) { + if (map != null && values != null) { + try { + for (V value : values) { + removeToValue(map, value); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToValues"); + } + } + return false; + } + + // ============ + // = 快捷判断 = + // ============ + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + // = + + /** + * 切换保存状态 + *
+     *     1. 如果存在, 则删除
+     *     2. 如果不存在, 则保存
+     * 
+ * @param map {@link Map} + * @param key key + * @param value value + * @param key + * @param value + */ + public static void toggle(final Map map, final K key, final V value) { + if (map != null) { + // 判断是否存在 key + boolean existKey = map.containsKey(key); + try { + if (existKey) { // 存在则删除 + map.remove(key); + } else { // 不存在则保存 + map.put(key, value); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toggle"); + } + } + } + + /** + * 判断指定 key 的 value 是否为 null + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return {@code true} yes, {@code false} no + */ + public static boolean isNullToValue(final Map map, final K key) { + if (map != null) { + return map.get(key) == null; + } + return true; + } + + /** + * 判断 Map 是否存储了 key + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return {@code true} yes, {@code false} no + */ + public static boolean containsKey(final Map map, final K key) { + if (map != null) { + try { + return map.containsKey(key); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "containsKey"); + } + } + return false; + } + + /** + * 判断 Map 是否存储了 value + * @param map {@link Map} + * @param value value + * @param key + * @param value + * @return {@code true} yes, {@code false} no + */ + public static boolean containsValue(final Map map, final V value) { + if (map != null) { + try { + return map.containsValue(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "containsValue"); + } + } + return false; + } + + // ================= + // = 特殊 Map 操作 = + // ================= + + /** + * 添加一条数据 - (Value) List + * @param map 待添加 {@link Map} + * @param key key + * @param value value, add to list + * @param key + * @param value type + * @return {@code true} success, {@code false} fail + */ + public static boolean putToList(final Map> map, final K key, final T value) { + return putToList(map, key, value, true); + } + + /** + * 添加一条数据 - (Value) List + * @param map {@link Map} + * @param key key + * @param value value, add to list + * @param isNew 当指定 (key) 的 value 为 null, 是否创建 + * @param key + * @param value type + * @return {@code true} success, {@code false} fail + */ + public static boolean putToList(final Map> map, final K key, final T value, final boolean isNew) { + if (map != null) { + if (map.containsKey(key)) { + ArrayList lists = map.get(key); + if (lists != null) { + try { + lists.add(value); + map.put(key, lists); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putToList"); + } + } + } else { + // 判断是否创建 + if (isNew) { + try { + ArrayList lists = new ArrayList<>(); + lists.add(value); + map.put(key, lists); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putToList"); + } + } + } + } + return false; + } + + // = + + /** + * 移除一条数据 - (Value) List + * @param map {@link Map} + * @param key key + * @param key + * @param value type + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToList(final Map> map, final K key) { + if (map != null) { + try { + map.remove(key); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToList"); + } + } + return false; + } + + /** + * 移除一条数据 - (Value) List + * @param map {@link Map} + * @param key key + * @param value value, remove to list + * @param key + * @param value type + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToList(final Map> map, final K key, final T value) { + if (map != null) { + if (map.containsKey(key)) { + ArrayList lists = map.get(key); + if (lists != null) { + try { + lists.remove(value); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToList"); + } + } + } + } + return false; + } + + /** + * 移除多条数据 - (Value) List + * @param map {@link Map} + * @param key key + * @param lists 删除的 list 数据源 + * @param key + * @param value type + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToLists(final Map> map, final K key, final ArrayList lists) { + if (map != null && lists != null) { + if (map.containsKey(key)) { + ArrayList list = map.get(key); + if (list != null) { + try { + list.removeAll(lists); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToLists"); + } + } + } + } + return false; + } + + // = + + /** + * 移除多条数据 - 通过 Map 进行移除 + * @param map {@link Map} + * @param removeMap {@link Map} 移除对比数据源 + * @param key + * @param value type + */ + public static void removeToMap(final Map> map, final Map> removeMap) { + removeToMap(map, removeMap, true, false); + } + + /** + * 移除多条数据 - 通过 Map 进行移除 + * @param map {@link Map} + * @param removeMap {@link Map} 移除对比数据源 + * @param removeEmpty 是否移除 null、长度为 0 的数据 + * @param isNullRemoveAll 如果待移除的 ArrayList 是 null, 是否移除全部 + * @param key + * @param value type + */ + public static void removeToMap(final Map> map, final Map> removeMap, + final boolean removeEmpty, final boolean isNullRemoveAll) { + if (map != null && removeMap != null) { + Iterator>> iterator = removeMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + // 获取 key + K key = entry.getKey(); + // 进行移除处理 + if (map.containsKey(key)) { + ArrayList value = entry.getValue(); + try { + if (value != null) { + map.get(key).removeAll(value); + } else { + if (isNullRemoveAll) { + map.remove(key); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToMap - removeAll"); + } + // 判断是否移除 null、长度为 0 的数据 + if (removeEmpty) { + ArrayList lists = map.get(key); + try { + // 不存在数据了, 则移除 + if (lists == null || lists.size() == 0) { + map.remove(key); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToMap"); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/NumberUtils.java b/lib/DevApp/src/main/java/dev/utils/common/NumberUtils.java new file mode 100644 index 0000000000..1f06ef40aa --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/NumberUtils.java @@ -0,0 +1,1008 @@ +package dev.utils.common; + +import java.math.BigDecimal; +import java.util.regex.Pattern; + +import dev.utils.JCLogUtils; + +/** + * detail: 数字 ( 计算 ) 工具类 + * @author Ttt + */ +public final class NumberUtils { + + private NumberUtils() { + } + + // 日志 TAG + private static final String TAG = NumberUtils.class.getSimpleName(); + + // ======= + // = int = + // ======= + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI(final int value, final int max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + if (value >= max) return 1; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI(final float value, final float max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + if (value >= max) return 1; + return (int) (value / max); + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI(final long value, final long max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + if (value >= max) return 1; + return (int) (value / max); + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI(final double value, final double max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + if (value >= max) return 1; + return (int) (value / max); + } + + // ========== + // = double = + // ========== + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD(final int value, final int max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + if (value >= max) return 1.0d; + return (double) value / (double) max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD(final float value, final float max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + if (value >= max) return 1.0d; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD(final long value, final long max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + if (value >= max) return 1.0d; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD(final double value, final double max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + if (value >= max) return 1.0d; + return value / max; + } + + // ======== + // = long = + // ======== + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL(final int value, final int max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + if (value >= max) return 1L; + return (long) value / (long) max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL(final float value, final float max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + if (value >= max) return 1L; + return (long) value / (long) max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL(final long value, final long max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + if (value >= max) return 1L; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL(final double value, final double max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + if (value >= max) return 1L; + return (long) (value / max); + } + + // ========= + // = float = + // ========= + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF(final int value, final int max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return (float) value / (float) max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF(final float value, final float max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF(final long value, final long max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF(final double value, final double max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return (float) (value / max); + } + + // =============================== + // = 计算百分比值 ( 可超出 100%) = + // =============================== + + // ======= + // = int = + // ======= + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI2(final int value, final int max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI2(final float value, final float max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + return (int) (value / max); + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI2(final long value, final long max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + return (int) (value / max); + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI2(final double value, final double max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + return (int) (value / max); + } + + // ========== + // = double = + // ========== + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD2(final int value, final int max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + return (double) value / (double) max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD2(final float value, final float max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD2(final long value, final long max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD2(final double value, final double max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + return value / max; + } + + // ======== + // = long = + // ======== + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL2(final int value, final int max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + return (long) value / (long) max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL2(final float value, final float max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + return (long) value / (long) max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL2(final long value, final long max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL2(final double value, final double max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + return (long) (value / max); + } + + // ========= + // = float = + // ========= + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF2(final int value, final int max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + return (float) value / (float) max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF2(final float value, final float max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF2(final long value, final long max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF2(final double value, final double max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + return (float) (value / max); + } + + // = + + /** + * 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max + * @param value 指定值 + * @param max 最大值 + * @param min 最小值 + * @return 介于 max、min 之间的 value + */ + public static int clamp(final int value, final int max, final int min) { + return value > max ? max : value < min ? min : value; + } + + /** + * 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max + * @param value 指定值 + * @param max 最大值 + * @param min 最小值 + * @return 介于 max、min 之间的 value + */ + public static double clamp(final double value, final double max, final double min) { + return value > max ? max : value < min ? min : value; + } + + /** + * 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max + * @param value 指定值 + * @param max 最大值 + * @param min 最小值 + * @return 介于 max、min 之间的 value + */ + public static long clamp(final long value, final long max, final long min) { + return value > max ? max : value < min ? min : value; + } + + /** + * 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max + * @param value 指定值 + * @param max 最大值 + * @param min 最小值 + * @return 介于 max、min 之间的 value + */ + public static float clamp(final float value, final float max, final float min) { + return value > max ? max : value < min ? min : value; + } + + // ======== + // = calc = + // ======== + + /** + * 获取倍数 ( 自动补 1) + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultiple(final int value, final int divisor) { + if (value <= 0 || divisor <= 0) return 0; + if (value <= divisor) return 1; + return (value % divisor == 0) ? (value / divisor) : (value / divisor) + 1; + } + + /** + * 获取倍数 ( 自动补 1) + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultiple(final double value, final double divisor) { + if (value <= 0 || divisor <= 0) return 0; + if (value <= divisor) return 1; + int result = (int) (value / divisor); + return ((value - divisor * result == 0d) ? result : result + 1); + } + + /** + * 获取倍数 ( 自动补 1) + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultiple(final long value, final long divisor) { + if (value <= 0 || divisor <= 0) return 0; + if (value <= divisor) return 1; + return (int) ((value % divisor == 0) ? (value / divisor) : (value / divisor) + 1); + } + + /** + * 获取倍数 ( 自动补 1) + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultiple(final float value, final float divisor) { + if (value <= 0 || divisor <= 0) return 0; + if (value <= divisor) return 1; + int result = (int) (value / divisor); + return ((value - divisor * result == 0f) ? result : result + 1); + } + + // ======= + // = int = + // ======= + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultipleI(final int value, final int divisor) { + if (value <= 0 || divisor <= 0) return 0; + return (int) (value / divisor); + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultipleI(final double value, final double divisor) { + if (value <= 0 || divisor <= 0) return 0; + return (int) (value / divisor); + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultipleI(final long value, final long divisor) { + if (value <= 0 || divisor <= 0) return 0; + return (int) (value / divisor); + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultipleI(final float value, final float divisor) { + if (value <= 0 || divisor <= 0) return 0; + return (int) (value / divisor); + } + + // ========== + // = double = + // ========== + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static double getMultipleD(final int value, final int divisor) { + if (value <= 0d || divisor <= 0d) return 0d; + return (double) value / (double) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static double getMultipleD(final double value, final double divisor) { + if (value <= 0d || divisor <= 0d) return 0d; + return (double) value / (double) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static double getMultipleD(final long value, final long divisor) { + if (value <= 0d || divisor <= 0d) return 0d; + return (double) value / (double) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static double getMultipleD(final float value, final float divisor) { + if (value <= 0d || divisor <= 0d) return 0d; + return (double) value / (double) divisor; + } + + // ======== + // = long = + // ======== + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static long getMultipleL(final int value, final int divisor) { + if (value <= 0L || divisor <= 0L) return 0L; + return (long) value / (long) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static long getMultipleL(final double value, final double divisor) { + if (value <= 0L || divisor <= 0L) return 0L; + return (long) value / (long) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static long getMultipleL(final long value, final long divisor) { + if (value <= 0L || divisor <= 0L) return 0L; + return (long) value / (long) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static long getMultipleL(final float value, final float divisor) { + if (value <= 0L || divisor <= 0L) return 0L; + return (long) value / (long) divisor; + } + + // ========= + // = float = + // ========= + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static float getMultipleF(final int value, final int divisor) { + if (value <= 0f || divisor <= 0f) return 0f; + return (float) value / (float) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static float getMultipleF(final double value, final double divisor) { + if (value <= 0f || divisor <= 0f) return 0f; + return (float) value / (float) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static float getMultipleF(final long value, final long divisor) { + if (value <= 0f || divisor <= 0f) return 0f; + return (float) value / (float) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static float getMultipleF(final float value, final float divisor) { + if (value <= 0f || divisor <= 0f) return 0f; + return (float) value / (float) divisor; + } + + // ================== + // = 数字、中文互转 = + // ================== + + // 零索引 + private static final int ZERO = 0; + // 十位数索引 + private static final int TEN_POS = 10; + // 中文 ( 数字单位 ) + private static final String[] CHN_NUMBER_UNITS = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "百", "千", "万", "亿", "兆"}; + // 中文大写 ( 数字单位 ) + private static final String[] CHN_NUMBER_UPPER_UNITS = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖", "拾", "佰", "仟", "万", "亿", "兆"}; + // 数字单位对应数值 + private static final double[] NUMBER_UNITS = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1000, 10000, 100000000, 1000000000000d}; + + // ============ + // = 对外公开 = + // ============ + + // ============== + // = 数字转中文 = + // ============== + + /** + * 数字转中文数值 + * @param number 数值 + * @param isUpper 是否大写金额 + * @return 数字中文化字符串 + */ + public static String numberToCHN(final double number, final boolean isUpper) { + try { + return numberToCHNNumber(BigDecimal.valueOf(number), isUpper ? CHN_NUMBER_UPPER_UNITS : CHN_NUMBER_UNITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "numberToCHN"); + } + return null; + } + + /** + * 数字转中文数值 + * @param number 数值 + * @param isUpper 是否大写金额 + * @return 数字中文化字符串 + */ + public static String numberToCHN(final String number, final boolean isUpper) { + if (number != null) { + try { + return numberToCHNNumber(new BigDecimal(number), isUpper ? CHN_NUMBER_UPPER_UNITS : CHN_NUMBER_UNITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "numberToCHN"); + } + } + return null; + } + + /** + * 数字转中文数值 + * @param number 数值 + * @param isUpper 是否大写金额 + * @return 数字中文化字符串 + */ + public static String numberToCHN(final BigDecimal number, final boolean isUpper) { + return numberToCHNNumber(number, isUpper ? CHN_NUMBER_UPPER_UNITS : CHN_NUMBER_UNITS); + } + + // ============ + // = 内部方法 = + // ============ + + // ============== + // = 数字转中文 = + // ============== + + /** + * 数字转中文数值 + * @param bigDecimal 数值 + * @param chnUnits 中文数字单位数组 + * @return 数字中文化字符串 + */ + private static String numberToCHNNumber(final BigDecimal bigDecimal, final String[] chnUnits) { + // 防止为 null + if (bigDecimal == null) return null; + // 去除小数点 + BigDecimal number = bigDecimal.setScale(0, BigDecimal.ROUND_DOWN); + // 防止小于 1, 直接返回 0 + if (number.doubleValue() < 1) return chnUnits[ZERO]; + + StringBuilder builder = new StringBuilder(); + // 索引记录 + int unitIndex = 0; // 当前数字单位 + int beforeUnitIndex = 0; // 之前的数字单位 + // 循环处理 + for (int i = NUMBER_UNITS.length - 1; i > 0; i--) { + if (number.doubleValue() >= NUMBER_UNITS[i]) { + final int multiple = (int) (number.doubleValue() / NUMBER_UNITS[i]); // 倍数 + number = number.subtract(BigDecimal.valueOf(multiple * NUMBER_UNITS[i])); // 递减 + // 如果倍数大于万倍, 则直接递归 + if (multiple >= 10000) { + // 拼接数值 + builder.append(numberToCHNNumber(new BigDecimal(multiple), chnUnits)); + builder.append(chnUnits[i]); // 数字单位 + // 判断是否需要补零 + if (unitIndex > i && unitIndex != 0) { + builder.append(chnUnits[ZERO]); // 补零 + } + } else { + // 判断 兆级与亿级、亿级与万级 补零操作 + if ((i == 14 && unitIndex == 15) || (i == 13 && unitIndex == 14)) { + if (multiple < 1000) { + builder.append(chnUnits[ZERO]); // 补零 + } + } else if (unitIndex > i && unitIndex != 0) { // 跨数字单位处理 + builder.append(chnUnits[ZERO]); // 补零 + } + // 拼接数值 + builder.append(thousandConvertCHN(multiple, chnUnits)); + builder.append(chnUnits[i]); // 数字单位 + } + // 保存旧的数字单位索引 + beforeUnitIndex = unitIndex; + // 保存新的数字单位索引 + unitIndex = i; + } + + double numberValue = number.doubleValue(); + // 如果位数小于万位 ( 属于千位 ), 则进行处理 + if (numberValue < 10000) { + // 判断是否需要补零 + if (unitIndex >= (TEN_POS + 3)) { + // 是否大于 1 ( 结尾零, 则不补充数字单位 ) + if (numberValue >= 1) { + if (beforeUnitIndex == 0) { + beforeUnitIndex = unitIndex; + } + // 如果旧的索引, 大于当前索引, 则补零 + if (unitIndex != 13 && (beforeUnitIndex == 14 || beforeUnitIndex == 15) + && beforeUnitIndex >= unitIndex) { // 属于亿、兆级别, 都需要补零 + builder.append(chnUnits[ZERO]); // 补零 + } else { // 当前数字单位属于万级 + if (numberValue < 1000) { + builder.append(chnUnits[ZERO]); // 补零 + } + } + } + } + // 拼接数值 + builder.append(thousandConvertCHN((int) numberValue, chnUnits)); + return builder.toString(); + } + } + return builder.toString(); + } + + /** + * 万位以下 ( 千位 ) 级别转换 + * @param value 数值 + * @param chnUnits 中文数字单位数组 + * @return 数字中文化字符串 + */ + private static String thousandConvertCHN(final int value, final String[] chnUnits) { + StringBuilder builder = new StringBuilder(); + // 转换数字 + int number = value; + // 对应数值 + int[] units = {10, 100, 1000}; + // 判断是否需要补零 + boolean[] zeros = new boolean[3]; + zeros[0] = number >= 10; // 大于等于十位, 才需要补零 + zeros[1] = number >= 100; // 大于等于百位, 才需要补零 + zeros[2] = false; // 千位数, 不需要补零 + // 循环处理 + for (int i = 2; i >= 0; i--) { + if (number >= units[i]) { + int multiple = (number / units[i]); + number -= multiple * units[i]; + // 拼接数值 + builder.append(chnUnits[multiple]); // 个位数 + builder.append(chnUnits[TEN_POS + i]); // 数字单位 + // 进行改变处理 + zeros[i] = false; + } + // 补零判断处理 + if (number >= 1 && zeros[i]) { + if (i == 1) { // 属于百位 + builder.append(chnUnits[ZERO]); // 补零 + } else { // 属于十位 + // 如果百位, 补零了, 个位数则不用补零 + if (!zeros[i + 1]) { + builder.append(chnUnits[ZERO]); // 补零 + } + } + } + } + // 判断最后值, 是否大于 1 ( 结尾零, 则不补充数字单位 ) + if (number >= 1) { + builder.append(chnUnits[number]); + } + return builder.toString(); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + // ================== + // = ValidatorUtils = + // ================== + + // 正则表达式: 验证数字 + private static final String REGEX_NUMBER = "^[0-9]*$"; + // 正则表达式: 验证数字或包含小数点 + private static final String REGEX_NUMBER_OR_DECIMAL = "^[0-9]*[.]?[0-9]*$"; + + /** + * 检验数字 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNumber(final String str) { + return match(REGEX_NUMBER, str); + } + + /** + * 检验数字或包含小数点 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNumberDecimal(final String str) { + return match(REGEX_NUMBER_OR_DECIMAL, str); + } + + /** + * 通用匹配函数 + * @param regex 正则表达式 + * @param input 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean match(final String regex, final String input) { + if (!isEmpty(input)) { + try { + return Pattern.matches(regex, input); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "match"); + } + } + return false; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/ObjectUtils.java b/lib/DevApp/src/main/java/dev/utils/common/ObjectUtils.java new file mode 100644 index 0000000000..05150e8166 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/ObjectUtils.java @@ -0,0 +1,161 @@ +package dev.utils.common; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: 对象相关工具类 + * @author Ttt + */ +public final class ObjectUtils { + + private ObjectUtils() { + } + + // 日志 TAG + private static final String TAG = ObjectUtils.class.getSimpleName(); + + // ========== + // = Object = + // ========== + + /** + * 判断对象是否为空 + * @param object 对象 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Object object) { + if (object == null) { + return true; + } + try { + if (object.getClass().isArray() && Array.getLength(object) == 0) { + return true; + } + if (object instanceof CharSequence && object.toString().length() == 0) { + return true; + } + if (object instanceof Collection && ((Collection) object).isEmpty()) { + return true; + } + if (object instanceof Map && ((Map) object).isEmpty()) { + return true; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isEmpty"); + } + return false; + } + + /** + * 判断对象是否非空 + * @param object 对象 + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Object object) { + return !isEmpty(object); + } + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + /** + * 检查对象是否为 null, 为 null 则抛出异常, 不为 null 则返回该对象 + * @param object 对象 + * @param message 报错信息 + * @param 泛型 + * @return 非空对象 + * @throws NullPointerException null 异常 + */ + public static T requireNonNull(final T object, final String message) throws NullPointerException { + if (object == null) throw new NullPointerException(message); + return object; + } + + /** + * 获取非空或默认对象 + * @param object 对象 + * @param defaultObject 默认值 + * @param 泛型 + * @return 非空或默认对象 + */ + public static T getOrDefault(final T object, final T defaultObject) { + return (object != null) ? object : defaultObject; + } + + /** + * 获取对象哈希值 + * @param object 对象 + * @return 哈希值 + */ + public static int hashCode(final Object object) { + return object != null ? object.hashCode() : 0; + } + + /** + * 获取一个对象的独一无二的标记 + * @param object 对象 + * @return 对象唯一标记 + */ + public static String getObjectTag(final Object object) { + if (object == null) return null; + // 对象所在的包名 + 对象的内存地址 + return object.getClass().getName() + Integer.toHexString(object.hashCode()); + } + + /** + * 获取转换对象 + * @param object 对象 + * @param 泛型 + * @return 非空或默认对象 + */ + public static T converObj(final Object object) { + if (object == null) return null; + try { + return (T) object; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "converObj"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/RandomUtils.java b/lib/DevApp/src/main/java/dev/utils/common/RandomUtils.java new file mode 100644 index 0000000000..c409a5bcd1 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/RandomUtils.java @@ -0,0 +1,557 @@ +package dev.utils.common; + +import java.util.Random; + +import dev.utils.JCLogUtils; + +/** + * detail: 随机工具类 + * @author Ttt + */ +public final class RandomUtils { + + private RandomUtils() { + } + + // 日志 TAG + private static final String TAG = RandomUtils.class.getSimpleName(); + + // 0123456789 + public static final char[] NUMBERS = new char[]{48, 49, 50, 51, 52, 53, 54, 55, 56, 57}; + + // abcdefghijklmnopqrstuvwxyz + public static final char[] LOWER_CASE_LETTERS = new char[]{97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122}; + + // ABCDEFGHIJKLMNOPQRSTUVWXYZ + public static final char[] CAPITAL_LETTERS = new char[]{65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90}; + + // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + public static final char[] LETTERS = new char[]{97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90}; + + // 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + public static final char[] NUMBERS_AND_LETTERS = new char[]{48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90}; + + /** + * 获取伪随机 boolean 值 + * @param random Random + * @return 随机 boolean 值 + */ + public static boolean nextBoolean(final Random random) { + return random != null ? random.nextBoolean() : new Random().nextBoolean(); + } + + /** + * 获取伪随机 byte[] + * @param random Random + * @param data 随机数据源 + * @return 随机 byte[] + */ + public static byte[] nextBytes(final Random random, final byte[] data) { + if (random == null || data == null) return data; + try { + random.nextBytes(data); + } catch (Exception e) { + } + return data; + } + + /** + * 获取伪随机 double 值 + * @param random Random + * @return 随机 double 值 + */ + public static double nextDouble(final Random random) { + return random != null ? random.nextDouble() : new Random().nextDouble(); + } + + /** + * 获取伪随机高斯分布值 + * @param random Random + * @return 伪随机高斯分布值 + */ + public static double nextGaussian(final Random random) { + return random != null ? random.nextGaussian() : new Random().nextGaussian(); + } + + /** + * 获取伪随机 float 值 + * @param random Random + * @return 随机 float 值 + */ + public static float nextFloat(final Random random) { + return random != null ? random.nextFloat() : new Random().nextFloat(); + } + + /** + * 获取伪随机 int 值 + * @param random Random + * @return 随机 int 值 + */ + public static int nextInt(final Random random) { + return random != null ? random.nextInt() : new Random().nextInt(); + } + + /** + * 获取伪随机 int 值 - 该值介于 [0, n) 的区间 + * @param random Random + * @param number 最大随机值 + * @return 随机介于 [0, n) 的区间值 + */ + public static int nextInt(final Random random, final int number) { + if (number <= 0) return 0; + return random != null ? random.nextInt(number) : new Random().nextInt(number); + } + + /** + * 获取伪随机 long 值 + * @param random Random + * @return 随机 long 值 + */ + public static long nextLong(final Random random) { + return random != null ? random.nextLong() : new Random().nextLong(); + } + + // = + + /** + * 获取伪随机 boolean 值 + * @return 随机 boolean 值 + */ + public static boolean nextBoolean() { + return new Random().nextBoolean(); + } + + /** + * 获取伪随机 byte[] + * @param data 随机数据源 + * @return 随机 byte[] + */ + public static byte[] nextBytes(final byte[] data) { + if (data == null) return null; + try { + new Random().nextBytes(data); + } catch (Exception e) { + } + return data; + } + + /** + * 获取伪随机 double 值 + * @return 随机 double 值 + */ + public static double nextDouble() { + return new Random().nextDouble(); + } + + /** + * 获取伪随机高斯分布值 + * @return 伪随机高斯分布值 + */ + public static double nextGaussian() { + return new Random().nextGaussian(); + } + + /** + * 获取伪随机 float 值 + * @return 随机 float 值 + */ + public static float nextFloat() { + return new Random().nextFloat(); + } + + /** + * 获取伪随机 int 值 + * @return 随机 int 值 + */ + public static int nextInt() { + return new Random().nextInt(); + } + + /** + * 获取伪随机 int 值 - 该值介于 [0, n) 的区间 + * @param number 最大随机值 + * @return 随机介于 [0, n) 的区间值 + */ + public static int nextInt(final int number) { + if (number <= 0) return 0; + return new Random().nextInt(number); + } + + /** + * 获取伪随机 long 值 + * @return 随机 long 值 + */ + public static long nextLong() { + return new Random().nextLong(); + } + + // = + + /** + * 获取数字自定义长度的随机数 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandomNumbers(final int length) { + return getRandom(NUMBERS, length); + } + + /** + * 获取小写字母自定义长度的随机数 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandomLowerCaseLetters(final int length) { + return getRandom(LOWER_CASE_LETTERS, length); + } + + /** + * 获取大写字母自定义长度的随机数 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandomCapitalLetters(final int length) { + return getRandom(CAPITAL_LETTERS, length); + } + + /** + * 获取大小写字母自定义长度的随机数 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandomLetters(final int length) { + return getRandom(LETTERS, length); + } + + /** + * 获取数字、大小写字母自定义长度的随机数 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandomNumbersAndLetters(final int length) { + return getRandom(NUMBERS_AND_LETTERS, length); + } + + /** + * 获取自定义数据自定义长度的随机数 + * @param source 随机的数据源 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandom(final String source, final int length) { + if (source == null) return null; + return getRandom(source.toCharArray(), length); + } + + /** + * 获取 char[] 内的随机数 + * @param chars 随机的数据源 + * @param length 需要最终长度 + * @return 随机字符串 + */ + public static String getRandom(final char[] chars, final int length) { + if (length > 0 && chars != null && chars.length != 0) { + StringBuilder builder = new StringBuilder(length); + Random random = new Random(); + for (int i = 0; i < length; i++) { + builder.append(chars[random.nextInt(chars.length)]); + } + return builder.toString(); + } + return null; + } + + /** + * 获取 0 - 最大随机数之间的随机数 + * @param max 最大随机数 + * @return 随机介于 [0, max) 的区间值 + */ + public static int getRandom(final int max) { + return getRandom(0, max); + } + + /** + * 获取两个数之间的随机数 ( 不含最大随机数, 需要 + 1) + * @param min 最小随机数 + * @param max 最大随机数 + * @return 随机介于 [min, max) 的区间值 + */ + public static int getRandom(final int min, final int max) { + if (min > max) { + return 0; + } else if (min == max) { + return min; + } + return min + new Random().nextInt(max - min); + } + + // = + + // 内置洗牌算法 + // java.util.Collections.shuffle(List list); + + /** + * 洗牌算法 ( 第一种 ), 随机置换指定的数组使用的默认源的随机性 ( 随机数据源小于三个, 则无效 ) + * @param objects 随机数据源 + * @return {@code true} success, {@code false} fail + */ + public static boolean shuffle(final Object[] objects) { + if (objects == null) return false; + return shuffle(objects, getRandom(1, objects.length)); + } + + /** + * 洗牌算法 ( 第一种 ), 随机置换指定的数组使用的默认源的随机性 ( 随机数据源小于三个, 则无效 ) + * @param objects 随机数据源 + * @param shuffleCount 洗牌次数 + * @return {@code true} success, {@code false} fail + */ + public static boolean shuffle(final Object[] objects, final int shuffleCount) { + int length; + if (shuffleCount > 0 && objects != null && (length = objects.length) >= shuffleCount) { + for (int i = 1; i <= shuffleCount; i++) { + int random = getRandom(0, length - i); + Object temp = objects[length - i]; + objects[length - i] = objects[random]; + objects[random] = temp; + } + return true; + } + return false; + } + + /** + * 洗牌算法 ( 第一种 ), 随机置换指定的数组使用的默认源的随机性 ( 随机数据源小于三个, 则无效 ) + * @param ints 随机数据源 + * @return 随机 int[] + */ + public static int[] shuffle(final int[] ints) { + if (ints == null) return null; + return shuffle(ints, getRandom(1, ints.length)); + } + + /** + * 洗牌算法 ( 第一种 ), 随机置换指定的数组使用的默认源的随机性 ( 随机数据源小于三个, 则无效 ) + * @param ints 随机数据源 + * @param shuffleCount 洗牌次数 + * @return 随机 int[] + */ + public static int[] shuffle(final int[] ints, final int shuffleCount) { + int length; + if (shuffleCount > 0 && ints != null && (length = ints.length) >= shuffleCount) { + int[] out = new int[shuffleCount]; + for (int i = 1; i <= shuffleCount; i++) { + int random = getRandom(0, length - i); + out[i - 1] = ints[random]; + int temp = ints[length - i]; + ints[length - i] = ints[random]; + ints[random] = temp; + } + return out; + } + return null; + } + + // = + + /** + * 洗牌算法 ( 第二种 ), 随机置换指定的数组使用的默认源的随机性 + * @param objects 随机数据源 + * @return {@code true} success, {@code false} fail + */ + public static boolean shuffle2(final Object[] objects) { + if (objects == null) return false; + int len = objects.length; + if (len > 0) { + for (int i = 0; i < len - 1; i++) { + int idx = (int) (Math.random() * (len - i)); + Object temp = objects[idx]; + objects[idx] = objects[len - i - 1]; + objects[len - i - 1] = temp; + } + return true; + } + return false; + } + + // = + + /** + * 获取指定范围 int 值 + * @param origin 开始值 + * @param bound 范围值 + * @return 属于指定范围随机 int 值 + * @throws IllegalArgumentException 参数错误 + */ + public static int nextIntRange(final int origin, final int bound) throws IllegalArgumentException { + if (origin > bound) { + throw new IllegalArgumentException("bound must be greater than origin"); + } else if (origin == bound) { + return origin; + } + Random random = new Random(); + int n = bound - origin; + if (n > 0) { + return random.nextInt(n) + origin; + } else { + int r; + do { + r = random.nextInt(); + } while (r < origin || r >= bound); + return r; + } + } + + /** + * 获取指定范围 long 值 + * @param origin 开始值 + * @param bound 范围值 + * @return 属于指定范围随机 long 值 + * @throws IllegalArgumentException 参数错误 + */ + public static long nextLongRange(final long origin, final long bound) throws IllegalArgumentException { + if (origin > bound) { + throw new IllegalArgumentException("bound must be greater than origin"); + } else if (origin == bound) { + return origin; + } + Random random = new Random(); + long r = random.nextLong(); + long n = bound - origin, m = n - 1; + if ((n & m) == 0L) // power of two + r = (r & m) + origin; + else if (n > 0L) { // reject over-represented candidates + for (long u = r >>> 1; // ensure nonnegative + u + m - (r = u % n) < 0L; // rejection check + u = random.nextLong() >>> 1) // retry + ; + r += origin; + } else { // range not representable as long + while (r < origin || r >= bound) + r = random.nextLong(); + } + return r; + } + + /** + * 获取指定范围 double 值 + * @param origin 开始值 + * @param bound 范围值 + * @return 属于指定范围随机 double 值 + * @throws IllegalArgumentException 参数错误 + */ + public static double nextDoubleRange(final double origin, final double bound) throws IllegalArgumentException { + if (origin > bound) { + throw new IllegalArgumentException("bound must be greater than origin"); + } else if (origin == bound) { + return origin; + } + double r = new Random().nextDouble(); + r = r * (bound - origin) + origin; + if (r >= bound) // correct for rounding + r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); + return r; + } + + /** + * 获取随机 int[] + * @param streamSize 数组长度 + * @param randomNumberOrigin 开始值 + * @param randomNumberBound 结束值 ( 最大值范围 ) + * @return 指定范围随机 int[] + */ + public static int[] ints(final int streamSize, final int randomNumberOrigin, final int randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) { + return null; + } else if (streamSize < 0) { + return null; + } +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +// IntStream intStream = new Random().ints(streamSize, randomNumberOrigin, randomNumberBound); +// return intStream.toArray(); +// } else { + try { + int[] ints = new int[streamSize]; + for (int i = 0; i < streamSize; i++) { + ints[i] = nextIntRange(randomNumberOrigin, randomNumberBound); + } + return ints; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "ints"); + } + return null; +// } + } + + /** + * 获取随机 long[] + * @param streamSize 数组长度 + * @param randomNumberOrigin 开始值 + * @param randomNumberBound 结束值 ( 最大值范围 ) + * @return 指定范围随机 long[] + */ + public static long[] longs(final int streamSize, final long randomNumberOrigin, final long randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) { + return null; + } else if (streamSize < 0) { + return null; + } +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +// LongStream longStream = new Random().longs(streamSize, randomNumberOrigin, randomNumberBound); +// return longStream.toArray(); +// } else { + try { + long[] longs = new long[streamSize]; + for (int i = 0; i < streamSize; i++) { + longs[i] = nextLongRange(randomNumberOrigin, randomNumberBound); + } + return longs; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "longs"); + } + return null; +// } + } + + /** + * 获取随机 double[] + * @param streamSize 数组长度 + * @param randomNumberOrigin 开始值 + * @param randomNumberBound 结束值 ( 最大值范围 ) + * @return 指定范围随机 double[] + */ + public static double[] doubles(final int streamSize, final double randomNumberOrigin, final double randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) { + return null; + } else if (streamSize < 0) { + return null; + } +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +// DoubleStream doubleStream = new Random().doubles(streamSize, randomNumberOrigin, randomNumberBound); +// return doubleStream.toArray(); +// } else { + try { + double[] doubles = new double[streamSize]; + for (int i = 0; i < streamSize; i++) { + doubles[i] = nextDoubleRange(randomNumberOrigin, randomNumberBound); + } + return doubles; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "doubles"); + } + return null; +// } + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/Reflect2Utils.java b/lib/DevApp/src/main/java/dev/utils/common/Reflect2Utils.java new file mode 100644 index 0000000000..3ba1cbc3ec --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/Reflect2Utils.java @@ -0,0 +1,594 @@ +package dev.utils.common; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import dev.utils.JCLogUtils; + +/** + * detail: 反射相关工具类 + * @author Ttt + *
+ *     有两个方法: getMethod, getDeclaredMethod
+ *     

+ * getDeclaredMethod() 获取的是类自身声明的所有方法, 包含 public、protected 和 private 方法 + * getMethod() 获取的是类的所有共有方法, 这就包括自身的所有 public 方法, 和从基类继承的、从接口实现的所有 public 方法 + *

+ * getMethod 只能调用 public 声明的方法, 而 getDeclaredMethod 基本可以调用任何类型声明的方法 + * 反射多用 getDeclaredMethod, 尽量少用 getMethod + *
+ */ +public final class Reflect2Utils { + + private Reflect2Utils() { + } + + // 日志 TAG + private static final String TAG = Reflect2Utils.class.getSimpleName(); + + // ============ + // = 对象变量 = + // ============ + + /** + * 设置某个对象变量值 ( 可设置静态变量 ) + * @param object 对象 + * @param fieldName 属性名 + * @param value 字段值 + * @return {@code true} success, {@code false} fail + */ + public static boolean setProperty(final Object object, final String fieldName, final Object value) { + if (object == null || fieldName == null) return false; + try { + Field field = object.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(object, value); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "setProperty"); + } + return false; + } + + /** + * 获取某个对象的变量 ( 可获取静态变量 ) + * @param object 对象 + * @param fieldName 属性名 + * @param 泛型 + * @return 该变量对象 + */ + public static T getProperty(final Object object, final String fieldName) { + if (object == null || fieldName == null) return null; + try { + Field field = object.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return (T) field.get(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getProperty"); + } + return null; + } + + // ======================== + // = 获取某个类的静态变量 = + // ======================== + + /** + * 获取某个类的静态变量 ( 只能获取静态变量 ) + * @param object 对象 + * @param fieldName 属性名 + * @param 泛型 + * @return 该变量对象 + */ + public static T getStaticProperty(final Object object, final String fieldName) { + if (object == null) return null; + return getStaticProperty(object.getClass().getName(), fieldName); + } + + /** + * 获取某个类的静态变量 ( 只能获取静态变量 ) + * @param clazz 类 + * @param fieldName 属性名 + * @param 泛型 + * @return 该变量对象 + */ + public static T getStaticProperty(final Class clazz, final String fieldName) { + if (clazz == null) return null; + return getStaticProperty(clazz.getName(), fieldName); + } + + /** + * 获取某个类的静态变量 ( 只能获取静态变量 ) + * @param className 类名 + * @param fieldName 属性名 + * @param 泛型 + * @return 该变量对象 + */ + public static T getStaticProperty(final String className, final String fieldName) { + if (className == null || fieldName == null) return null; + try { + Class clazz = Class.forName(className); + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return (T) field.get(clazz); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getStaticProperty"); + } + return null; + } + + // ==================== + // = 执行某个对象方法 = + // ==================== + + /** + * 执行某个对象方法 ( 可执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeMethod(final Object object, final String methodName) { + return invokeMethod(object, methodName, null, null); + } + + /** + * 执行某个对象方法 ( 可执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param args 参数 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeMethod(final Object object, final String methodName, final Object[] args) { + return invokeMethod(object, methodName, args, getArgsClass(args)); + } + + /** + * 执行某个对象方法 ( 可执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param args 参数 + * @param argsClass 参数类型 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeMethod(final Object object, final String methodName, final Object[] args, final Class[] argsClass) { + if (object == null || methodName == null) return null; + try { + Class clazz = object.getClass(); + if (args != null && argsClass != null) { // 参数、参数类型不为 null, 并且数量相等 + if (args.length == argsClass.length && args.length != 0) { + Method method = clazz.getDeclaredMethod(methodName, argsClass); + method.setAccessible(true); + return (T) method.invoke(object, args); + } + } else { + // 无参数、参数类型, 才执行 + if (args == null && argsClass == null) { + Method method = clazz.getDeclaredMethod(methodName); + method.setAccessible(true); + return (T) method.invoke(object); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "invokeMethod"); + } + return null; + } + + // ======================== + // = 执行某个类的静态方法 = + // ======================== + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Object object, final String methodName) { + if (object == null) return null; + return invokeStaticMethod(object.getClass().getName(), methodName, null, null); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param args 参数数组 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Object object, final String methodName, final Object[] args) { + if (object == null) return null; + return invokeStaticMethod(object.getClass().getName(), methodName, args, getArgsClass(args)); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param args 参数数组 + * @param argsClass 参数类型 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Object object, final String methodName, final Object[] args, final Class[] argsClass) { + if (object == null) return null; + return invokeStaticMethod(object.getClass().getName(), methodName, args, argsClass); + } + + // = + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param clazz 类 + * @param methodName 方法名 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Class clazz, final String methodName) { + if (clazz == null) return null; + return invokeStaticMethod(clazz.getName(), methodName, null, null); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param clazz 类 + * @param methodName 方法名 + * @param args 参数数组 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Class clazz, final String methodName, final Object[] args) { + if (clazz == null) return null; + return invokeStaticMethod(clazz.getName(), methodName, args, getArgsClass(args)); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param clazz 类 + * @param methodName 方法名 + * @param args 参数数组 + * @param argsClass 参数类型 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Class clazz, final String methodName, final Object[] args, final Class[] argsClass) { + if (clazz == null) return null; + return invokeStaticMethod(clazz.getName(), methodName, args, argsClass); + } + + // = + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param className 类名 + * @param methodName 方法名 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final String className, final String methodName) { + if (className == null) return null; + return invokeStaticMethod(className, methodName, null, null); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param className 类名 + * @param methodName 方法名 + * @param args 参数数组 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final String className, final String methodName, final Object[] args) { + if (className == null) return null; + return invokeStaticMethod(className, methodName, args, getArgsClass(args)); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param className 类名 + * @param methodName 方法名 + * @param args 参数数组 + * @param argsClass 参数类型 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final String className, final String methodName, final Object[] args, final Class[] argsClass) { + if (className == null || methodName == null) return null; + try { + Class clazz = Class.forName(className); + if (args != null && argsClass != null) { // 参数、参数类型不为 null, 并且数量相等 + if (args.length == argsClass.length && args.length != 0) { + Method method = clazz.getDeclaredMethod(methodName, argsClass); + method.setAccessible(true); + return (T) method.invoke(clazz, args); + } + } else { + // 无参数、参数类型, 才执行 + if (args == null && argsClass == null) { + Method method = clazz.getDeclaredMethod(methodName); + method.setAccessible(true); + return (T) method.invoke(clazz); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "invokeStaticMethod"); + } + return null; + } + + // ============================= + // = 新建实例 ( 构造函数创建 ) = + // ============================= + + /** + * 新建实例 ( 构造函数创建 ) + * @param object 对象 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Object object) { + if (object == null) return null; + return newInstance(object.getClass().getName(), null, null); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param object 对象 + * @param args 参数 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Object object, final Object[] args) { + if (object == null) return null; + return newInstance(object.getClass().getName(), args, getArgsClass(args)); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param object 对象 + * @param args 参数 + * @param argsClass 参数类型 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Object object, final Object[] args, final Class[] argsClass) { + if (object == null) return null; + return newInstance(object.getClass().getName(), args, argsClass); + } + + // = + + /** + * 新建实例 ( 构造函数创建 ) + * @param clazz 类 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Class clazz) { + if (clazz == null) return null; + return newInstance(clazz.getName(), null, null); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param clazz 类 + * @param args 参数 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Class clazz, final Object[] args) { + if (clazz == null) return null; + return newInstance(clazz.getName(), args, getArgsClass(args)); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param clazz 类 + * @param args 参数 + * @param argsClass 参数类型 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Class clazz, final Object[] args, final Class[] argsClass) { + if (clazz == null) return null; + return newInstance(clazz.getName(), args, argsClass); + } + + // = + + /** + * 新建实例 ( 构造函数创建 ) + * @param className 类名 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final String className) { + if (className == null) return null; + return newInstance(className, null, null); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param className 类名 + * @param args 参数 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final String className, final Object[] args) { + if (className == null) return null; + return newInstance(className, args, getArgsClass(args)); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param className 类名 + * @param args 参数 + * @param argsClass 参数类型 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final String className, final Object[] args, final Class[] argsClass) { + if (className == null) return null; + try { + Class newoneClass = Class.forName(className); + if (args == null) { + return (T) newoneClass.newInstance(); + } else { + Constructor cons = newoneClass.getConstructor(argsClass); + return (T) cons.newInstance(args); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "newInstance"); + } + return null; + } + + // = + + /** + * 是不是某个类的实例 + * @param object 实例 + * @param clazz 待判断类 + * @return 如果 obj 是此类的实例, 则返回 true + */ + public static boolean isInstance(final Object object, final Class clazz) { + if (object == null || clazz == null) return false; + try { + return clazz.isInstance(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isInstance"); + } + return false; + } + + /** + * 获取参数类型 + * @param args 参数 + * @return 参数类型数组 + */ + public static Class[] getArgsClass(final Object... args) { + if (args != null) { + try { + Class[] argsClass = new Class[args.length]; + for (int i = 0, len = args.length; i < len; i++) { + argsClass[i] = args[i].getClass(); + } + return argsClass; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getArgsClass"); + } + } + return new Class[0]; + } + + // = + + /** + * 获取某个对象的变量 + *
+     *     例: 获取父类中的变量
+     *     Object object = 对象;
+     *     getObject(getDeclaredFieldParent(object, "父类中变量名"), object);
+     * 
+ * @param object 对象 + * @param field {@link Field} + * @param 泛型 + * @return 该变量对象 + */ + public static T getProperty(final Object object, final Field field) { + if (object == null || field == null) return null; + try { + field.setAccessible(true); + return (T) field.get(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getProperty"); + } + return null; + } + + /** + * 获取父类中的变量对象 + * @param object 子类对象 + * @param fieldName 父类中的属性名 + * @param 泛型 + * @return 父类中的变量对象 + */ + public static T getPropertyByParent(final Object object, final String fieldName) { + return getPropertyByParent(object, fieldName, 1); + } + + /** + * 获取父类中的变量对象 + * @param object 子类对象 + * @param fieldName 父类中的属性名 + * @param fieldNumber 字段出现次数, 如果父类还有父类, 并且有相同变量名, 设置负数 一直会跟到最后的变量 + * @param 泛型 + * @return 父类中的变量对象 + */ + public static T getPropertyByParent(final Object object, final String fieldName, final int fieldNumber) { + if (object == null || fieldName == null) return null; + try { + Field field = getDeclaredFieldParent(object, fieldName, fieldNumber); + return getProperty(object, field); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPropertyByParent"); + } + return null; + } + + /** + * 获取父类中的变量对象 ( 循环向上转型, 获取对象的 DeclaredField) + * @param object 对象 + * @param fieldName 属性名 + * @return {@link Field} + */ + public static Field getDeclaredFieldParent(final Object object, final String fieldName) { + return getDeclaredFieldParent(object, fieldName, 1); + } + + /** + * 获取父类中的变量对象 ( 循环向上转型, 获取对象的 DeclaredField) + * @param object 子类对象 + * @param fieldName 父类中的属性名 + * @param fieldNumber 字段出现次数, 如果父类还有父类, 并且有相同变量名, 设置负数 一直会跟到最后的变量 + * @return {@link Field} 父类中的属性对象 + */ + public static Field getDeclaredFieldParent(final Object object, final String fieldName, final int fieldNumber) { + if (object == null || fieldName == null) return null; + try { + if (fieldNumber == 0) return null; + // 获取当前出现次数 + int number = 0; + // 限制值 + int limitNumber = (fieldNumber >= 0) ? fieldNumber : Integer.MAX_VALUE; + // = + Field field = null; + Class clazz = object.getClass(); + for (; clazz != Object.class; clazz = clazz.getSuperclass()) { + try { + field = clazz.getDeclaredField(fieldName); + number++; + } catch (Exception e) { + // 这里甚么都不要做, 并且这里的异常必须这样写, 不能抛出去 + // 如果这里的异常打印或者往外抛, 则就不会执行 clazz = clazz.getSuperclass(), 最后就不会进入到父类中了 + } + if (number >= limitNumber) { + return field; + } + } + // 负数表示跟到最后 + if (fieldNumber < 0) { + return field; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDeclaredFieldParent"); + } + return null; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/ReflectUtils.java b/lib/DevApp/src/main/java/dev/utils/common/ReflectUtils.java similarity index 55% rename from DevLibUtils/src/main/java/dev/utils/common/ReflectUtils.java rename to lib/DevApp/src/main/java/dev/utils/common/ReflectUtils.java index 1340621631..93800beff3 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/ReflectUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/common/ReflectUtils.java @@ -3,40 +3,45 @@ import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; + +import dev.utils.JCLogUtils; /** * detail: 反射相关工具类 - * Created by Ttt - * == - * 有两个方法:getMethod,getDeclaredMethod。看了下说明大概的意思就是getMethod只能调用public声明的方法,而getDeclaredMethod基本可以调用任何类型声明的方法 - * 反射多用getDeclaredMethod,尽量少用getMethod。 + * @author Ttt */ public final class ReflectUtils { - private final Class type; + // 日志 TAG + private static final String TAG = ReflectUtils.class.getSimpleName(); + + private final Class mType; - private final Object object; + private final Object mObject; private ReflectUtils(final Class type) { this(type, type); } - private ReflectUtils(final Class type, Object object) { - this.type = type; - this.object = object; + private ReflectUtils(final Class type, final Object object) { + this.mType = type; + this.mObject = object; } - // ================= - // ==== reflect ==== - // ================= + // =========== + // = reflect = + // =========== /** * 设置要反射的类 @@ -63,9 +68,8 @@ public static ReflectUtils reflect(final String className, final ClassLoader cla * 设置要反射的类 * @param clazz 类的类型 * @return {@link ReflectUtils} - * @throws ReflectException 反射异常 */ - public static ReflectUtils reflect(final Class clazz) throws ReflectException { + public static ReflectUtils reflect(final Class clazz) { return new ReflectUtils(clazz); } @@ -73,37 +77,56 @@ public static ReflectUtils reflect(final Class clazz) throws ReflectException * 设置要反射的类 * @param object 类对象 * @return {@link ReflectUtils} - * @throws ReflectException 反射异常 */ - public static ReflectUtils reflect(final Object object) throws ReflectException { + public static ReflectUtils reflect(final Object object) { return new ReflectUtils(object == null ? Object.class : object.getClass(), object); } - private static Class forName(String className) { + // = + + /** + * 获取 Class + * @param className 类名 + * @param 未知类型 + * @return 指定类 + * @throws ReflectException 反射异常 + */ + private static Class forName(final String className) throws ReflectException { try { return Class.forName(className); - } catch (ClassNotFoundException e) { + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "forName"); throw new ReflectException(e); } } - private static Class forName(String name, ClassLoader classLoader) { + /** + * 获取 Class + * @param name 类名 + * @param classLoader 类加载器 + * @param 未知类型 + * @return 指定类 + * @throws ReflectException 反射异常 + */ + private static Class forName(final String name, final ClassLoader classLoader) throws ReflectException { try { return Class.forName(name, true, classLoader); - } catch (ClassNotFoundException e) { + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "forName"); throw new ReflectException(e); } } - // ===================== - // ==== newInstance ==== - // ===================== + // =============== + // = newInstance = + // =============== /** * 实例化反射对象 * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 */ - public ReflectUtils newInstance() { + public ReflectUtils newInstance() throws ReflectException { return newInstance(new Object[0]); } @@ -111,8 +134,9 @@ public ReflectUtils newInstance() { * 实例化反射对象 * @param args 实例化需要的参数 * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 */ - public ReflectUtils newInstance(Object... args) { + public ReflectUtils newInstance(final Object... args) throws ReflectException { Class[] types = getArgsType(args); try { Constructor constructor = type().getDeclaredConstructor(types); @@ -130,9 +154,17 @@ public ReflectUtils newInstance(Object... args) { sortConstructors(list); return newInstance(list.get(0), args); } + } catch (Exception e) { + throw new ReflectException(e); } } + /** + * 获取参数类型 + * @param args 参数数组 + * @param 未知类型 + * @return 参数类型数组 + */ private Class[] getArgsType(final Object... args) { if (args == null) return new Class[0]; Class[] result = new Class[args.length]; @@ -143,7 +175,12 @@ private Class[] getArgsType(final Object... args) { return result; } - private void sortConstructors(List> list) { + /** + * 进行排序 + * @param list 类构造函数信息集合 + */ + private void sortConstructors(final List> list) { + if (list == null) return; Collections.sort(list, new Comparator>() { @Override public int compare(Constructor o1, Constructor o2) { @@ -164,28 +201,37 @@ public int compare(Constructor o1, Constructor o2) { }); } - private ReflectUtils newInstance(final Constructor constructor, final Object... args) { + /** + * 获取实例对象 + * @param constructor 类构造函数信息 + * @param args 构造参数数组 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + private ReflectUtils newInstance(final Constructor constructor, final Object... args) throws ReflectException { try { return new ReflectUtils(constructor.getDeclaringClass(), accessible(constructor).newInstance(args)); } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "newInstance"); throw new ReflectException(e); } } - // =============== - // ==== field ==== - // =============== + // ========= + // = field = + // ========= /** * 设置反射的字段 * @param name 字段名 * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 */ - public ReflectUtils field(final String name) { + public ReflectUtils field(final String name) throws ReflectException { try { Field field = getField(name); - return new ReflectUtils(field.getType(), field.get(object)); - } catch (IllegalAccessException e) { + return new ReflectUtils(field.getType(), field.get(mObject)); + } catch (Exception e) { throw new ReflectException(e); } } @@ -195,18 +241,25 @@ public ReflectUtils field(final String name) { * @param name 字段名 * @param value 字段值 * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 */ - public ReflectUtils field(String name, Object value) { + public ReflectUtils field(final String name, final Object value) throws ReflectException { try { Field field = getField(name); - field.set(object, unwrap(value)); + field.set(mObject, unwrap(value)); return this; } catch (Exception e) { throw new ReflectException(e); } } - private Field getField(String name) throws IllegalAccessException { + /** + * 获取 Field 对象 + * @param name 字段名 + * @return {@link Field} + * @throws ReflectException 反射异常 + */ + private Field getField(final String name) throws ReflectException { Field field = getAccessibleField(name); if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) { try { @@ -214,13 +267,21 @@ private Field getField(String name) throws IllegalAccessException { modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); } catch (NoSuchFieldException ignore) { - // runs in android will happen + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getField"); + throw new ReflectException(e); } } return field; } - private Field getAccessibleField(String name) { + /** + * 获取可访问字段, 返回 Field 对象 + * @param name 字段名 + * @return {@link Field} + * @throws ReflectException 反射异常 + */ + private Field getAccessibleField(final String name) throws ReflectException { Class type = type(); try { return accessible(type.getField(name)); @@ -232,103 +293,47 @@ private Field getAccessibleField(String name) { } type = type.getSuperclass(); } while (type != null); + JCLogUtils.eTag(TAG, e, "getAccessibleField"); + throw new ReflectException(e); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAccessibleField"); throw new ReflectException(e); } } - private Object unwrap(Object object) { + /** + * 获取对象 + * @param object 对象 + * @return 需要反射的对象 + */ + private Object unwrap(final Object object) { if (object instanceof ReflectUtils) { return ((ReflectUtils) object).get(); } return object; } - // == - - /** - * 获取Object 对象 - * 例: 获取父类中的变量 - * Object obj = 对象; - * getObject(getDeclaredFieldBase(obj, "父类中变量名"), obj); - * @param field - * @param obj - * @return - */ - public static Object getObject(Field field, Object obj){ - try { - field.setAccessible(true); - Object out = field.get(obj); - return out; - } catch (Exception e){ - } - return null; - } + // = /** * 设置枚举值 - * @param clas 类型 - * @param name - * @param val + * @param clazz 类型 + * @param name 字段名 + * @param value 字段值 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 */ - public ReflectUtils setEnumVal(Class clas, String name, String val){ + public ReflectUtils setEnumVal(final Class clazz, final String name, final String value) throws ReflectException { try { - return field(name, Enum.valueOf((Class) clas, val)); + return field(name, Enum.valueOf((Class) clazz, value)); } catch (Exception e) { + throw new ReflectException(e); } - return this; - } - - /** - * 通过反射获取全部字段 - * @param obj - * @param name - * @return - * @throws Exception - */ - public static Object getDeclaredField(Object obj, String name) throws Exception { - Field f = obj.getClass().getDeclaredField(name); - f.setAccessible(true); - Object out = f.get(obj); - return out; - } - - /** - * 循环向上转型, 获取对象的 DeclaredField - * @param object - * @param fieldName - * @return - */ - public static Field getDeclaredFieldBase(Object object, String fieldName) { - return getDeclaredFieldBase(object, fieldName, false); - } - - /** - * 循环向上转型, 获取对象的 DeclaredField - * @param object : 子类对象 - * @param fieldName : 父类中的属性名 - * @param isSuper 是否一直跟到最后, 如果父类还有父类,并且有相同变量名, 则设置isSuper = true,一直会跟到最后的变量 - * @return 父类中的属性对象 - */ - public static Field getDeclaredFieldBase(Object object, String fieldName, boolean isSuper){ - Field field = null ; - Class clazz = object.getClass() ; - for(; clazz != Object.class ; clazz = clazz.getSuperclass()) { - try { - field = clazz.getDeclaredField(fieldName); - if (!isSuper){ - return field; - } - } catch (Exception e) { - //这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。 - //如果这里的异常打印或者往外抛,则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了 - } - } - return field; } - // ================ - // ==== method ==== - // ================ + // ========== + // = method = + // ========== /** * 设置反射的方法 @@ -351,48 +356,72 @@ public ReflectUtils method(final String name, final Object... args) throws Refle Class[] types = getArgsType(args); try { Method method = exactMethod(name, types); - return method(method, object, args); - } catch (NoSuchMethodException e) { + return method(method, mObject, args); + } catch (Exception e) { try { Method method = similarMethod(name, types); - return method(method, object, args); - } catch (NoSuchMethodException e1) { + return method(method, mObject, args); + } catch (Exception e1) { throw new ReflectException(e1); } } } - private ReflectUtils method(final Method method, final Object obj, final Object... args) { + /** + * 设置反射的方法处理 + * @param method 方法 + * @param object 对象 + * @param args 参数 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + private ReflectUtils method(final Method method, final Object object, final Object... args) throws ReflectException { try { accessible(method); if (method.getReturnType() == void.class) { - method.invoke(obj, args); - return reflect(obj); + method.invoke(object, args); + return reflect(object); } else { - return reflect(method.invoke(obj, args)); + return reflect(method.invoke(object, args)); } } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "method"); throw new ReflectException(e); } } - private Method exactMethod(final String name, final Class[] types) throws NoSuchMethodException { + /** + * 获取准确参数的方法 + * @param name 方法 + * @param types 参数类型 + * @return {@link Method} + * @throws ReflectException 反射异常 + */ + private Method exactMethod(final String name, final Class[] types) throws ReflectException { Class type = type(); try { return type.getMethod(name, types); - } catch (NoSuchMethodException e) { + } catch (Exception e) { do { try { return type.getDeclaredMethod(name, types); - } catch (NoSuchMethodException ignore) { + } catch (Exception ignore) { } type = type.getSuperclass(); } while (type != null); - throw new NoSuchMethodException(); + JCLogUtils.eTag(TAG, e, "exactMethod"); + throw new ReflectException(e); } } - private Method similarMethod(final String name, final Class[] types) throws NoSuchMethodException { + /** + * 获取相似参数的方法 + * @param name 方法 + * @param types 参数类型 + * @return {@link Method} + * @throws ReflectException 反射异常 + */ + private Method similarMethod(final String name, final Class[] types) throws ReflectException { Class type = type(); List methods = new ArrayList<>(); for (Method method : type.getMethods()) { @@ -416,11 +445,15 @@ private Method similarMethod(final String name, final Class[] types) throws N } type = type.getSuperclass(); } while (type != null); - - throw new NoSuchMethodException("No similar method " + name + " with params " + Arrays.toString(types) + " could be found on type " + type() + "."); + throw new ReflectException(String.format("No similar method %s with params %s could be found on type %s", name, Arrays.toString(types), type())); } + /** + * 进行方法排序 + * @param methods 方法集合 + */ private void sortMethods(final List methods) { + if (methods == null) return; Collections.sort(methods, new Comparator() { @Override public int compare(Method o1, Method o2) { @@ -441,12 +474,25 @@ public int compare(Method o1, Method o2) { }); } + /** + * 判断是否相似方法 + * @param possiblyMatchingMethod 可能的匹配方法 + * @param desiredMethodName 期望方法名 + * @param desiredParamTypes 所需参数类型 + * @return {@code true} yes, {@code false} no + */ private boolean isSimilarSignature(final Method possiblyMatchingMethod, final String desiredMethodName, final Class[] desiredParamTypes) { return possiblyMatchingMethod.getName().equals(desiredMethodName) && match(possiblyMatchingMethod.getParameterTypes(), desiredParamTypes); } + /** + * 对比处理, 判断是否一样 + * @param declaredTypes 声明类型 + * @param actualTypes 实际类型 + * @return {@code true} yes, {@code false} no + */ private boolean match(final Class[] declaredTypes, final Class[] actualTypes) { - if (declaredTypes.length == actualTypes.length) { + if (declaredTypes != null && actualTypes != null && declaredTypes.length == actualTypes.length) { for (int i = 0; i < actualTypes.length; i++) { if (actualTypes[i] == NULL.class || wrapper(declaredTypes[i]).isAssignableFrom(wrapper(actualTypes[i]))) { continue; @@ -459,7 +505,13 @@ private boolean match(final Class[] declaredTypes, final Class[] actualTyp } } - private T accessible(T accessible) { + /** + * 设置对象可访问处理 + * @param accessible 对象 + * @param 泛型 + * @return 传入的对象 + */ + private T accessible(final T accessible) { if (accessible == null) return null; if (accessible instanceof Member) { Member member = (Member) accessible; @@ -471,10 +523,78 @@ private T accessible(T accessible) { return accessible; } + // ======== + // = 代理 = + // ======== + + /** + * 根据类, 代理创建并返回对象 + * @param proxyType 代理类 + * @param

泛型 + * @return 代理的对象 + */ + public

P proxy(final Class

proxyType) { + if (proxyType == null || mObject == null) return null; + final boolean isMap = (mObject instanceof Map); + final InvocationHandler handler = new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + String name = method.getName(); + try { + return reflect(mObject).method(name, args).get(); + } catch (Exception e) { + if (isMap) { + Map map = (Map) mObject; + int length = (args == null ? 0 : args.length); + if (length == 0 && name.startsWith("get")) { + return map.get(property(name.substring(3))); + } else if (length == 0 && name.startsWith("is")) { + return map.get(property(name.substring(2))); + } else if (length == 1 && name.startsWith("set")) { + map.put(property(name.substring(3)), args[0]); + return null; + } + } + JCLogUtils.eTag(TAG, e, "proxy"); + } + return null; + } + }; + return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), new Class[]{proxyType}, handler); + } + + /** + * 获取实体类属性名 get/set + * @param str 属性名 + * @return 属性名字 + */ + private String property(final String str) { + int length = str.length(); + if (length == 0) { + return ""; + } else if (length == 1) { + return str.toLowerCase(); + } else { + return str.substring(0, 1).toLowerCase() + str.substring(1); + } + } + + // = + + /** + * 获取类型 + * @return {@link Class} + */ public Class type() { - return type; + return mType; } + /** + * 获取类型 + * @param type {@link Class} + * @param 未知类型 + * @return {@link Class} 类所属类型 + */ private Class wrapper(final Class type) { if (type == null) { return null; @@ -504,37 +624,66 @@ private Class wrapper(final Class type) { /** * 获取反射想要获取的 - * @param 返回的范型 + * @param 泛型 * @return 反射想要获取的 */ - @SuppressWarnings("unchecked") public T get() { - return (T) object; + return (T) mObject; } + /** + * 获取 HashCode + * @return hashCode + */ @Override public int hashCode() { - return object.hashCode(); + return this.mObject != null ? mObject.hashCode() : 0; } + /** + * 判断反射的两个对象是否一样 + * @param object 对象 + * @return {@code true} yes, {@code false} no + */ @Override - public boolean equals(Object obj) { - return obj instanceof ReflectUtils && object.equals(((ReflectUtils) obj).get()); + public boolean equals(final Object object) { + if (this.mObject == null && object == null) { + return true; + } else { + if (this.mObject != null && object != null) { + if (object instanceof ReflectUtils) { + return this.mObject.equals(((ReflectUtils) object).get()); + } else { + this.mObject.equals(object); + } + } + return false; + } } + /** + * 获取反射获取的对象 + * @return {@link Object#toString()} + */ @Override public String toString() { - return object.toString(); + return this.mObject != null ? mObject.toString() : null; } - // == + // = - /** 内部标记 null */ + /** + * detail: 内部标记 null + * @author Ttt + */ private static class NULL { } - /** 定义反射异常类 */ - public static class ReflectException extends RuntimeException { + /** + * detail: 定义 ReflectUtils 工具异常类 + * @author Ttt + */ + public static class ReflectException extends Exception { private static final long serialVersionUID = 858774075258496016L; @@ -550,4 +699,4 @@ public ReflectException(Throwable cause) { super(cause); } } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/ScaleUtils.java b/lib/DevApp/src/main/java/dev/utils/common/ScaleUtils.java new file mode 100644 index 0000000000..850fd1eadf --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/ScaleUtils.java @@ -0,0 +1,520 @@ +package dev.utils.common; + +import dev.utils.JCLogUtils; + +/** + * detail: 计算比例工具类 + * @author Ttt + */ +public final class ScaleUtils { + + private ScaleUtils() { + } + + // 日志 TAG + private static final String TAG = ScaleUtils.class.getSimpleName(); + + // ======= + // = int = + // ======= + + /** + * 计算缩放比例 - 根据宽度比例转换高度 + * @param targetWidth 需要的最终宽度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return int[] { 宽度, 高度 } + */ + public static int[] calcScaleToWidth(final int targetWidth, final int currentWidth, final int currentHeight) { + try { + if (currentWidth == 0) { + return new int[]{0, 0}; + } + // 计算比例 + float scale = ((float) targetWidth) / ((float) currentWidth); + // 计算缩放后的高度 + int scaleHeight = (int) (scale * (float) currentHeight); + // 返回对应的数据 + return new int[]{targetWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToWidth"); + } + return null; + } + + /** + * 计算缩放比例 - 根据高度比例转换宽度 + * @param targetHeight 需要的最终高度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return int[] { 宽度, 高度 } + */ + public static int[] calcScaleToHeight(final int targetHeight, final int currentWidth, final int currentHeight) { + try { + if (currentHeight == 0) { + return new int[]{0, 0}; + } + // 计算比例 + float scale = ((float) targetHeight) / ((float) currentHeight); + // 计算缩放后的宽度 + int scaleWidth = (int) (scale * (float) currentWidth); + // 返回对应的数据 + return new int[]{scaleWidth, targetHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToHeight"); + } + return null; + } + + /** + * 通过宽度、高度根据对应的比例, 转换成对应的比例宽度高度 - 智能转换 + * @param width 宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return int[] { 宽度, 高度 } + */ + public static int[] calcWidthHeightToScale(final int width, final int height, final float widthScale, final float heightScale) { + try { + // 如果宽度的比例, 大于等于高度比例 + if (widthScale >= heightScale) { // 以宽度为基准 + // 设置宽度, 以宽度为基准 + int scaleWidth = width; + // 计算宽度 + int scaleHeight = (int) (((float) scaleWidth) * (heightScale / widthScale)); + // 返回对应的比例 + return new int[]{scaleWidth, scaleHeight}; + } else { // 以高度为基准 + // 设置高度 + int scaleHeight = height; + // 同步缩放比例 + int scaleWidth = (int) (((float) scaleHeight) * (widthScale / heightScale)); + // 返回对应的比例 + return new int[]{scaleWidth, scaleHeight}; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthHeightToScale"); + } + return null; + } + + /** + * 以宽度为基准, 转换对应比例的高度 + * @param width 宽度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return int[] { 宽度, 高度 } + */ + public static int[] calcWidthToScale(final int width, final float widthScale, final float heightScale) { + try { + // 设置宽度 + int scaleWidth = width; + // 计算高度 + int scaleHeight = (int) (((float) scaleWidth) * (heightScale / widthScale)); + // 返回对应的比例 + return new int[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthToScale"); + } + return null; + } + + /** + * 以高度为基准, 转换对应比例的宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return int[] { 宽度, 高度 } + */ + public static int[] calcHeightToScale(final int height, final float widthScale, final float heightScale) { + try { + // 设置高度 + int scaleHeight = height; + // 计算宽度 + int scaleWidth = (int) (((float) scaleHeight) * (widthScale / heightScale)); + // 返回对应的比例 + return new int[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcHeightToScale"); + } + return null; + } + + // ========== + // = double = + // ========== + + /** + * 计算缩放比例 - 根据宽度比例转换高度 + * @param targetWidth 需要的最终宽度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return double[] { 宽度, 高度 } + */ + public static double[] calcScaleToWidth(final double targetWidth, final double currentWidth, final double currentHeight) { + try { + if (currentWidth == 0d) { + return new double[]{0d, 0d}; + } + // 计算比例 + double scale = targetWidth / currentWidth; + // 计算缩放后的高度 + double scaleHeight = scale * currentHeight; + // 返回对应的数据 + return new double[]{targetWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToWidth"); + } + return null; + } + + /** + * 计算缩放比例 - 根据高度比例转换宽度 + * @param targetHeight 需要的最终高度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return double[] { 宽度, 高度 } + */ + public static double[] calcScaleToHeight(final double targetHeight, final double currentWidth, final double currentHeight) { + try { + if (currentHeight == 0d) { + return new double[]{0d, 0d}; + } + // 计算比例 + double scale = targetHeight / currentHeight; + // 计算缩放后的宽度 + double scaleWidth = scale * currentWidth; + // 返回对应的数据 + return new double[]{scaleWidth, targetHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToHeight"); + } + return null; + } + + /** + * 通过宽度、高度根据对应的比例, 转换成对应的比例宽度高度 - 智能转换 + * @param width 宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return double[] { 宽度, 高度 } + */ + public static double[] calcWidthHeightToScale(final double width, final double height, final double widthScale, final double heightScale) { + try { + // 如果宽度的比例, 大于等于高度比例 + if (widthScale >= heightScale) { // 以宽度为基准 + // 设置宽度, 以宽度为基准 + double scaleWidth = width; + // 计算宽度 + double scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new double[]{scaleWidth, scaleHeight}; + } else { // 以高度为基准 + // 设置高度 + double scaleHeight = height; + // 同步缩放比例 + double scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new double[]{scaleWidth, scaleHeight}; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthHeightToScale"); + } + return null; + } + + /** + * 以宽度为基准, 转换对应比例的高度 + * @param width 宽度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return double[] { 宽度, 高度 } + */ + public static double[] calcWidthToScale(final double width, final double widthScale, final double heightScale) { + try { + // 设置宽度 + double scaleWidth = width; + // 计算高度 + double scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new double[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthToScale"); + } + return null; + } + + /** + * 以高度为基准, 转换对应比例的宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return double[] { 宽度, 高度 } + */ + public static double[] calcHeightToScale(final double height, final double widthScale, final double heightScale) { + try { + // 设置高度 + double scaleHeight = height; + // 计算宽度 + double scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new double[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcHeightToScale"); + } + return null; + } + + // ========= + // = long = + // ========= + + /** + * 计算缩放比例 - 根据宽度比例转换高度 + * @param targetWidth 需要的最终宽度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return long[] { 宽度, 高度 } + */ + public static long[] calcScaleToWidth(final long targetWidth, final long currentWidth, final long currentHeight) { + try { + if (currentWidth == 0L) { + return new long[]{0L, 0L}; + } + // 计算比例 + long scale = targetWidth / currentWidth; + // 计算缩放后的高度 + long scaleHeight = scale * currentHeight; + // 返回对应的数据 + return new long[]{targetWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToWidth"); + } + return null; + } + + /** + * 计算缩放比例 - 根据高度比例转换宽度 + * @param targetHeight 需要的最终高度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return long[] { 宽度, 高度 } + */ + public static long[] calcScaleToHeight(final long targetHeight, final long currentWidth, final long currentHeight) { + try { + if (currentHeight == 0L) { + return new long[]{0L, 0L}; + } + // 计算比例 + long scale = targetHeight / currentHeight; + // 计算缩放后的宽度 + long scaleWidth = scale * currentWidth; + // 返回对应的数据 + return new long[]{scaleWidth, targetHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToHeight"); + } + return null; + } + + /** + * 通过宽度、高度根据对应的比例, 转换成对应的比例宽度高度 - 智能转换 + * @param width 宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return long[] { 宽度, 高度 } + */ + public static long[] calcWidthHeightToScale(final long width, final long height, final long widthScale, final long heightScale) { + try { + // 如果宽度的比例, 大于等于高度比例 + if (widthScale >= heightScale) { // 以宽度为基准 + // 设置宽度, 以宽度为基准 + long scaleWidth = width; + // 计算宽度 + long scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new long[]{scaleWidth, scaleHeight}; + } else { // 以高度为基准 + // 设置高度 + long scaleHeight = height; + // 同步缩放比例 + long scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new long[]{scaleWidth, scaleHeight}; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthHeightToScale"); + } + return null; + } + + /** + * 以宽度为基准, 转换对应比例的高度 + * @param width 宽度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return long[] { 宽度, 高度 } + */ + public static long[] calcWidthToScale(final long width, final long widthScale, final long heightScale) { + try { + // 设置宽度 + long scaleWidth = width; + // 计算高度 + long scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new long[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthToScale"); + } + return null; + } + + /** + * 以高度为基准, 转换对应比例的宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return long[] { 宽度, 高度 } + */ + public static long[] calcHeightToScale(final long height, final long widthScale, final long heightScale) { + try { + // 设置高度 + long scaleHeight = height; + // 计算宽度 + long scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new long[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcHeightToScale"); + } + return null; + } + + // ========= + // = float = + // ========= + + /** + * 计算缩放比例 - 根据宽度比例转换高度 + * @param targetWidth 需要的最终宽度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return float[] { 宽度, 高度 } + */ + public static float[] calcScaleToWidth(final float targetWidth, final float currentWidth, final float currentHeight) { + try { + if (currentWidth == 0f) { + return new float[]{0f, 0f}; + } + // 计算比例 + float scale = targetWidth / currentWidth; + // 计算缩放后的高度 + float scaleHeight = scale * currentHeight; + // 返回对应的数据 + return new float[]{targetWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToWidth"); + } + return null; + } + + /** + * 计算缩放比例 - 根据高度比例转换宽度 + * @param targetHeight 需要的最终高度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return float[] { 宽度, 高度 } + */ + public static float[] calcScaleToHeight(final float targetHeight, final float currentWidth, final float currentHeight) { + try { + if (currentHeight == 0f) { + return new float[]{0f, 0f}; + } + // 计算比例 + float scale = targetHeight / currentHeight; + // 计算缩放后的宽度 + float scaleWidth = scale * currentWidth; + // 返回对应的数据 + return new float[]{scaleWidth, targetHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToHeight"); + } + return null; + } + + /** + * 通过宽度、高度根据对应的比例, 转换成对应的比例宽度高度 - 智能转换 + * @param width 宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return float[] { 宽度, 高度 } + */ + public static float[] calcWidthHeightToScale(final float width, final float height, final float widthScale, final float heightScale) { + try { + // 如果宽度的比例, 大于等于高度比例 + if (widthScale >= heightScale) { // 以宽度为基准 + // 设置宽度, 以宽度为基准 + float scaleWidth = width; + // 计算宽度 + float scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new float[]{scaleWidth, scaleHeight}; + } else { // 以高度为基准 + // 设置高度 + float scaleHeight = height; + // 同步缩放比例 + float scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new float[]{scaleWidth, scaleHeight}; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthHeightToScale"); + } + return null; + } + + /** + * 以宽度为基准, 转换对应比例的高度 + * @param width 宽度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return float[] { 宽度, 高度 } + */ + public static float[] calcWidthToScale(final float width, final float widthScale, final float heightScale) { + try { + // 设置宽度 + float scaleWidth = width; + // 计算高度 + float scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new float[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthToScale"); + } + return null; + } + + /** + * 以高度为基准, 转换对应比例的宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return float[] { 宽度, 高度 } + */ + public static float[] calcHeightToScale(final float height, final float widthScale, final float heightScale) { + try { + // 设置高度 + float scaleHeight = height; + // 计算宽度 + float scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new float[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcHeightToScale"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/StreamUtils.java b/lib/DevApp/src/main/java/dev/utils/common/StreamUtils.java new file mode 100644 index 0000000000..d49601f3c9 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/StreamUtils.java @@ -0,0 +1,221 @@ +package dev.utils.common; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import dev.utils.JCLogUtils; + +/** + * detail: 流操作工具类 + * @author Ttt + */ +public final class StreamUtils { + + private StreamUtils() { + } + + // 日志 TAG + private static final String TAG = StreamUtils.class.getSimpleName(); + + /** + * 输入流转输出流 + * @param inputStream {@link InputStream} + * @return {@link ByteArrayOutputStream} + */ + public static ByteArrayOutputStream inputToOutputStream(final InputStream inputStream) { + if (inputStream == null) return null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer, 0, 1024)) != -1) { + baos.write(buffer, 0, len); + } + return baos; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "inputToOutputStream"); + return null; + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (Exception e) { + } + } + } + } + + /** + * 输出流转输入流 + * @param outputStream {@link OutputStream} + * @return {@link ByteArrayInputStream} + */ + public static ByteArrayInputStream outputToInputStream(final OutputStream outputStream) { + if (outputStream == null) return null; + try { + return new ByteArrayInputStream(((ByteArrayOutputStream) outputStream).toByteArray()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "outputToInputStream"); + return null; + } + } + + /** + * 输入流转 byte[] + * @param inputStream {@link InputStream} + * @return byte[] + */ + public static byte[] inputStreamToBytes(final InputStream inputStream) { + if (inputStream == null) return null; + try { + return inputToOutputStream(inputStream).toByteArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "inputStreamToBytes"); + return null; + } + } + + /** + * byte[] 转输出流 + * @param bytes 数据源 + * @return {@link InputStream} + */ + public static InputStream bytesToInputStream(final byte[] bytes) { + if (bytes == null || bytes.length == 0) return null; + try { + return new ByteArrayInputStream(bytes); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToInputStream"); + return null; + } + } + + /** + * 输出流转 byte[] + * @param outputStream {@link OutputStream} + * @return byte[] + */ + public static byte[] outputStreamToBytes(final OutputStream outputStream) { + if (outputStream == null) return null; + try { + return ((ByteArrayOutputStream) outputStream).toByteArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "outputStreamToBytes"); + return null; + } + } + + /** + * byte[] 转 输出流 + * @param bytes 数据源 + * @return {@link OutputStream} + */ + public static OutputStream bytesToOutputStream(final byte[] bytes) { + if (bytes == null || bytes.length == 0) return null; + ByteArrayOutputStream baos = null; + try { + baos = new ByteArrayOutputStream(); + baos.write(bytes); + return baos; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToOutputStream"); + return null; + } finally { + if (baos != null) { + try { + baos.close(); + } catch (Exception e) { + } + } + } + } + + /** + * 输入流转 String + * @param inputStream {@link InputStream} + * @param charsetName 编码格式 + * @return 指定编码字符串 + */ + public static String inputStreamToString(final InputStream inputStream, final String charsetName) { + if (inputStream == null || isSpace(charsetName)) return null; + try { + return new String(inputStreamToBytes(inputStream), charsetName); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "inputStreamToString"); + return null; + } + } + + /** + * String 转换输入流 + * @param string 数据源 + * @param charsetName 编码格式 + * @return {@link InputStream} + */ + public static InputStream stringToInputStream(final String string, final String charsetName) { + if (string == null || isSpace(charsetName)) return null; + try { + return new ByteArrayInputStream(string.getBytes(charsetName)); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "stringToInputStream"); + return null; + } + } + + /** + * 输出流转 String + * @param outputStream {@link OutputStream} + * @param charsetName 编码格式 + * @return 指定编码字符串 + */ + public static String outputStreamToString(final OutputStream outputStream, final String charsetName) { + if (outputStream == null || isSpace(charsetName)) return null; + try { + return new String(outputStreamToBytes(outputStream), charsetName); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "outputStreamToString"); + return null; + } + } + + /** + * String 转 输出流 + * @param string 数据源 + * @param charsetName 编码格式 + * @return {@link OutputStream} + */ + public static OutputStream stringToOutputStream(final String string, final String charsetName) { + if (string == null || isSpace(charsetName)) return null; + try { + return bytesToOutputStream(string.getBytes(charsetName)); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "stringToOutputStream"); + return null; + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/StringUtils.java b/lib/DevApp/src/main/java/dev/utils/common/StringUtils.java new file mode 100644 index 0000000000..55fbd61b5c --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/StringUtils.java @@ -0,0 +1,1179 @@ +package dev.utils.common; + +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import dev.utils.JCLogUtils; + +/** + * detail: 字符串工具类 + * @author Ttt + */ +public final class StringUtils { + + private StringUtils() { + } + + // 日志 TAG + private static final String TAG = StringUtils.class.getSimpleName(); + // 换行字符串 + public static final String NEW_LINE_STR = System.getProperty("line.separator"); + // 换行字符串 ( 两行 ) + public static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; + + // ========== + // = String = + // ========== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @param isTrim 是否调用 trim() + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String str, final boolean isTrim) { + if (str != null) { + return isEmpty(isTrim ? str.trim() : str); + } + return false; + } + + /** + * 判断多个字符串是否存在为 null 的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + if (isEmpty(strs[i])) { + return true; + } + } + return false; + } + // 默认表示属于 null + return true; + } + + // = + + /** + * 判断字符串是否不为 null + * @param str 待校验的字符串 + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final String str) { + return (str != null && str.length() != 0); + } + + /** + * 判断字符串是否不为 null + * @param str 待校验的字符串 + * @param isTrim 是否调用 trim() + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final String str, final boolean isTrim) { + return isNotEmpty(isTrim ? str.trim() : str); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取字符串长度 + * @param str 待校验的字符串 + * @return 字符串长度, 如果字符串为 null, 则返回 0 + */ + public static int length(final String str) { + return str == null ? 0 : str.length(); + } + + /** + * 获取字符串长度 + * @param str 待校验的字符串 + * @param defaultLength 字符串为 null 时, 返回的长度 + * @return 字符串长度, 如果字符串为 null, 则返回 defaultLength + */ + public static int length(final String str, final int defaultLength) { + return str != null ? str.length() : defaultLength; + } + + // = + + /** + * 获取字符串长度 是否等于期望长度 + * @param str 待校验的字符串 + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final String str, final int length) { + return str != null && str.length() == length; + } + + // ============ + // = 对比判断 = + // ============ + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + /** + * 判断多个字符串是否相等, 只有全相等才返回 true - 对比大小写 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEquals(final String... strs) { + return isEquals(false, strs); + } + + /** + * 判断多个字符串是否相等, 只有全相等才返回 true + * @param isIgnore 是否忽略大小写 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEquals(final boolean isIgnore, final String... strs) { + if (strs != null) { + String last = null; + // 获取数据长度 + int len = strs.length; + // 如果最多只有一个数据判断, 则直接跳过 + if (len <= 1) { + return false; + } + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 如果等于 null, 则跳过 + if (val == null) { + return false; + } + if (last != null) { + if (isIgnore) { + if (!val.equalsIgnoreCase(last)) { + return false; + } + } else { + if (!val.equals(last)) { + return false; + } + } + } + last = val; + } + return true; + } + return false; + } + + /** + * 判断多个字符串, 只要有一个符合条件则通过 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isOrEquals(final String str, final String... strs) { + return isOrEquals(false, str, strs); + } + + /** + * 判断多个字符串, 只要有一个符合条件则通过 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isOrEquals(final boolean isIgnore, final String str, final String... strs) { + if (str != null && strs != null && strs.length != 0) { + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 如果等于 null, 则跳过 + if (val == null) { + continue; + } else { + if (isIgnore) { + if (val.equalsIgnoreCase(str)) { + return true; + } + } else { + if (val.equals(str)) { + return true; + } + } + } + } + } + return false; + } + + /** + * 统计字符串匹配个数 + * @param str 待匹配字符串 + * @param keyword 匹配 key + * @return 字符串 key 匹配个数 + */ + public static int countMatches(final String str, final String keyword) { + if (isEmpty(str) || isEmpty(keyword)) return 0; + try { + int count = 0; + Matcher matcher = Pattern.compile(keyword).matcher(str); + // find() 对字符串进行匹配, 匹配到的字符串可以在任何位置 + while (matcher.find()) { + count++; + } + return count; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "countMatches"); + } + return -1; + } + + /** + * 统计字符串匹配个数 + * @param str 待匹配字符串 + * @param keyword 匹配 key + * @return 字符串 key 匹配个数 + */ + public static int countMatches2(final String str, final String keyword) { + if (isEmpty(str) || isEmpty(keyword)) return 0; + try { + // 获取匹配 key 长度 + int keyLength = keyword.length(); + // = + int count = 0; + int index = 0; + while ((index = str.indexOf(keyword, index)) != -1) { + index = index + keyLength; + count++; + } + return count; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "countMatches2"); + } + return -1; + } + + /** + * 判断一堆值中, 是否存在符合该条件的 ( 包含 ) + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContains(final String str, final String... strs) { + return isContains(false, str, strs); + } + + /** + * 判断一堆值中, 是否存在符合该条件的 ( 包含 ) + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContains(final boolean isIgnore, final String str, final String... strs) { + if (str != null && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取内容长度 + int cLength = strTemp.length(); + // 遍历判断 + for (int i = 0, len = strs.length; i < len; i++) { + // 获取参数 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val) && cLength != 0) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否包含 + if (valIgnore.indexOf(strTemp) != -1) { + return true; + } + } else { + // 判断是否包含 + if (val.indexOf(strTemp) != -1) { + return true; + } + } + } else { + // 下面这一串可以不要, 因为判断字符串是否包含 + // 已经处理了值不为 null, 并且需要判断的值长度不能为 0, 下面则不需要加上 + if (strTemp.equals(val)) { + return true; + } + } + } + } + return false; + } + + /** + * 判断内容, 是否属于特定字符串开头 - 对比大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isStartsWith(final String str, final String... strs) { + return isStartsWith(false, str, strs); + } + + /** + * 判断内容, 是否属于特定字符串开头 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isStartsWith(final boolean isIgnore, final String str, final String... strs) { + if (!isEmpty(str) && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val)) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否属于 val 开头 + if (strTemp.startsWith(valIgnore)) { + return true; + } + } else { + // 判断是否属于 val 开头 + if (strTemp.startsWith(val)) { + return true; + } + } + } + } + } + return false; + } + + /** + * 判断内容, 是否属于特定字符串结尾 - 对比大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEndsWith(final String str, final String... strs) { + return isEndsWith(false, str, strs); + } + + /** + * 判断内容, 是否属于特定字符串结尾 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEndsWith(final boolean isIgnore, final String str, final String... strs) { + if (!isEmpty(str) && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val)) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否属于 val 结尾 + if (strTemp.endsWith(valIgnore)) { + return true; + } + } else { + // 判断是否属于 val 结尾 + if (strTemp.endsWith(val)) { + return true; + } + } + } + } + } + return false; + } + + // ============ + // = 其他处理 = + // ============ + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + * 清空字符串全部空格 + * @param str 待处理字符串 + * @return 处理后的字符串 + */ + public static String toClearSpace(final String str) { + if (isEmpty(str)) return str; + return str.replaceAll(" ", ""); + } + + /** + * 清空字符串前后所有空格 + * @param str 待处理字符串 + * @return 处理后的字符串 + */ + public static String toClearSpaceTrim(final String str) { + if (isEmpty(str)) return str; + String strTemp = str; + // 如果前面或者后面都是空格开头, 就一直进行处理 + while (strTemp.startsWith(" ") || strTemp.endsWith(" ")) { + strTemp = strTemp.trim(); + } + return strTemp; + } + + // = + + /** + * 追加空格 + * @param number 空格数量 + * @return 指定数量的空格字符串 + */ + public static String appendSpace(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append(" "); + } + } + return builder.toString(); + } + + /** + * 追加 Tab + * @param number tab 键数量 + * @return 指定数量的 Tab 字符串 + */ + public static String appendTab(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append("\t"); + } + } + return builder.toString(); + } + + /** + * 追加换行 + * @param number 换行数量 + * @return 指定数量的换行字符串 + */ + public static String appendLine(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append(NEW_LINE_STR); + } + } + return builder.toString(); + } + + /** + * 冒号分割处理 + * @param str 待处理字符串 + * @return 冒号分割后的字符串 + */ + public static String colonSplit(final String str) { + if (!isEmpty(str)) { + return str.replaceAll("(?<=[0-9A-F]{2})[0-9A-F]{2}", ":$0"); + } + return str; + } + + // = + + /** + * 检查字符串 + * @param str 待校验字符串 + * @return 如果待校验字符串为 null, 则返回默认字符串, 如果不为 null, 则返回该字符串 + */ + public static String toCheckValue(final String str) { + return toCheckValue("", str); + } + + /** + * 检查字符串 + * @param defaultStr 默认字符串 + * @param str 待校验字符串 + * @return 如果待校验字符串为 null, 则返回 defaultStr, 如果不为 null, 则返回该字符串 + */ + public static String toCheckValue(final String defaultStr, final String str) { + return isEmpty(str) ? defaultStr : str; + } + + /** + * 检查字符串 - 单独检查两个值 + * @param defaultStr 默认字符串 + * @param value1 第一个待校验字符串 + * @param value2 第二个待校验字符串 + * @return 两个待校验字符串中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValue(final String defaultStr, final String value1, final String value2) { + if (isEmpty(value1)) { + if (isEmpty(value2)) { + return defaultStr; + } else { + return value2; + } + } else { + return value1; + } + } + + /** + * 检查字符串 - 多个值 + * @param defaultStr 默认字符串 + * @param strs 待校验字符串数组 + * @return 字符串数组中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValues(final String defaultStr, final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + String val = strs[i]; + if (isEmpty(val)) { + if (i == len - 1) { + return defaultStr; // 属于最后一个, 则返回默认值 + } else { + continue; // 不属于最后一个则跳过 + } + } else { + return val; + } + } + } + return defaultStr; + } + + /** + * 检查字符串 - 多个值 ( 删除前后空格对比判断 ) + * @param defaultStr 默认字符串 + * @param strs 待校验字符串数组 + * @return 字符串数组中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValuesSpace(final String defaultStr, final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + // 删除前后空格处理后, 进行返回 + String val = toClearSpaceTrim(strs[i]); + if (isEmpty(val)) { + if (i == len - 1) { + return defaultStr; // 属于最后一个, 则返回默认值 + } else { + continue; // 不属于最后一个则跳过 + } + } else { + return val; + } + } + } + return defaultStr; + } + + // ================== + // = 数据格式化处理 = + // ================== + + /** + * 获取格式化后的字符串 + * @param format 待格式化字符串 + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getFormatString(final String format, final Object... args) { + if (format == null) return null; + try { + if (args != null && args.length != 0) { + return String.format(format, args); + } else { + return format; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFormatString"); + } + return null; + } + + // = + + /** + * 获取自动数量格式化后的字符串 ( 可变参数 ) + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getAutoFormatString(final Object... args) { + if (args != null && args.length != 0) { + try { + int length = args.length; + StringBuilder builder = new StringBuilder(); + builder.append("%s"); + if (length > 1) { + for (int i = 1; i < length; i++) { + builder.append(" %s"); + } + } + return String.format(builder.toString(), args); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAutoFormatString"); + } + } + return null; + } + + /** + * 获取自动数量格式化后的字符串 ( 可变参数 ) + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getAutoFormatString2(final Object... args) { + if (args != null && args.length != 0) { + try { + int length = args.length; + StringBuilder builder = new StringBuilder(); + builder.append("【%s】"); + if (length > 1) { + for (int i = 1; i < length; i++) { + builder.append(" %s"); + } + } + return String.format(builder.toString(), args); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAutoFormatString2"); + } + } + return null; + } + + // = + + /** + * StringBuilder 拼接处理 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final Object... args) { + return appends(new StringBuilder(), null, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param split 追加间隔 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final String split, final Object... args) { + return appends(new StringBuilder(), split, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param builder 拼接 Builder + * @param split 追加间隔 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final StringBuilder builder, final String split, final Object... args) { + return appends(builder, split, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param builder 拼接 Builder + * @param split 追加间隔 + * @param end 结尾是否追加 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final StringBuilder builder, final String split, final boolean end, final Object... args) { + if (builder != null && args != null) { + // 获取间隔字符串, 优化循环判断 + String str = isEmpty(split) ? "" : split; + // 循环处理 + for (int i = 0, len = args.length; i < len; i++) { + builder.append(args[i]); // 拼接数据 + // 判断是否结尾 + if (len - i == 1) { + // 判断结尾是否追加 + if (end) builder.append(str); // 间隔追加 + } else { + builder.append(str); // 间隔追加 + } + } + return builder; + } + return builder; + } + + // ============ + // = 转换处理 = + // ============ + + /** + * 字符串进行 GBK 编码 + * @param str 待处理字符串 + * @return GBK 编码后的字符串 + */ + public static String toGBKEncode(final String str) { + return toStrEncode(str, "GBK"); + } + + /** + * 字符串进行 GBK2312 编码 + * @param str 待处理字符串 + * @return GBK2312 编码后的字符串 + */ + public static String toGBK2312Encode(final String str) { + return toStrEncode(str, "GBK-2312"); + } + + /** + * 字符串进行 UTF-8 编码 + * @param str 待处理字符串 + * @return UTF-8 编码后的字符串 + */ + public static String toUTF8Encode(final String str) { + return toStrEncode(str, "UTF-8"); + } + + /** + * 进行字符串编码 + * @param str 待处理字符串 + * @param enc 编码格式 + * @return 指定编码格式编码后的字符串 + */ + public static String toStrEncode(final String str, final String enc) { + if (str == null || enc == null) return null; + try { + return new String(str.getBytes(), enc); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toStrEncode"); + } + return str; + } + + // = + + /** + * 进行 URL 编码, 默认 UTF-8 + * @param str 待处理字符串 + * @return UTF-8 编码格式 URL 编码后的字符串 + */ + public static String toUrlEncode(final String str) { + return toUrlEncode(str, "UTF-8"); + } + + /** + * 进行 URL 编码 + * @param str 待处理字符串 + * @param enc 编码格式 + * @return 指定编码格式 URL 编码后的字符串 + */ + public static String toUrlEncode(final String str, final String enc) { + if (str == null || enc == null) return null; + try { + return URLEncoder.encode(str, enc); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toUrlEncode"); + } + return null; + } + + // = + + /** + * 进行 URL 解码, 默认 UTF-8 + * @param str 待处理字符串 + * @return UTF-8 编码格式 URL 解码后的字符串 + */ + public static String toUrlDecode(final String str) { + return toUrlDecode(str, "UTF-8"); + } + + /** + * 进行 URL 解码 + * @param str 待处理字符串 + * @param enc 解码格式 + * @return 指定编码格式 URL 解码后的字符串 + */ + public static String toUrlDecode(final String str, final String enc) { + if (str == null || enc == null) return null; + try { + return URLDecoder.decode(str, enc); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toUrlDecode"); + } + return null; + } + + // = + + /** + * 将字符串转移为 ASCII 码 + * @param str 待处理字符串 + * @return 字符串转 ASCII 码后的字符串 + */ + public static String toASCII(final String str) { + if (isEmpty(str)) return str; + try { + StringBuilder builder = new StringBuilder(); + byte[] bytes = str.getBytes(); + for (int i = 0, len = bytes.length; i < len; i++) { + builder.append(Integer.toHexString(bytes[i] & 0xff)); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toASCII"); + } + return null; + } + + /** + * 将字符串转移为 Unicode 码 + * @param str 待处理字符串 + * @return 字符串转 Unicode 码后的字符串 + */ + public static String toUnicode(final String str) { + if (isEmpty(str)) return str; + try { + StringBuilder builder = new StringBuilder(); + char[] chars = str.toCharArray(); + for (int i = 0, len = chars.length; i < len; i++) { + builder.append("\\u").append(Integer.toHexString(chars[i])); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toUnicode"); + } + return null; + } + + /** + * 将字符数组转移为 Unicode 码 + * @param chars char[] + * @return char[] 转 Unicode 码后的字符串 + */ + public static String toUnicodeString(final char[] chars) { + if (chars == null) return null; + try { + StringBuilder builder = new StringBuilder(); + for (int i = 0, len = chars.length; i < len; i++) { + builder.append("\\u").append(Integer.toHexString(chars[i])); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toUnicodeString"); + } + return null; + } + + /** + * 转化为半角字符 + * @param str 待处理字符串 + * @return 转换半角字符串 + */ + public static String toDBC(final String str) { + if (isEmpty(str)) return str; + char[] chars = str.toCharArray(); + for (int i = 0, len = chars.length; i < len; i++) { + if (chars[i] == 12288) { + chars[i] = ' '; + } else if (65281 <= chars[i] && chars[i] <= 65374) { + chars[i] = (char) (chars[i] - 65248); + } else { + chars[i] = chars[i]; + } + } + return new String(chars); + } + + /** + * 转化为全角字符 如: a = a, A = A + * @param str 待处理字符串 + * @return 转换全角字符串 + */ + public static String toSBC(final String str) { + if (isEmpty(str)) return str; + char[] chars = str.toCharArray(); + for (int i = 0, len = chars.length; i < len; i++) { + if (chars[i] == ' ') { + chars[i] = (char) 12288; + } else if (33 <= chars[i] && chars[i] <= 126) { + chars[i] = (char) (chars[i] + 65248); + } else { + chars[i] = chars[i]; + } + } + return new String(chars); + } + + // = + + /** + * 检测字符串是否全是中文 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean checkChineseToString(final String str) { + if (isEmpty(str)) return false; + boolean result = true; + char[] chars = str.toCharArray(); + for (int i = 0, len = chars.length; i < len; i++) { + if (!isChinese(chars[i])) { + result = false; + break; + } + } + return result; + } + + /** + * 判定输入汉字 + * @param ch 待校验字符 + * @return {@code true} yes, {@code false} no + */ + public static boolean isChinese(final char ch) { + Character.UnicodeBlock ub = Character.UnicodeBlock.of(ch); + return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS + || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS + || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A + || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION + || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION + || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS; + } + + // ================== + // = 字符串处理方法 = + // ================== + + /** + * 首字母大写 + * @param str 待处理字符串 + * @return 首字母大写字符串 + */ + public static String upperFirstLetter(final String str) { + if (isEmpty(str) || !Character.isLowerCase(str.charAt(0))) return str; + try { + return (char) (str.charAt(0) - 32) + str.substring(1); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "upperFirstLetter"); + return str; + } + } + + /** + * 首字母小写 + * @param str 待处理字符串 + * @return 首字母小写字符串 + */ + public static String lowerFirstLetter(final String str) { + if (isEmpty(str) || !Character.isUpperCase(str.charAt(0))) return str; + try { + return (char) (str.charAt(0) + 32) + str.substring(1); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "lowerFirstLetter"); + return str; + } + } + + /** + * 反转字符串 + * @param str 待处理字符串 + * @return 反转字符串 + */ + public static String reverse(final String str) { + int len = length(str); + if (len <= 1) return str; + int mid = len >> 1; + char[] chars = str.toCharArray(); + char ch; + for (int i = 0; i < mid; ++i) { + ch = chars[i]; + chars[i] = chars[len - i - 1]; + chars[len - i - 1] = ch; + } + return new String(chars); + } + + /** + * 字符串连接, 将参数列表拼接为一个字符串 + * @param args 追加数据 + * @return 拼接后的字符串 + */ + public static String concat(final Object... args) { + return concatSpiltWith("", args); + } + + /** + * 字符串连接, 将参数列表拼接为一个字符串 + * @param startStr 开始字符串 + * @param args 追加数据 + * @return 拼接后的字符串 + */ + public static String concatSpiltWith(final String startStr, final Object... args) { + if (args == null) return null; + StringBuilder builder = new StringBuilder(); + for (int i = 0, len = args.length; i < len; i++) { + if (i != 0) builder.append(startStr); + builder.append(args[i]); + } + return builder.toString(); + } + + /** + * 下划线命名转为驼峰命名 + * @param str 下划线命名格式字符串 + * @return 驼峰命名格式字符串 + */ + public static String underScoreCaseToCamelCase(final String str) { + if (isEmpty(str)) return str; + if (!str.contains("_")) return str; + StringBuilder builder = new StringBuilder(); + char[] chars = str.toCharArray(); + boolean hitUnderScore = false; + builder.append(chars[0]); + for (int i = 1, len = chars.length; i < len; i++) { + char c = chars[i]; + if (c == '_') { + hitUnderScore = true; + } else { + if (hitUnderScore) { + builder.append(Character.toUpperCase(c)); + hitUnderScore = false; + } else { + builder.append(c); + } + } + } + return builder.toString(); + } + + /** + * 驼峰命名法转为下划线命名 + * @param str 驼峰命名格式字符串 + * @return 下划线命名格式字符串 + */ + public static String camelCaseToUnderScoreCase(final String str) { + if (isEmpty(str)) return str; + StringBuilder builder = new StringBuilder(); + char[] chars = str.toCharArray(); + for (int i = 0, len = chars.length; i < len; i++) { + char c = chars[i]; + if (Character.isUpperCase(c)) { + builder.append("_").append(Character.toLowerCase(c)); + } else { + builder.append(c); + } + } + return builder.toString(); + } + + /** + * 字符串数据库字符转义 + * @param str 待处理字符串 + * @return 转义处理后的字符串 + */ + public static String sqliteEscape(final String str) { + if (isEmpty(str)) return str; + String keyWord = str; + // 替换关键字 + keyWord = keyWord.replace("/", "//"); + keyWord = keyWord.replace("'", "''"); + keyWord = keyWord.replace("[", "/["); + keyWord = keyWord.replace("]", "/]"); + keyWord = keyWord.replace("%", "/%"); + keyWord = keyWord.replace("&", "/&"); + keyWord = keyWord.replace("_", "/_"); + keyWord = keyWord.replace("(", "/("); + keyWord = keyWord.replace(")", "/)"); + return keyWord; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/ThrowableUtils.java b/lib/DevApp/src/main/java/dev/utils/common/ThrowableUtils.java new file mode 100644 index 0000000000..827b3f9e9f --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/ThrowableUtils.java @@ -0,0 +1,99 @@ +package dev.utils.common; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; + +import dev.utils.JCLogUtils; + +/** + * detail: 异常处理工具类 + * @author Ttt + */ +public final class ThrowableUtils { + + private ThrowableUtils() { + } + + // 日志 TAG + private static final String TAG = ThrowableUtils.class.getSimpleName(); + + /** + * 获取异常信息 + * @param throwable 异常 + * @return 异常信息字符串 + */ + public static String getThrowable(final Throwable throwable) { + return getThrowable(throwable, "error(null)"); + } + + /** + * 获取异常信息 + * @param throwable 异常 + * @param errorInfo 获取失败返回字符串 + * @return 异常信息字符串 + */ + public static String getThrowable(final Throwable throwable, final String errorInfo) { + if (throwable != null) { + Throwable cause = throwable.getCause(); + if (cause != null) { + return cause.toString(); + } + return throwable.toString(); + } + return errorInfo; + } + + // ============== + // = 异常栈信息 = + // ============== + + /** + * 获取异常栈信息 + * @param throwable 异常 + * @return 异常栈信息字符串 + */ + public static String getThrowableStackTrace(final Throwable throwable) { + return getThrowableStackTrace(throwable, "error(null)"); + } + + /** + * 获取异常栈信息 + * @param throwable 异常 + * @param errorInfo 获取失败返回字符串 + * @return 异常栈信息字符串 + */ + public static String getThrowableStackTrace(final Throwable throwable, final String errorInfo) { + if (throwable != null) { + PrintWriter printWriter = null; + try { + Writer writer = new StringWriter(); + printWriter = new PrintWriter(writer); + throwable.printStackTrace(printWriter); +// // 获取错误栈信息 +// StackTraceElement[] stElement = throwable.getStackTrace(); +// // 标题, 提示属于什么异常 +// printWriter.append(throwable.toString()); +// printWriter.append(NEW_LINE_STR); +// // 遍历错误栈信息, 并且进行换行缩进 +// for (StackTraceElement element : stElement) { +// printWriter.append("\tat "); +// printWriter.append(element.toString()); +// printWriter.append(NEW_LINE_STR); +// } + return writer.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getThrowableStackTrace"); + return e.toString(); + } finally { + if (printWriter != null) { + try { + printWriter.close(); + } catch (Exception e) { + } + } + } + } + return errorInfo; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/ZipUtils.java b/lib/DevApp/src/main/java/dev/utils/common/ZipUtils.java similarity index 58% rename from DevLibUtils/src/main/java/dev/utils/common/ZipUtils.java rename to lib/DevApp/src/main/java/dev/utils/common/ZipUtils.java index d8e8065f6b..24b774768f 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/ZipUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/common/ZipUtils.java @@ -2,10 +2,10 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; @@ -16,38 +16,42 @@ import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; +import dev.utils.JCLogUtils; + /** * detail: 压缩相关工具类 - * Created by Ttt + * @author Ttt */ public final class ZipUtils { private ZipUtils() { } + // 日志 TAG + private static final String TAG = ZipUtils.class.getSimpleName(); // 缓存大小 private static final int BUFFER_LEN = 8192; /** * 批量压缩文件 - * @param resFiles 待压缩文件路径集合 + * @param resFiles 待压缩文件路径集合 * @param zipFilePath 压缩文件路径 - * @return true : 压缩成功, false : 压缩失败 - * @throws IOException IO错误时抛出 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 */ - public static boolean zipFiles(final Collection resFiles, final String zipFilePath) throws IOException { + public static boolean zipFiles(final Collection resFiles, final String zipFilePath) throws Exception { return zipFiles(resFiles, zipFilePath, null); } /** * 批量压缩文件 * @param resFilePaths 待压缩文件路径集合 - * @param zipFilePath 压缩文件路径 - * @param comment 压缩文件的注释 - * @return true : 压缩成功, false : 压缩失败 - * @throws IOException IO错误时抛出 + * @param zipFilePath 压缩文件路径 + * @param comment 压缩文件的注释 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 */ - public static boolean zipFiles(final Collection resFilePaths, final String zipFilePath, final String comment) throws IOException { + public static boolean zipFiles(final Collection resFilePaths, final String zipFilePath, final String comment) throws Exception { if (resFilePaths == null || zipFilePath == null) return false; ZipOutputStream zos = null; try { @@ -59,7 +63,7 @@ public static boolean zipFiles(final Collection resFilePaths, final Stri } finally { if (zos != null) { zos.finish(); - CloseUtils.closeIO(zos); + closeIOQuietly(zos); } } } @@ -67,23 +71,23 @@ public static boolean zipFiles(final Collection resFilePaths, final Stri /** * 批量压缩文件 * @param resFiles 待压缩文件集合 - * @param zipFile 压缩文件 - * @return true : 压缩成功, false : 压缩失败 - * @throws IOException IO错误时抛出 + * @param zipFile 压缩文件 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 */ - public static boolean zipFiles(final Collection resFiles, final File zipFile) throws IOException { + public static boolean zipFiles(final Collection resFiles, final File zipFile) throws Exception { return zipFiles(resFiles, zipFile, null); } /** * 批量压缩文件 * @param resFiles 待压缩文件集合 - * @param zipFile 压缩文件 - * @param comment 压缩文件的注释 - * @return true : 压缩成功, false : 压缩失败 - * @throws IOException IO错误时抛出 + * @param zipFile 压缩文件 + * @param comment 压缩文件的注释 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 */ - public static boolean zipFiles(final Collection resFiles, final File zipFile, final String comment) throws IOException { + public static boolean zipFiles(final Collection resFiles, final File zipFile, final String comment) throws Exception { if (resFiles == null || zipFile == null) return false; ZipOutputStream zos = null; try { @@ -95,7 +99,7 @@ public static boolean zipFiles(final Collection resFiles, final File zipFi } finally { if (zos != null) { zos.finish(); - CloseUtils.closeIO(zos); + closeIOQuietly(zos); } } } @@ -104,10 +108,10 @@ public static boolean zipFiles(final Collection resFiles, final File zipFi * 压缩文件 * @param resFilePath 待压缩文件路径 * @param zipFilePath 压缩文件路径 - * @return true : 压缩成功, false : 压缩失败 - * @throws IOException IO 错误时抛出 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 */ - public static boolean zipFile(final String resFilePath, final String zipFilePath) throws IOException { + public static boolean zipFile(final String resFilePath, final String zipFilePath) throws Exception { return zipFile(getFileByPath(resFilePath), getFileByPath(zipFilePath), null); } @@ -115,11 +119,11 @@ public static boolean zipFile(final String resFilePath, final String zipFilePath * 压缩文件 * @param resFilePath 待压缩文件路径 * @param zipFilePath 压缩文件路径 - * @param comment 压缩文件的注释 - * @return true : 压缩成功, false : 压缩失败 - * @throws IOException IO 错误时抛出 + * @param comment 压缩文件的注释 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 */ - public static boolean zipFile(final String resFilePath, final String zipFilePath, final String comment) throws IOException { + public static boolean zipFile(final String resFilePath, final String zipFilePath, final String comment) throws Exception { return zipFile(getFileByPath(resFilePath), getFileByPath(zipFilePath), comment); } @@ -127,10 +131,10 @@ public static boolean zipFile(final String resFilePath, final String zipFilePath * 压缩文件 * @param resFile 待压缩文件 * @param zipFile 压缩文件 - * @return true : 压缩成功, false : 压缩失败 - * @throws IOException IO 错误时抛出 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 */ - public static boolean zipFile(final File resFile, final File zipFile) throws IOException { + public static boolean zipFile(final File resFile, final File zipFile) throws Exception { return zipFile(resFile, zipFile, null); } @@ -139,10 +143,10 @@ public static boolean zipFile(final File resFile, final File zipFile) throws IOE * @param resFile 待压缩文件 * @param zipFile 压缩文件 * @param comment 压缩文件的注释 - * @return true : 压缩成功, false : 压缩失败 - * @throws IOException IO 错误时抛出 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 */ - public static boolean zipFile(final File resFile, final File zipFile, final String comment) throws IOException { + public static boolean zipFile(final File resFile, final File zipFile, final String comment) throws Exception { if (resFile == null || zipFile == null) return false; ZipOutputStream zos = null; try { @@ -150,51 +154,52 @@ public static boolean zipFile(final File resFile, final File zipFile, final Stri return zipFile(resFile, "", zos, comment); } finally { if (zos != null) { - CloseUtils.closeIO(zos); + closeIOQuietly(zos); } } } /** * 压缩文件 - * @param resFile 待压缩文件 + * @param resFile 待压缩文件 * @param rootPath 相对于压缩文件的路径 - * @param zos 压缩文件输出流 - * @param comment 压缩文件的注释 - * @return true : 压缩成功, false : 压缩失败 - * @throws IOException IO 错误时抛出 + * @param zos 压缩文件输出流 + * @param comment 压缩文件的注释 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 */ - private static boolean zipFile(final File resFile, String rootPath, final ZipOutputStream zos, final String comment) throws IOException { - rootPath = rootPath + (isSpace(rootPath) ? "" : File.separator) + resFile.getName(); + private static boolean zipFile(final File resFile, final String rootPath, final ZipOutputStream zos, final String comment) throws Exception { + // 处理后的文件路径 + String filePath = rootPath + (isSpace(rootPath) ? "" : File.separator) + resFile.getName(); if (resFile.isDirectory()) { File[] fileList = resFile.listFiles(); - // 如果是空文件夹那么创建它,我把'/'换为File.separator测试就不成功,eggPain - if (fileList == null || fileList.length <= 0) { - ZipEntry entry = new ZipEntry(rootPath + '/'); + // 如果是空文件夹那么创建它 + if (fileList == null || fileList.length == 0) { + ZipEntry entry = new ZipEntry(filePath + '/'); entry.setComment(comment); zos.putNextEntry(entry); zos.closeEntry(); } else { for (File file : fileList) { // 如果递归返回 false 则返回 false - if (!zipFile(file, rootPath, zos, comment)) return false; + if (!zipFile(file, filePath, zos, comment)) return false; } } } else { InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(resFile)); - ZipEntry entry = new ZipEntry(rootPath); + ZipEntry entry = new ZipEntry(filePath); entry.setComment(comment); zos.putNextEntry(entry); - byte buffer[] = new byte[BUFFER_LEN]; + byte[] buffer = new byte[BUFFER_LEN]; int len; while ((len = is.read(buffer, 0, BUFFER_LEN)) != -1) { zos.write(buffer, 0, len); } zos.closeEntry(); } finally { - CloseUtils.closeIO(is); + closeIOQuietly(is); } } return true; @@ -205,9 +210,9 @@ private static boolean zipFile(final File resFile, String rootPath, final ZipOut * @param zipFilePath 待解压文件路径 * @param destDirPath 目标目录路径 * @return 文件链表 - * @throws IOException IO 错误时抛出 + * @throws Exception 异常时抛出 */ - public static List unzipFile(final String zipFilePath, final String destDirPath) throws IOException { + public static List unzipFile(final String zipFilePath, final String destDirPath) throws Exception { return unzipFileByKeyword(zipFilePath, destDirPath, null); } @@ -216,9 +221,9 @@ public static List unzipFile(final String zipFilePath, final String destDi * @param zipFile 待解压文件 * @param destDir 目标目录 * @return 文件链表 - * @throws IOException IO 错误时抛出 + * @throws Exception 异常时抛出 */ - public static List unzipFile(final File zipFile, final File destDir) throws IOException { + public static List unzipFile(final File zipFile, final File destDir) throws Exception { return unzipFileByKeyword(zipFile, destDir, null); } @@ -226,11 +231,11 @@ public static List unzipFile(final File zipFile, final File destDir) throw * 解压带有关键字的文件 * @param zipFilePath 待解压文件路径 * @param destDirPath 目标目录路径 - * @param keyword 关键字 - * @return 返回带有关键字的文件链表 - * @throws IOException IO 错误时抛出 + * @param keyword 关键字 + * @return 带有关键字的文件链表 + * @throws Exception 异常时抛出 */ - public static List unzipFileByKeyword(final String zipFilePath, final String destDirPath, final String keyword) throws IOException { + public static List unzipFileByKeyword(final String zipFilePath, final String destDirPath, final String keyword) throws Exception { return unzipFileByKeyword(getFileByPath(zipFilePath), getFileByPath(destDirPath), keyword); } @@ -239,26 +244,34 @@ public static List unzipFileByKeyword(final String zipFilePath, final Stri * @param zipFile 待解压文件 * @param destDir 目标目录 * @param keyword 关键字 - * @return 返回带有关键字的文件链表 - * @throws IOException IO 错误时抛出 + * @return 带有关键字的文件链表 + * @throws Exception 异常时抛出 */ - public static List unzipFileByKeyword(final File zipFile, final File destDir, final String keyword) throws IOException { + public static List unzipFileByKeyword(final File zipFile, final File destDir, final String keyword) throws Exception { if (zipFile == null || destDir == null) return null; List files = new ArrayList<>(); - ZipFile zf = new ZipFile(zipFile); - Enumeration entries = zf.entries(); + ZipFile zip = new ZipFile(zipFile); + Enumeration entries = zip.entries(); if (isSpace(keyword)) { while (entries.hasMoreElements()) { ZipEntry entry = ((ZipEntry) entries.nextElement()); String entryName = entry.getName(); - if (!unzipChildFile(destDir, files, zf, entry, entryName)) return files; + if (entryName.contains("../")) { + JCLogUtils.dTag(TAG, "entryName: %s is dangerous!", entryName); + continue; + } + if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files; } } else { while (entries.hasMoreElements()) { ZipEntry entry = ((ZipEntry) entries.nextElement()); String entryName = entry.getName(); + if (entryName.contains("../")) { + JCLogUtils.dTag(TAG, "entryName: %s is dangerous!", entryName); + continue; + } if (entryName.contains(keyword)) { - if (!unzipChildFile(destDir, files, zf, entry, entryName)) return files; + if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files; } } } @@ -267,34 +280,34 @@ public static List unzipFileByKeyword(final File zipFile, final File destD /** * 解压文件 - * @param destDir - * @param files - * @param zf - * @param entry - * @param entryName - * @return - * @throws IOException + * @param destDir 目标目录 + * @param files 解压文件链表 + * @param zf 压缩文件条目 + * @param entry 压缩文件信息 + * @param entryName 文件名 + * @return {@code true} success, {@code false} fail + * @throws Exception 异常时抛出 */ - private static boolean unzipChildFile(final File destDir, final List files, final ZipFile zf, final ZipEntry entry, final String entryName) throws IOException { - String filePath = destDir + File.separator + entryName; - File file = new File(filePath); + private static boolean unzipChildFile(final File destDir, final List files, final ZipFile zf, + final ZipEntry entry, final String entryName) throws Exception { + File file = new File(destDir, entryName); files.add(file); if (entry.isDirectory()) { - if (!createOrExistsDir(file)) return false; + return createOrExistsDir(file); } else { if (!createOrExistsFile(file)) return false; - InputStream in = null; - OutputStream out = null; + InputStream is = null; + OutputStream os = null; try { - in = new BufferedInputStream(zf.getInputStream(entry)); - out = new BufferedOutputStream(new FileOutputStream(file)); - byte buffer[] = new byte[BUFFER_LEN]; + is = new BufferedInputStream(zf.getInputStream(entry)); + os = new BufferedOutputStream(new FileOutputStream(file)); + byte[] buffer = new byte[BUFFER_LEN]; int len; - while ((len = in.read(buffer)) != -1) { - out.write(buffer, 0, len); + while ((len = is.read(buffer)) != -1) { + os.write(buffer, 0, len); } } finally { - CloseUtils.closeIO(in, out); + closeIOQuietly(is, os); } } return true; @@ -304,9 +317,9 @@ private static boolean unzipChildFile(final File destDir, final List files * 获取压缩文件中的文件路径链表 * @param zipFilePath 压缩文件路径 * @return 压缩文件中的文件路径链表 - * @throws IOException IO 错误时抛出 + * @throws Exception 异常时抛出 */ - public static List getFilesPath(final String zipFilePath) throws IOException { + public static List getFilesPath(final String zipFilePath) throws Exception { return getFilesPath(getFileByPath(zipFilePath)); } @@ -314,14 +327,20 @@ public static List getFilesPath(final String zipFilePath) throws IOExcep * 获取压缩文件中的文件路径链表 * @param zipFile 压缩文件 * @return 压缩文件中的文件路径链表 - * @throws IOException IO 错误时抛出 + * @throws Exception 异常时抛出 */ - public static List getFilesPath(final File zipFile) throws IOException { + public static List getFilesPath(final File zipFile) throws Exception { if (zipFile == null) return null; List paths = new ArrayList<>(); Enumeration entries = new ZipFile(zipFile).entries(); while (entries.hasMoreElements()) { - paths.add(((ZipEntry) entries.nextElement()).getName()); + String entryName = ((ZipEntry) entries.nextElement()).getName(); + if (entryName.contains("../")) { + JCLogUtils.dTag(TAG, "entryName: %s is dangerous!", entryName); + paths.add(entryName); + } else { + paths.add(entryName); + } } return paths; } @@ -330,9 +349,9 @@ public static List getFilesPath(final File zipFile) throws IOException { * 获取压缩文件中的注释链表 * @param zipFilePath 压缩文件路径 * @return 压缩文件中的注释链表 - * @throws IOException IO 错误时抛出 + * @throws Exception 异常时抛出 */ - public static List getComments(final String zipFilePath) throws IOException { + public static List getComments(final String zipFilePath) throws Exception { return getComments(getFileByPath(zipFilePath)); } @@ -340,9 +359,9 @@ public static List getComments(final String zipFilePath) throws IOExcept * 获取压缩文件中的注释链表 * @param zipFile 压缩文件 * @return 压缩文件中的注释链表 - * @throws IOException IO 错误时抛出 + * @throws Exception 异常时抛出 */ - public static List getComments(final File zipFile) throws IOException { + public static List getComments(final File zipFile) throws Exception { if (zipFile == null) return null; List comments = new ArrayList<>(); Enumeration entries = new ZipFile(zipFile).entries(); @@ -353,51 +372,81 @@ public static List getComments(final File zipFile) throws IOException { return comments; } - // = + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============== + // = CloseUtils = + // ============== /** - * 判断目录是否存在,不存在则判断是否创建成功 + * 安静关闭 IO + * @param closeables Closeable[] + */ + private static void closeIOQuietly(final Closeable... closeables) { + if (closeables == null) return; + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception ignore) { + } + } + } + } + + // ============= + // = FileUtils = + // ============= + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 判断目录是否存在, 不存在则判断是否创建成功 * @param file 文件 - * @return true : 存在或创建成功, false : 不存在或创建失败 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 */ private static boolean createOrExistsDir(final File file) { - // 如果存在,是目录则返回 true,是文件则返回 false,不存在则返回是否创建成功 + // 如果存在, 是目录则返回 true, 是文件则返回 false, 不存在则返回是否创建成功 return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); } /** - * 判断文件是否存在,不存在则判断是否创建成功 + * 判断文件是否存在, 不存在则判断是否创建成功 * @param file 文件 - * @return true : 存在或创建成功, false : 不存在或创建失败 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 */ private static boolean createOrExistsFile(final File file) { if (file == null) return false; - // 如果存在,是文件则返回 true,是目录则返回 false + // 如果存在, 是文件则返回 true, 是目录则返回 false if (file.exists()) return file.isFile(); // 判断文件是否存在, 不存在则直接返回 if (!createOrExistsDir(file.getParentFile())) return false; try { // 存在, 则返回新的路径 return file.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "createOrExistsFile"); return false; } } - /** - * 获取文件 - * @param filePath The path of file. - * @return - */ - private static File getFileByPath(final String filePath){ - return filePath != null ? new File(filePath) : null; - } + // =============== + // = StringUtils = + // =============== /** * 判断字符串是否为 null 或全为空白字符 * @param str 待校验字符串 - * @return + * @return {@code true} yes, {@code false} no */ private static boolean isSpace(final String str) { if (str == null) return true; @@ -408,4 +457,4 @@ private static boolean isSpace(final String str) { } return true; } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/assist/Averager.java b/lib/DevApp/src/main/java/dev/utils/common/assist/Averager.java new file mode 100644 index 0000000000..afe5fa47dd --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/assist/Averager.java @@ -0,0 +1,60 @@ +package dev.utils.common.assist; + +import java.util.ArrayList; +import java.util.List; + +/** + * detail: 均值计算 ( 用以统计平均数 ) 辅助类 + * @author Ttt + */ +public class Averager { + + private List mNumLists = new ArrayList<>(); + + /** + * 添加一个数字 + * @param number Number + */ + public synchronized void add(final Number number) { + mNumLists.add(number); + } + + /** + * 清除全部 + */ + public void clear() { + mNumLists.clear(); + } + + /** + * 获取参与均值计算的数字个数 + * @return 参与均值计算的数字个数 + */ + public Number size() { + return mNumLists.size(); + } + + /** + * 获取平均数 + * @return 全部数字平均数 + */ + public Number getAverage() { + if (mNumLists.size() == 0) { + return 0; + } else { + Float sum = 0f; + for (int i = 0, len = mNumLists.size(); i < len; i++) { + sum = sum.floatValue() + mNumLists.get(i).floatValue(); + } + return sum / mNumLists.size(); + } + } + + /** + * 输出参与均值计算的数字 + * @return 参与均值计算的数字 + */ + public String print() { + return "printList(" + size() + "): " + mNumLists; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/assist/TimeAverager.java b/lib/DevApp/src/main/java/dev/utils/common/assist/TimeAverager.java new file mode 100644 index 0000000000..dcbf78fa41 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/assist/TimeAverager.java @@ -0,0 +1,64 @@ +package dev.utils.common.assist; + +/** + * detail: 时间均值计算辅助类 + * @author Ttt + */ +public class TimeAverager { + + // 计时器 + private TimeCounter mTimeCounter = new TimeCounter(); + // 均值器 + private Averager mAverager = new Averager(); + + /** + * 开始计时 ( 毫秒 ) + * @return 开始时间 ( 毫秒 ) + */ + public long start() { + return mTimeCounter.start(); + } + + /** + * 结束计时 ( 毫秒 ) + * @return 结束时间 ( 毫秒 ) + */ + public long end() { + long time = mTimeCounter.duration(); + mAverager.add(time); + return time; + } + + /** + * 结束计时, 并重新启动新的计时 + * @return 距离上次计时的时间差 ( 毫秒 ) + */ + public long endAndRestart() { + long time = mTimeCounter.durationRestart(); + mAverager.add(time); + return time; + } + + /** + * 求全部计时均值 + * @return 全部计时时间均值 + */ + public Number average() { + return mAverager.getAverage(); + } + + /** + * 输出全部时间值 + * @return 计时信息 + */ + public String print() { + return mAverager.print(); + } + + /** + * 清除计时数据 + */ + public void clear() { + mAverager.clear(); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/assist/TimeCounter.java b/lib/DevApp/src/main/java/dev/utils/common/assist/TimeCounter.java new file mode 100644 index 0000000000..e62605cf39 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/assist/TimeCounter.java @@ -0,0 +1,59 @@ +package dev.utils.common.assist; + +/** + * detail: 时间计时辅助类 + * @author Ttt + */ +public class TimeCounter { + + // 开始时间 + private long mStart; + + public TimeCounter() { + this(true); + } + + /** + * 构造函数 + * @param isStart 是否开始计时 + */ + public TimeCounter(final boolean isStart) { + if (isStart) start(); + } + + /** + * 开始计时 ( 毫秒 ) + * @return 开始时间 ( 毫秒 ) + */ + public long start() { + mStart = System.currentTimeMillis(); + return mStart; + } + + /** + * 获取持续的时间并重新启动 ( 毫秒 ) + * @return 距离上次开始时间的时间差 ( 毫秒 ) + */ + public long durationRestart() { + long now = System.currentTimeMillis(); + long diff = now - mStart; + mStart = now; + return diff; + } + + /** + * 获取持续的时间 ( 毫秒 ) + * @return 距离开始时间的时间差 ( 毫秒 ) + */ + public long duration() { + return System.currentTimeMillis() - mStart; + } + + /** + * 获取开始时间 ( 毫秒 ) + * @return 开始时间 ( 毫秒 ) + */ + public long getStartTime() { + return mStart; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/assist/TimeKeeper.java b/lib/DevApp/src/main/java/dev/utils/common/assist/TimeKeeper.java new file mode 100644 index 0000000000..d57836c190 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/assist/TimeKeeper.java @@ -0,0 +1,65 @@ +package dev.utils.common.assist; + +/** + * detail: 堵塞时间辅助类 + * @author Ttt + */ +public class TimeKeeper { + + /** + * 设置等待一段时间后, 通知方法 ( 异步 ) + * @param keepTimeMillis 堵塞时间 ( 毫秒 ) + * @param endCallback 结束回调通知 + */ + public void waitForEndAsyn(final long keepTimeMillis, final OnEndCallback endCallback) { + if (keepTimeMillis > 0L) { + new Thread(new Runnable() { + @Override + public void run() { + waitForEnd(keepTimeMillis, endCallback); + } + }).start(); + } + } + + /** + * 设置等待一段时间后, 通知方法 ( 同步 ) + * @param keepTimeMillis 堵塞时间 ( 毫秒 ) + * @param endCallback 结束回调通知 + */ + public void waitForEnd(final long keepTimeMillis, final OnEndCallback endCallback) { + if (keepTimeMillis > 0L) { + // 开始堵塞时间 + long startTime = System.currentTimeMillis(); + try { + // 进行堵塞 + Thread.sleep(keepTimeMillis); + // 触发回调 + if (endCallback != null) { + endCallback.onEnd(keepTimeMillis, startTime, System.currentTimeMillis(), false); + } + } catch (Exception e) { + // 触发回调 + if (endCallback != null) { + endCallback.onEnd(keepTimeMillis, startTime, System.currentTimeMillis(), true); + } + } + } + } + + /** + * detail: 结束通知回调 + * @author Ttt + */ + public interface OnEndCallback { + + /** + * 结束触发通知方法 + * @param keepTimeMillis 堵塞时间 ( 毫秒 ) + * @param startTimeMillis 开始堵塞时间 ( 毫秒 ) + * @param endTimeMillis 结束时间 ( 毫秒 ) + * @param isError 是否发生异常 + */ + void onEnd(long keepTimeMillis, long startTimeMillis, long endTimeMillis, boolean isError); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java b/lib/DevApp/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java new file mode 100644 index 0000000000..a474a9b7a1 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java @@ -0,0 +1,413 @@ +package dev.utils.common.assist.search; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; + +import dev.utils.JCLogUtils; + +/** + * detail: 文件广度优先搜索算法 ( 多线程 + 队列, 搜索某个目录下的全部文件 ) + * @author Ttt + */ +public final class FileBreadthFirstSearchUtils { + + // 日志 TAG + private static final String TAG = FileBreadthFirstSearchUtils.class.getSimpleName(); + + // ============ + // = 构造函数 = + // ============ + + /** + * 构造函数 + */ + public FileBreadthFirstSearchUtils() { + } + + /** + * 构造函数 + * @param searchHandler 搜索处理接口 + */ + public FileBreadthFirstSearchUtils(final SearchHandler searchHandler) { + this.mSearchHandler = searchHandler; + } + + /** + * detail: 文件信息 Item + * @author Ttt + */ + public final class FileItem { + + public FileItem(final File file) { + this.file = file; + } + + // 文件 + public File file; + + // 子文件夹目录 + public Map mapChilds; + + /** + * 保存子文件信息 + * @param file 文件 + * @return 文件信息 {@link FileItem} + */ + private synchronized FileItem put(final File file) { + if (mapChilds == null) { + mapChilds = new HashMap<>(); + } + if (file != null) { + FileItem fileItem = new FileItem(file); + mapChilds.put(file.getAbsolutePath(), fileItem); + return fileItem; + } + return null; + } + } + + /** + * detail: 文件队列 + * @author Ttt + */ + private class FileQueue { + + public FileQueue(File file, FileItem fileItem) { + this.file = file; + this.fileItem = fileItem; + } + + // 当前准备处理文件夹 + private File file; + + // 上一级目录对象 + private FileItem fileItem; + } + + /** + * detail: 搜索处理接口 + * @author Ttt + */ + public interface SearchHandler { + + /** + * 判断是否处理该文件 + * @param file 文件 + * @return {@code true} 处理该文件, {@code false} 跳过该文件不处理 + */ + boolean isHandlerFile(File file); + + /** + * 是否添加到集合 + * @param file 文件 + * @return {@code true} 添加, {@code false} 不添加 + */ + boolean isAddToList(File file); + + /** + * 搜索结束监听 + * @param rootFileItem 根文件信息 {@link FileItem} + * @param startTime 开始扫描时间 + * @param endTime 扫描结束时间 + */ + void OnEndListener(FileItem rootFileItem, long startTime, long endTime); + } + + // 搜索处理接口 + private SearchHandler mSearchHandler; + + // 内部实现接口 + private SearchHandler mInsideHandler = new SearchHandler() { + @Override + public boolean isHandlerFile(File file) { + if (mSearchHandler != null) { + return mSearchHandler.isHandlerFile(file); + } + return true; + } + + @Override + public boolean isAddToList(File file) { + if (mSearchHandler != null) { + return mSearchHandler.isAddToList(file); + } + return true; + } + + @Override + public void OnEndListener(FileItem rootFileItem, long startTime, long endTime) { + // 表示非搜索中 + mIsRunning = false; + // 触发回调 + if (mSearchHandler != null) { + mSearchHandler.OnEndListener(rootFileItem, startTime, endTime); + } + } + }; + + /** + * 设置搜索处理接口 + * @param searchHandler 搜索处理接口 + * @return {@link FileBreadthFirstSearchUtils} + */ + public FileBreadthFirstSearchUtils setSearchHandler(final SearchHandler searchHandler) { + this.mSearchHandler = searchHandler; + return this; + } + + /** + * 获取任务队列同时进行数量 + * @return 队列数量 + */ + public int getQueueSameTimeNumber() { + return mQueueSameTimeNumber; + } + + /** + * 任务队列同时进行数量 + * @param queueSameTimeNumber 同一时间线程队列数量 + * @return {@link FileBreadthFirstSearchUtils} + */ + public synchronized FileBreadthFirstSearchUtils setQueueSameTimeNumber(final int queueSameTimeNumber) { + if (mIsRunning) { + return this; + } + this.mQueueSameTimeNumber = queueSameTimeNumber; + return this; + } + + /** + * 是否搜索中 + * @return {@code true} 搜索 / 运行中, {@code false} 非搜索 / 运行中 + */ + public boolean isRunning() { + return mIsRunning; + } + + /** + * 停止搜索 + */ + public void stop() { + mIsStop = true; + } + + /** + * 是否停止搜索 + * @return {@code true} 已停止搜索, {@code false} 搜索中 + */ + public boolean isStop() { + return mIsStop; + } + + /** + * 获取开始搜索时间 ( 毫秒 ) + * @return 开始搜索时间 ( 毫秒 ) + */ + public long getStartTime() { + return mStartTime; + } + + /** + * 获取结束搜索时间 ( 毫秒 ) + * @return 结束搜索时间 ( 毫秒 ) + */ + public long getEndTime() { + return mEndTime; + } + + /** + * 获取延迟校验时间 ( 毫秒 ) + * @return 延迟线程校验时间 ( 毫秒 ) + */ + public long getDelayTime() { + return mDelayTime; + } + + /** + * 设置延迟校验时间 ( 毫秒 ) + * @param delayTimeMillis 延迟校验时间 ( 毫秒 ) + * @return {@link FileBreadthFirstSearchUtils} + */ + public FileBreadthFirstSearchUtils setDelayTime(final long delayTimeMillis) { + this.mDelayTime = delayTimeMillis; + return this; + } + + // = + + // 根目录对象 + private FileItem mRootFileItem; + // 判断是否运行中 + private boolean mIsRunning = false; + // 是否停止搜索 + private boolean mIsStop = false; + // 开始搜索时间 + private long mStartTime = 0L; + // 结束搜索时间 + private long mEndTime = 0L; + // 延迟时间 + private long mDelayTime = 50L; + // 任务队列同时进行数量 + private int mQueueSameTimeNumber = 5; + // 线程池 + private ExecutorService mExecutor = Executors.newCachedThreadPool(); + // 任务队列 + private LinkedBlockingQueue mTaskQueue = new LinkedBlockingQueue<>(); + + /** + * 搜索目录 + * @param path 根目录路径 + */ + public synchronized void query(final String path) { + if (mIsRunning) { + return; + } else if (path == null || path.trim().length() == 0) { + // 触发结束回调 + mInsideHandler.OnEndListener(null, -1, -1); + return; + } + // 表示运行中 + mIsRunning = true; + mIsStop = false; + // 设置开始搜索时间 + mStartTime = System.currentTimeMillis(); + try { + // 获取根目录 File + File file = new File(path); + // 初始化根目录 + mRootFileItem = new FileItem(file); + // 判断是否文件 + if (file.isFile()) { + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(mRootFileItem, mStartTime, mEndTime); + return; + } + // 获取文件夹全部子文件 + String[] fileArys = file.list(); + // 获取文件总数 + if (fileArys != null && fileArys.length != 0) { + new Thread(new Runnable() { + @Override + public void run() { + // 查询文件 + queryFile(mRootFileItem.file, mRootFileItem); + // 循环队列 + whileQueue(); + } + }).start(); + } else { + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(mRootFileItem, mStartTime, mEndTime); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "query"); + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(mRootFileItem, mStartTime, mEndTime); + } + } + + /** + * 搜索文件 + * @param file 文件 + * @param fileItem 所在文件夹信息对象 ( 上一级目录 ) + */ + private void queryFile(final File file, final FileItem fileItem) { + try { + if (mIsStop) { + return; + } + if (file != null && file.exists()) { + // 判断是否处理 + if (mInsideHandler.isHandlerFile(file)) { + // 如果属于文件夹 + if (file.isDirectory()) { + // 获取文件夹全部子文件 + File[] files = file.listFiles(); + if (files == null) { + return; + } + // 循环处理 + for (File f : files) { + // 属于文件夹 + if (f.isDirectory()) { + if (mIsStop) { + return; + } + FileItem subFileItem = fileItem.put(f); + // 添加任务 + mTaskQueue.offer(new FileQueue(f, subFileItem)); + } else { // 属于文件 + if (!mIsStop && mInsideHandler.isAddToList(f)) { + // 属于文件则直接保存 + fileItem.put(f); + } + } + } + } else { // 属于文件 + if (!mIsStop && mInsideHandler.isAddToList(file)) { + // 属于文件则直接保存 + fileItem.put(file); + } + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "queryFile"); + } + } + + // ============== + // = 线程池处理 = + // ============== + + /** + * 循环队列 + */ + private void whileQueue() { + // 判断是否为 null + boolean isEmpty = mTaskQueue.isEmpty(); + // 循环则不处理 + while (!isEmpty) { + if (mIsStop) break; + // 获取线程活动数量 + int threadCount = ((ThreadPoolExecutor) mExecutor).getActiveCount(); + // 判断是否超过 + if (threadCount > mQueueSameTimeNumber) { + continue; + } + // 获取文件对象 + final FileQueue fileQueue = mTaskQueue.poll(); + // 判断是否为 null + if (fileQueue != null) { + mExecutor.execute(new Runnable() { + @Override + public void run() { + queryFile(fileQueue.file, fileQueue.fileItem); + } + }); + } + + // 判断是否存在队列数据 + isEmpty = (mTaskQueue.isEmpty() && threadCount == 0); + if (isEmpty) { // 如果不存在, 防止搜索过快, 延迟再次判断 + if (mIsStop) break; + try { + Thread.sleep(mDelayTime); + } catch (Exception e) { + } + isEmpty = (mTaskQueue.isEmpty() && threadCount == 0); + } + } + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(mRootFileItem, mStartTime, mEndTime); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java b/lib/DevApp/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java new file mode 100644 index 0000000000..4a748045d1 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java @@ -0,0 +1,288 @@ +package dev.utils.common.assist.search; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 文件深度优先搜索算法 ( 递归搜索某个目录下的全部文件 ) + * @author Ttt + */ +public final class FileDepthFirstSearchUtils { + + // 日志 TAG + private static final String TAG = FileDepthFirstSearchUtils.class.getSimpleName(); + + // ============ + // = 构造函数 = + // ============ + + /** + * 构造函数 + */ + public FileDepthFirstSearchUtils() { + } + + /** + * 构造函数 + * @param searchHandler 搜索处理接口 + */ + public FileDepthFirstSearchUtils(final SearchHandler searchHandler) { + this.mSearchHandler = searchHandler; + } + + /** + * detail: 文件信息 Item + * @author Ttt + */ + public final class FileItem { + + public FileItem(final File file) { + this.file = file; + } + + // 文件 + public File file; + + // 子文件夹目录 + public List listChilds = null; + } + + /** + * detail: 搜索处理接口 + * @author Ttt + */ + public interface SearchHandler { + + /** + * 判断是否处理该文件 + * @param file 文件 + * @return {@code true} 处理该文件, {@code false} 跳过该文件不处理 + */ + boolean isHandlerFile(File file); + + /** + * 是否添加到集合 + * @param file 文件 + * @return {@code true} 添加, {@code false} 不添加 + */ + boolean isAddToList(File file); + + /** + * 搜索结束监听 + * @param lists 根目录的子文件目录集合 + * @param startTime 开始扫描时间 + * @param endTime 扫描结束时间 + */ + void OnEndListener(List lists, long startTime, long endTime); + } + + // 搜索处理接口 + private SearchHandler mSearchHandler; + + // 内部实现接口 + private SearchHandler mInsideHandler = new SearchHandler() { + @Override + public boolean isHandlerFile(File file) { + if (mSearchHandler != null) { + return mSearchHandler.isHandlerFile(file); + } + return true; + } + + @Override + public boolean isAddToList(File file) { + if (mSearchHandler != null) { + return mSearchHandler.isAddToList(file); + } + return true; + } + + @Override + public void OnEndListener(List lists, long startTime, long endTime) { + // 表示非搜索中 + mIsRunning = false; + // 触发回调 + if (mSearchHandler != null) { + mSearchHandler.OnEndListener(lists, startTime, endTime); + } + } + }; + + /** + * 设置搜索处理接口 + * @param searchHandler 搜索处理接口 + * @return {@link FileDepthFirstSearchUtils} + */ + public FileDepthFirstSearchUtils setSearchHandler(final SearchHandler searchHandler) { + this.mSearchHandler = searchHandler; + return this; + } + + /** + * 是否搜索中 + * @return {@code true} 搜索 / 运行中, {@code false} 非搜索 / 运行中 + */ + public boolean isRunning() { + return mIsRunning; + } + + /** + * 停止搜索 + */ + public void stop() { + mIsStop = true; + } + + /** + * 是否停止搜索 + * @return {@code true} 已停止搜索, {@code false} 搜索中 + */ + public boolean isStop() { + return mIsStop; + } + + /** + * 获取开始搜索时间 ( 毫秒 ) + * @return 开始搜索时间 ( 毫秒 ) + */ + public long getStartTime() { + return mStartTime; + } + + /** + * 获取结束搜索时间 ( 毫秒 ) + * @return 结束搜索时间 ( 毫秒 ) + */ + public long getEndTime() { + return mEndTime; + } + + // = + + // 判断是否运行中 + private boolean mIsRunning = false; + // 是否停止搜索 + private boolean mIsStop = false; + // 开始搜索时间 + private long mStartTime = 0L; + // 结束搜索时间 + private long mEndTime = 0L; + + /** + * 搜索目录 + * @param path 根目录路径 + * @param isRelation 是否关联到 Child List + */ + public synchronized void query(final String path, final boolean isRelation) { + if (mIsRunning) { + return; + } else if (path == null || path.trim().length() == 0) { + // 触发结束回调 + mInsideHandler.OnEndListener(null, -1, -1); + return; + } + // 表示运行中 + mIsRunning = true; + mIsStop = false; + // 设置开始搜索时间 + mStartTime = System.currentTimeMillis(); + try { + // 获取根目录 File + final File file = new File(path); + // 判断是否文件 + if (file.isFile()) { + List lists = new ArrayList<>(); + lists.add(new FileItem(file)); + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(lists, mStartTime, mEndTime); + return; + } + // 获取文件夹全部子文件 + String[] fileArys = file.list(); + // 获取文件总数 + if (fileArys != null && fileArys.length != 0) { + new Thread(new Runnable() { + @Override + public void run() { + List lists = new ArrayList<>(); + // 查询文件 + queryFile(file, lists, isRelation); + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(lists, mStartTime, mEndTime); + } + }).start(); + } else { + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(null, mStartTime, mEndTime); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "query"); + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(null, mStartTime, mEndTime); + } + } + + /** + * 搜索文件 + * @param file 文件 + * @param lists 保存数据源 + * @param isRelation 是否关联到 Child List + */ + private void queryFile(final File file, final List lists, final boolean isRelation) { + try { + if (mIsStop) { + return; + } + if (file != null && file.exists()) { + // 判断是否处理 + if (mInsideHandler.isHandlerFile(file)) { + // 如果属于文件夹 + if (file.isDirectory()) { + // 获取文件夹全部子文件 + File[] files = file.listFiles(); + if (files == null) { + return; + } + // 循环处理 + for (File f : files) { + if (isRelation) { + if (f.isDirectory()) { + List childs = new ArrayList<>(); + // 查找文件 + queryFile(f, childs, isRelation); + // 保存数据 + FileItem fileItem = new FileItem(f); + fileItem.listChilds = childs; + lists.add(fileItem); + } else { + // 属于文件 + if (mInsideHandler.isAddToList(f)) { + // 属于文件则直接保存 + lists.add(new FileItem(f)); + } + } + } else { + // 查找文件 + queryFile(f, lists, isRelation); + } + } + } else { // 属于文件 + if (mInsideHandler.isAddToList(file)) { + // 属于文件则直接保存 + lists.add(new FileItem(file)); + } + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "queryFile"); + } + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/assist/Base64.java b/lib/DevApp/src/main/java/dev/utils/common/cipher/Base64.java similarity index 86% rename from DevLibUtils/src/main/java/dev/utils/common/assist/Base64.java rename to lib/DevApp/src/main/java/dev/utils/common/cipher/Base64.java index 2761cd6002..650618bd0a 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/assist/Base64.java +++ b/lib/DevApp/src/main/java/dev/utils/common/cipher/Base64.java @@ -1,30 +1,28 @@ /* * Copyright (C) 2010 The Android Open Source Project - * * 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 dev.utils.common.assist; +package dev.utils.common.cipher; import java.io.UnsupportedEncodingException; /** * detail: Base64 工具类 - * Utilities for encoding and decoding the Base64 representation of - * binary data. See RFCs 2045 and 3548. + * @author Android + *

+ *     Utilities for encoding and decoding the Base64 representation of
+ *     binary data.  See RFCs 2045 and 3548.
+ * 
*/ -public class Base64 { +public final class Base64 { /** * Default values for encoder/decoder flags. */ @@ -77,13 +75,11 @@ public class Base64 { * provided by the caller, and must be big enough to hold all * the coded data. On exit, this.opwill be set to the length * of the coded data. - * * @param finish true if this is the final call to process for - * this object. Will finalize the coder state and - * include any final bytes in the output. - * + * this object. Will finalize the coder state and + * include any final bytes in the output. * @return true if the input so far is good; false if some - * error has been detected in the input stream.. + * error has been detected in the input stream.. */ public abstract boolean process(byte[] input, int offset, int len, boolean finish); @@ -102,17 +98,15 @@ public class Base64 { /** * Decode the Base64-encoded data in input and return the data in * a new byte array. - * + *

* The padding '=' characters at the end are considered optional, but * if any are present, there must be the correct number of them. - * - * @param str the input String to decode, which is converted to - * bytes using the default charset - * @param flags controls certain features of the decoded output. - * Pass {@code DEFAULT} to decode standard Base64. - * + * @param str the input String to decode, which is converted to + * bytes using the default charset + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. * @throws IllegalArgumentException if the input contains - * incorrect padding + * incorrect padding */ public static byte[] decode(String str, int flags) { return decode(str.getBytes(), flags); @@ -121,16 +115,14 @@ public static byte[] decode(String str, int flags) { /** * Decode the Base64-encoded data in input and return the data in * a new byte array. - * + *

* The padding '=' characters at the end are considered optional, but * if any are present, there must be the correct number of them. - * * @param input the input array to decode - * @param flags controls certain features of the decoded output. - * Pass {@code DEFAULT} to decode standard Base64. - * + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. * @throws IllegalArgumentException if the input contains - * incorrect padding + * incorrect padding */ public static byte[] decode(byte[] input, int flags) { return decode(input, 0, input.length, flags); @@ -139,23 +131,21 @@ public static byte[] decode(byte[] input, int flags) { /** * Decode the Base64-encoded data in input and return the data in * a new byte array. - * + *

* The padding '=' characters at the end are considered optional, but * if any are present, there must be the correct number of them. - * * @param input the data to decode * @param offset the position within the input array at which to start * @param len the number of bytes of input to decode * @param flags controls certain features of the decoded output. * Pass {@code DEFAULT} to decode standard Base64. - * * @throws IllegalArgumentException if the input contains - * incorrect padding + * incorrect padding */ public static byte[] decode(byte[] input, int offset, int len, int flags) { // Allocate space for the most data the input could represent. // (It could contain less if it contains whitespace, etc.) - Decoder decoder = new Decoder(flags, new byte[len*3/4]); + Decoder decoder = new Decoder(flags, new byte[len * 3 / 4]); if (!decoder.process(input, offset, len, true)) { throw new IllegalArgumentException("bad base-64"); @@ -178,12 +168,12 @@ public static byte[] decode(byte[] input, int offset, int len, int flags) { * Lookup table for turning bytes into their position in the * Base64 alphabet. */ - private static final int DECODE[] = { + private static final int[] DECODE = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, @@ -201,12 +191,12 @@ public static byte[] decode(byte[] input, int offset, int len, int flags) { * Decode lookup table for the "web safe" variant (RFC 3548 * sec. 4) where - and _ replace + and /. */ - private static final int DECODE_WEBSAFE[] = { + private static final int[] DECODE_WEBSAFE = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, @@ -220,7 +210,9 @@ public static byte[] decode(byte[] input, int offset, int len, int flags) { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; - /** Non-data values in the DECODE arrays. */ + /** + * Non-data values in the DECODE arrays. + */ private static final int SKIP = -1; private static final int EQUALS = -2; @@ -251,14 +243,13 @@ public Decoder(int flags, byte[] output) { * len} bytes could decode to. */ public int maxOutputSize(int len) { - return len * 3/4 + 10; + return len * 3 / 4 + 10; } /** * Decode another block of input data. - * * @return true if the state machine is still healthy. false if - * bad base-64 data has been detected in the input stream. + * bad base-64 data has been detected in the input stream. */ public boolean process(byte[] input, int offset, int len, boolean finish) { if (this.state == 6) return false; @@ -293,13 +284,13 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { // You can remove this whole block and the output should // be the same, just slower. if (state == 0) { - while (p+4 <= len && + while (p + 4 <= len && (value = ((alphabet[input[p] & 0xff] << 18) | - (alphabet[input[p+1] & 0xff] << 12) | - (alphabet[input[p+2] & 0xff] << 6) | - (alphabet[input[p+3] & 0xff]))) >= 0) { - output[op+2] = (byte) value; - output[op+1] = (byte) (value >> 8); + (alphabet[input[p + 1] & 0xff] << 12) | + (alphabet[input[p + 2] & 0xff] << 6) | + (alphabet[input[p + 3] & 0xff]))) >= 0) { + output[op + 2] = (byte) value; + output[op + 1] = (byte) (value >> 8); output[op] = (byte) (value >> 16); op += 3; p += 4; @@ -307,7 +298,7 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { if (p >= len) break; } - // The fast path isn't available -- either we've read a + // The fast path isn't available either we've read a // partial tuple, or the next four input bytes aren't all // data, or whatever. Fall back to the slower state // machine implementation. @@ -354,15 +345,15 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { if (d >= 0) { // Emit the output triple and return to state 0. value = (value << 6) | d; - output[op+2] = (byte) value; - output[op+1] = (byte) (value >> 8); + output[op + 2] = (byte) value; + output[op + 1] = (byte) (value >> 8); output[op] = (byte) (value >> 16); op += 3; state = 0; } else if (d == EQUALS) { // Emit the last (partial) output tuple; // expect no further data or padding characters. - output[op+1] = (byte) (value >> 2); + output[op + 1] = (byte) (value >> 2); output[op] = (byte) (value >> 10); op += 2; state = 5; @@ -445,11 +436,10 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { /** * Base64-encode the given data and return a newly allocated * String with the result. - * - * @param input the data to encode - * @param flags controls certain features of the encoded output. - * Passing {@code DEFAULT} results in output that - * adheres to RFC 2045. + * @param input the data to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. */ public static String encodeToString(byte[] input, int flags) { try { @@ -463,7 +453,6 @@ public static String encodeToString(byte[] input, int flags) { /** * Base64-encode the given data and return a newly allocated * String with the result. - * * @param input the data to encode * @param offset the position within the input array at which to * start @@ -484,11 +473,10 @@ public static String encodeToString(byte[] input, int offset, int len, int flags /** * Base64-encode the given data and return a newly allocated * byte[] with the result. - * - * @param input the data to encode - * @param flags controls certain features of the encoded output. - * Passing {@code DEFAULT} results in output that - * adheres to RFC 2045. + * @param input the data to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. */ public static byte[] encode(byte[] input, int flags) { return encode(input, 0, input.length, flags); @@ -497,7 +485,6 @@ public static byte[] encode(byte[] input, int flags) { /** * Base64-encode the given data and return a newly allocated * byte[] with the result. - * * @param input the data to encode * @param offset the position within the input array at which to * start @@ -519,15 +506,20 @@ public static byte[] encode(byte[] input, int offset, int len, int flags) { } } else { switch (len % 3) { - case 0: break; - case 1: output_len += 2; break; - case 2: output_len += 3; break; + case 0: + break; + case 1: + output_len += 2; + break; + case 2: + output_len += 3; + break; } } // Account for the newlines, if any. if (encoder.do_newline && len > 0) { - output_len += (((len-1) / (3 * Encoder.LINE_GROUPS)) + 1) * + output_len += (((len - 1) / (3 * Encoder.LINE_GROUPS)) + 1) * (encoder.do_cr ? 2 : 1); } @@ -551,7 +543,7 @@ public static byte[] encode(byte[] input, int offset, int len, int flags) { * Lookup table for turning Base64 alphabet positions (6 bits) * into output bytes. */ - private static final byte ENCODE[] = { + private static final byte[] ENCODE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', @@ -562,7 +554,7 @@ public static byte[] encode(byte[] input, int offset, int len, int flags) { * Lookup table for turning Base64 alphabet positions (6 bits) * into output bytes. */ - private static final byte ENCODE_WEBSAFE[] = { + private static final byte[] ENCODE_WEBSAFE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', @@ -597,7 +589,7 @@ public Encoder(int flags, byte[] output) { * len} bytes could encode to. */ public int maxOutputSize(int len) { - return len * 8/5 + 10; + return len * 8 / 5 + 10; } public boolean process(byte[] input, int offset, int len, boolean finish) { @@ -621,18 +613,19 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { break; case 1: - if (p+2 <= len) { + if (p + 2 <= len) { // A 1-byte tail with at least 2 bytes of // input available now. v = ((tail[0] & 0xff) << 16) | ((input[p++] & 0xff) << 8) | (input[p++] & 0xff); tailLen = 0; - }; + } + ; break; case 2: - if (p+1 <= len) { + if (p + 1 <= len) { // A 2-byte tail with at least 1 byte of input. v = ((tail[0] & 0xff) << 16) | ((tail[1] & 0xff) << 8) | @@ -659,14 +652,14 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { // The main loop, turning 3 input bytes into 4 output bytes on // each iteration. - while (p+3 <= len) { + while (p + 3 <= len) { v = ((input[p] & 0xff) << 16) | - ((input[p+1] & 0xff) << 8) | - (input[p+2] & 0xff); + ((input[p + 1] & 0xff) << 8) | + (input[p + 2] & 0xff); output[op] = alphabet[(v >> 18) & 0x3f]; - output[op+1] = alphabet[(v >> 12) & 0x3f]; - output[op+2] = alphabet[(v >> 6) & 0x3f]; - output[op+3] = alphabet[v & 0x3f]; + output[op + 1] = alphabet[(v >> 12) & 0x3f]; + output[op + 2] = alphabet[(v >> 6) & 0x3f]; + output[op + 3] = alphabet[v & 0x3f]; p += 3; op += 4; if (--count == 0) { @@ -682,7 +675,7 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { // remaining in input; there should be at most two bytes // total. - if (p-tailLen == len-1) { + if (p - tailLen == len - 1) { int t = 0; v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4; tailLen -= t; @@ -696,7 +689,7 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { if (do_cr) output[op++] = '\r'; output[op++] = '\n'; } - } else if (p-tailLen == len-2) { + } else if (p - tailLen == len - 2) { int t = 0; v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) | (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2); @@ -722,11 +715,11 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { // Save the leftovers in tail to be consumed on the next // call to encodeInternal. - if (p == len-1) { + if (p == len - 1) { tail[tailLen++] = input[p]; - } else if (p == len-2) { + } else if (p == len - 2) { tail[tailLen++] = input[p]; - tail[tailLen++] = input[p+1]; + tail[tailLen++] = input[p + 1]; } } @@ -737,5 +730,6 @@ public boolean process(byte[] input, int offset, int len, boolean finish) { } } - private Base64() { } // don't instantiate -} + private Base64() { + } // don't instantiate +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/cipher/Base64Cipher.java b/lib/DevApp/src/main/java/dev/utils/common/cipher/Base64Cipher.java new file mode 100644 index 0000000000..92b89082b1 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/cipher/Base64Cipher.java @@ -0,0 +1,75 @@ +package dev.utils.common.cipher; + +/** + * detail: Base64 编解码 ( 并进行 ) 加解密 + * @author Ttt + */ +public class Base64Cipher implements Cipher { + + // 中间加密层 + private Cipher mCipher; + // Base64 编解码 flags + private int mFlags = Base64.DEFAULT; + + /** + * 构造函数 + * @param flags Base64 编解码 flags + */ + public Base64Cipher(final int flags) { + this.mFlags = flags; + } + + /** + * 构造函数 + * @param cipher 加解密中间层 + */ + public Base64Cipher(final Cipher cipher) { + this.mCipher = cipher; + } + + /** + * 构造函数 + * @param cipher 加解密中间层 + * @param flags Base64 编解码 flags + */ + public Base64Cipher(final Cipher cipher, final int flags) { + this.mCipher = cipher; + this.mFlags = flags; + } + + // = + + /** + * 解码 + * @param data 待解码数据 + * @return 解码后的 byte[] + */ + @Override + public byte[] decrypt(byte[] data) { + if (data == null) return null; + // 先解码 + data = Base64.decode(data, mFlags); + // 再解密 + if (mCipher != null) { + data = mCipher.decrypt(data); + } + return data; + } + + /** + * 编码 + * @param data 待编码数据 + * @return 编码后的 byte[] + */ + @Override + public byte[] encrypt(byte[] data) { + if (data == null) return null; + // 先加密 + if (mCipher != null) { + data = mCipher.encrypt(data); + } + if (data == null) return null; + // 再编码 + return Base64.encode(data, mFlags); + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/cipher/Cipher.java b/lib/DevApp/src/main/java/dev/utils/common/cipher/Cipher.java similarity index 57% rename from DevLibUtils/src/main/java/dev/utils/common/cipher/Cipher.java rename to lib/DevApp/src/main/java/dev/utils/common/cipher/Cipher.java index 2039149ed8..81d77b0ac2 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/cipher/Cipher.java +++ b/lib/DevApp/src/main/java/dev/utils/common/cipher/Cipher.java @@ -1,9 +1,8 @@ package dev.utils.common.cipher; /** - * detail: 通用加密/解密抽象实现类 - * @author MaTianyu + * detail: 通用加解密中间层 + * @author Ttt */ public interface Cipher extends Decrypt, Encrypt { - -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/cipher/CipherUtils.java b/lib/DevApp/src/main/java/dev/utils/common/cipher/CipherUtils.java new file mode 100644 index 0000000000..33b31519c2 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/cipher/CipherUtils.java @@ -0,0 +1,199 @@ +package dev.utils.common.cipher; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import dev.utils.JCLogUtils; + +/** + * detail: 加密工具类 + * @author Ttt + */ +public final class CipherUtils { + + private CipherUtils() { + } + + // 日志 TAG + private static final String TAG = CipherUtils.class.getSimpleName(); + + /** + * 加密方法 + * @param object 待加密对象 + * @return 加密后的十六进制字符串 + */ + public static String encrypt(final Object object) { + return encrypt(object, null); + } + + /** + * 加密方法 + * @param object 待加密对象 + * @param cipher 加解密中间层 + * @return 加密后的十六进制字符串 + */ + public static String encrypt(final Object object, final Cipher cipher) { + if (object == null) return null; + byte[] bytes = objectToBytes(object); + if (cipher != null) bytes = cipher.encrypt(bytes); + return toHexString(bytes); + } + + // = + + /** + * 解密方法 + * @param hex 十六进制字符串 + * @return 解密后的对象 + */ + public static Object decrypt(final String hex) { + return decrypt(hex, null); + } + + /** + * 解密方法 + * @param hex 十六进制字符串 + * @param cipher 加解密中间层 + * @return 解密后的对象 + */ + public static Object decrypt(final String hex, final Cipher cipher) { + if (hex == null) return null; + byte[] bytes = decodeHex(hex.toCharArray()); + if (cipher != null) bytes = cipher.decrypt(bytes); + return bytesToObject(bytes); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data) { + return toHexString(data, HEX_DIGITS); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + /** + * 将十六进制字符数组解码 + * @param data 十六进制 char[] + * @return 十六进制转 ( 解 ) 码后的数据 + */ + private static byte[] decodeHex(final char[] data) { + if (data == null) return null; + try { + int len = data.length; + byte[] out = new byte[len >> 1]; + // 十六进制由两个字符组成 + for (int i = 0, j = 0; j < len; i++) { + int d = toDigit(data[j], j) << 4; + j++; + d = d | toDigit(data[j], j); + j++; + out[i] = (byte) (d & 0xFF); + } + return out; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "decodeHex"); + } + return null; + } + + /** + * 将十六进制字符转换成一个整数 + * @param ch 十六进制 char + * @param index 十六进制字符在字符数组中的位置 + * @return 一个整数 + * @throws Exception 当 ch 不是一个合法的十六进制字符时, 抛出运行时异常 + */ + private static int toDigit(final char ch, final int index) throws Exception { + int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new Exception(String.format("Illegal hexadecimal character %s at index %s", ch, index)); + } + return digit; + } + + /** + * byte[] 转为 Object + * @param bytes byte[] + * @return {@link Object} + */ + private static Object bytesToObject(final byte[] bytes) { + if (bytes == null) return null; + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); + return ois.readObject(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToObject"); + } finally { + if (ois != null) { + try { + ois.close(); + } catch (Exception e) { + } + } + } + return null; + } + + /** + * Object 转为 byte[] + * @param object Object + * @return byte[] + */ + private static byte[] objectToBytes(final Object object) { + if (object == null) return null; + ObjectOutputStream oos = null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(baos); + oos.writeObject(object); + return baos.toByteArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "objectToBytes"); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (Exception e) { + } + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/cipher/Decrypt.java b/lib/DevApp/src/main/java/dev/utils/common/cipher/Decrypt.java new file mode 100644 index 0000000000..ab696494a6 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/cipher/Decrypt.java @@ -0,0 +1,15 @@ +package dev.utils.common.cipher; + +/** + * detail: 解密 ( 解码 ) 接口 + * @author Ttt + */ +public interface Decrypt { + + /** + * 解密 ( 解码 ) 方法 + * @param data 待解码数据 + * @return 解码后的 byte[] + */ + byte[] decrypt(byte[] data); +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/cipher/Encrypt.java b/lib/DevApp/src/main/java/dev/utils/common/cipher/Encrypt.java new file mode 100644 index 0000000000..d802d33e72 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/cipher/Encrypt.java @@ -0,0 +1,15 @@ +package dev.utils.common.cipher; + +/** + * detail: 加密 ( 编码 ) 接口 + * @author Ttt + */ +public interface Encrypt { + + /** + * 加密 ( 编码 ) 方法 + * @param data 待编码数据 + * @return 编码后的 byte[] + */ + byte[] encrypt(byte[] data); +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/AESUtils.java b/lib/DevApp/src/main/java/dev/utils/common/encrypt/AESUtils.java similarity index 56% rename from DevLibUtils/src/main/java/dev/utils/common/encrypt/AESUtils.java rename to lib/DevApp/src/main/java/dev/utils/common/encrypt/AESUtils.java index da5c1a1a6a..e6549af1ae 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/AESUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/common/encrypt/AESUtils.java @@ -8,28 +8,32 @@ import dev.utils.JCLogUtils; /** - * detail: AES对称加密(Advanced Encryption Standard,高级数据加密标准,AES算法可以有效抵制针对DES的攻击算法,对称加密算法) - * Created by Ttt + * detail: AES 对称加密工具类 + * @author Ttt + *

+ *     Advanced Encryption Standard 高级数据加密标准 ( 对称加密算法 )
+ *     AES 算法可以有效抵制针对 DES 的攻击算法
+ * 
*/ public final class AESUtils { private AESUtils() { } - // 日志TAG + // 日志 TAG private static final String TAG = AESUtils.class.getSimpleName(); /** * 生成密钥 - * @return + * @return 密钥 byte[] */ - public static byte[] initKey(){ + public static byte[] initKey() { try { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); - keyGen.init(256); //192 256 + keyGen.init(256); // 192 256 SecretKey secretKey = keyGen.generateKey(); return secretKey.getEncoded(); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "initKey"); } return null; @@ -37,18 +41,18 @@ public static byte[] initKey(){ /** * AES 加密 - * @param data - * @param key - * @return + * @param data 待加密数据 + * @param key 密钥 + * @return 加密后的 byte[] */ - public static byte[] encrypt(byte[] data, byte[] key){ + public static byte[] encrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; try { SecretKey secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); - byte[] cipherBytes = cipher.doFinal(data); - return cipherBytes; - } catch (Exception e){ + return cipher.doFinal(data); + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "encrypt"); } return null; @@ -56,18 +60,18 @@ public static byte[] encrypt(byte[] data, byte[] key){ /** * AES 解密 - * @param data - * @param key - * @return + * @param data 待解密数据 + * @param key 密钥 + * @return 解密后的 byte[] */ - public static byte[] decrypt(byte[] data, byte[] key){ + public static byte[] decrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; try { SecretKey secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, secretKey); - byte[] plainBytes = cipher.doFinal(data); - return plainBytes; - } catch (Exception e){ + return cipher.doFinal(data); + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "decrypt"); } return null; diff --git a/lib/DevApp/src/main/java/dev/utils/common/encrypt/CRCUtils.java b/lib/DevApp/src/main/java/dev/utils/common/encrypt/CRCUtils.java new file mode 100644 index 0000000000..68ca4f1505 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/encrypt/CRCUtils.java @@ -0,0 +1,88 @@ +package dev.utils.common.encrypt; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.zip.CRC32; + +import dev.utils.JCLogUtils; + +/** + * detail: CRC 工具类 + * @author Ttt + *
+ *     Cyclic Redundancy Check 循环冗余校验
+ *     CRC 是一种根据网络数据包或电脑文件等数据产生简短固定位数校验码的一种散列函数
+ * 
+ */ +public final class CRCUtils { + + private CRCUtils() { + } + + // 日志 TAG + private static final String TAG = CRCUtils.class.getSimpleName(); + + /** + * 获取 CRC32 值 + * @param data 字符串数据 + * @return CRC32 long 值 + */ + public static long getCRC32(final String data) { + if (data == null) return -1L; + try { + CRC32 crc32 = new CRC32(); + crc32.update(data.getBytes()); + return crc32.getValue(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getCRC32"); + } + return -1L; + } + + /** + * 获取 CRC32 值 + * @param data 字符串数据 + * @return CRC32 字符串 + */ + public static String getCRC32ToHexString(final String data) { + if (data == null) return null; + try { + CRC32 crc32 = new CRC32(); + crc32.update(data.getBytes()); + return Long.toHexString(crc32.getValue()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getCRC32ToHexString"); + } + return null; + } + + /** + * 获取文件 CRC32 值 + * @param filePath 文件路径 + * @return 文件 CRC32 值 + */ + public static String getFileCRC32(final String filePath) { + if (filePath == null) return null; + InputStream is = null; + try { + is = new FileInputStream(filePath); + byte[] buffer = new byte[1024]; + CRC32 crc32 = new CRC32(); + int numRead; + while ((numRead = is.read(buffer)) > 0) { + crc32.update(buffer, 0, numRead); + } + return Long.toHexString(crc32.getValue()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileCRC32"); + } finally { + if (is != null) { + try { + is.close(); + } catch (Exception e) { + } + } + } + return null; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/DESUtils.java b/lib/DevApp/src/main/java/dev/utils/common/encrypt/DESUtils.java similarity index 50% rename from DevLibUtils/src/main/java/dev/utils/common/encrypt/DESUtils.java rename to lib/DevApp/src/main/java/dev/utils/common/encrypt/DESUtils.java index e13c20f094..68898c8fbc 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/DESUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/common/encrypt/DESUtils.java @@ -11,50 +11,51 @@ import dev.utils.JCLogUtils; /** - * detail: DES对称加密(Data Encryption Standard,数据加密标准,对称加密算法) - * Created by Ttt + * detail: DES 对称加密工具类 + * @author Ttt + *
+ *     Data Encryption Standard 数据加密标准 ( 对称加密算法 )
+ * 
*/ public final class DESUtils { private DESUtils() { } - // 日志TAG - private static final String TAG = CRCUtils.class.getSimpleName(); + // 日志 TAG + private static final String TAG = DESUtils.class.getSimpleName(); /** - * 返回可逆算法DES的密钥 - * @param key 前8字节将被用来生成密钥。 - * @return 生成的密钥 - * @throws Exception + * 获取可逆算法 DES 的密钥 + * @param key 前八个字节将被用来生成密钥 + * @return 可逆算法 DES 的密钥 */ - public static Key getDESKey(byte[] key){ + public static Key getDESKey(final byte[] key) { + if (key == null) return null; try { - DESKeySpec des = new DESKeySpec(key); + DESKeySpec desKey = new DESKeySpec(key); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); - return keyFactory.generateSecret(des); - } catch (Exception e){ + return keyFactory.generateSecret(desKey); + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "getDESKey"); } return null; } - /** * DES 加密 - * @param data - * @param key - * @return - * @throws Exception + * @param data 待加密数据 + * @param key 密钥 + * @return 加密后的 byte[] */ - public static byte[] encrypt(byte[] data, byte[] key){ + public static byte[] encrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; try { SecretKey secretKey = new SecretKeySpec(key, "DES"); Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); - byte[] cipherBytes = cipher.doFinal(data); - return cipherBytes; - } catch (Exception e){ + return cipher.doFinal(data); + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "encrypt"); } return null; @@ -62,21 +63,20 @@ public static byte[] encrypt(byte[] data, byte[] key){ /** * DES 解密 - * @param data - * @param key - * @return - * @throws Exception + * @param data 待解密数据 + * @param key 密钥 + * @return 解密后的 byte[] */ - public static byte[] decrypt(byte[] data, byte[] key){ + public static byte[] decrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; try { SecretKey secretKey = new SecretKeySpec(key, "DES"); Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKey); - byte[] plainBytes = cipher.doFinal(data); - return plainBytes; - } catch (Exception e){ + return cipher.doFinal(data); + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "decrypt"); } return null; } -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/encrypt/EncryptUtils.java b/lib/DevApp/src/main/java/dev/utils/common/encrypt/EncryptUtils.java new file mode 100644 index 0000000000..42fd1250b2 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/encrypt/EncryptUtils.java @@ -0,0 +1,1137 @@ +package dev.utils.common.encrypt; + +import java.io.File; +import java.io.FileInputStream; +import java.security.DigestInputStream; +import java.security.Key; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import dev.utils.JCLogUtils; +import dev.utils.common.cipher.Base64; + +/** + * detail: 加解密通用工具类 + * @author Blankj + * @author Ttt ( 重写、规范注释、逻辑判断等 ) + */ +public final class EncryptUtils { + + private EncryptUtils() { + } + + // 日志 TAG + private static final String TAG = EncryptUtils.class.getSimpleName(); + + /** + * MD2 加密 + * @param data 待加密数据 + * @return MD2 加密后的数据 + */ + public static byte[] encryptMD2(final byte[] data) { + return hashTemplate(data, "MD2"); + } + + /** + * MD2 加密 + * @param data 待加密数据 + * @return MD2 加密后的十六进制字符串 + */ + public static String encryptMD2ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptMD2ToHexString(data.getBytes()); + } + + /** + * MD2 加密 + * @param data 待加密数据 + * @return MD2 加密后的十六进制字符串 + */ + public static String encryptMD2ToHexString(final byte[] data) { + return toHexString(encryptMD2(data)); + } + + // = + + /** + * MD5 加密 + * @param data 待加密数据 + * @return MD5 加密后的数据 + */ + public static byte[] encryptMD5(final byte[] data) { + return hashTemplate(data, "MD5"); + } + + /** + * MD5 加密 + * @param data 待加密数据 + * @return MD5 加密后的十六进制字符串 + */ + public static String encryptMD5ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptMD5ToHexString(data.getBytes()); + } + + /** + * MD5 加密 + * @param data 待加密数据 + * @param salt salt + * @return MD5 加密后的十六进制字符串 + */ + public static String encryptMD5ToHexString(final String data, final String salt) { + if (data == null && salt == null) return null; + if (salt == null) return toHexString(encryptMD5(data.getBytes())); + if (data == null) return toHexString(encryptMD5(salt.getBytes())); + return toHexString(encryptMD5((data + salt).getBytes())); + } + + // = + + /** + * MD5 加密 + * @param data 待加密数据 + * @return MD5 加密后的十六进制字符串 + */ + public static String encryptMD5ToHexString(final byte[] data) { + return toHexString(encryptMD5(data)); + } + + /** + * MD5 加密 + * @param data 待加密数据 + * @param salt salt + * @return MD5 加密后的十六进制字符串 + */ + public static String encryptMD5ToHexString(final byte[] data, final byte[] salt) { + if (data == null && salt == null) return null; + if (salt == null) return toHexString(encryptMD5(data)); + if (data == null) return toHexString(encryptMD5(salt)); + // 拼接数据 + byte[] bytes = arraycopy(data, salt); + return toHexString(encryptMD5(bytes)); + } + + // = + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值 + */ + public static byte[] encryptMD5File(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return encryptMD5File(file); + } + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String encryptMD5FileToHexString(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return encryptMD5FileToHexString(file); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String encryptMD5FileToHexString(final File file) { + return toHexString(encryptMD5File(file)); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值 byte[] + */ + public static byte[] encryptMD5File(final File file) { + if (file == null) return null; + DigestInputStream dis = null; + try { + FileInputStream fis = new FileInputStream(file); + MessageDigest digest = MessageDigest.getInstance("MD5"); + dis = new DigestInputStream(fis, digest); + byte[] buffer = new byte[256 * 1024]; + while (true) { + if (!(dis.read(buffer) > 0)) break; + } + digest = dis.getMessageDigest(); + return digest.digest(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "encryptMD5File"); + return null; + } finally { + if (dis != null) { + try { + dis.close(); + } catch (Exception e) { + } + } + } + } + + // = + + /** + * SHA1 加密 + * @param data 待加密数据 + * @return SHA1 加密后的数据 + */ + public static byte[] encryptSHA1(final byte[] data) { + return hashTemplate(data, "SHA-1"); + } + + /** + * SHA1 加密 + * @param data 待加密数据 + * @return SHA1 加密后的数据转十六进制字符串 + */ + public static String encryptSHA1ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptSHA1ToHexString(data.getBytes()); + } + + /** + * SHA1 加密 + * @param data 待加密数据 + * @return SHA1 加密后的数据转十六进制字符串 + */ + public static String encryptSHA1ToHexString(final byte[] data) { + return toHexString(encryptSHA1(data)); + } + + // = + + /** + * SHA224 加密 + * @param data 待加密数据 + * @return SHA224 加密后的数据 + */ + public static byte[] encryptSHA224(final byte[] data) { + return hashTemplate(data, "SHA224"); + } + + /** + * SHA224 加密 + * @param data 待加密数据 + * @return SHA224 加密后的数据转十六进制字符串 + */ + public static String encryptSHA224ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptSHA224ToHexString(data.getBytes()); + } + + /** + * SHA224 加密 + * @param data 待加密数据 + * @return SHA224 加密后的数据转十六进制字符串 + */ + public static String encryptSHA224ToHexString(final byte[] data) { + return toHexString(encryptSHA224(data)); + } + + // = + + /** + * SHA256 加密 + * @param data 待加密数据 + * @return SHA256 加密后的数据 + */ + public static byte[] encryptSHA256(final byte[] data) { + return hashTemplate(data, "SHA-256"); + } + + /** + * SHA256 加密 + * @param data 待加密数据 + * @return SHA256 加密后的数据转十六进制字符串 + */ + public static String encryptSHA256ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptSHA256ToHexString(data.getBytes()); + } + + /** + * SHA256 加密 + * @param data 待加密数据 + * @return SHA256 加密后的数据转十六进制 + */ + public static String encryptSHA256ToHexString(final byte[] data) { + return toHexString(encryptSHA256(data)); + } + + // = + + /** + * SHA384 加密 + * @param data 待加密数据 + * @return SHA384 加密后的数据 + */ + public static byte[] encryptSHA384(final byte[] data) { + return hashTemplate(data, "SHA-384"); + } + + /** + * SHA384 加密 + * @param data 待加密数据 + * @return SHA384 加密后的数据转十六进制 + */ + public static String encryptSHA384ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptSHA384ToHexString(data.getBytes()); + } + + /** + * SHA384 加密 + * @param data 待加密数据 + * @return SHA384 加密后的数据转十六进制 + */ + public static String encryptSHA384ToHexString(final byte[] data) { + return toHexString(encryptSHA384(data)); + } + + // = + + /** + * SHA512 加密 + * @param data 待加密数据 + * @return SHA512 加密后的数据 + */ + public static byte[] encryptSHA512(final byte[] data) { + return hashTemplate(data, "SHA-512"); + } + + /** + * SHA512 加密 + * @param data 待加密数据 + * @return SHA512 加密后的数据转十六进制 + */ + public static String encryptSHA512ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptSHA512ToHexString(data.getBytes()); + } + + /** + * SHA512 加密 + * @param data 待加密数据 + * @return SHA512 加密后的数据转十六进制 + */ + public static String encryptSHA512ToHexString(final byte[] data) { + return toHexString(encryptSHA512(data)); + } + + /** + * Hash 加密模版方法 + * @param data 待加密数据 + * @param algorithm 算法 + * @return 指定加密算法加密后的数据 + */ + private static byte[] hashTemplate(final byte[] data, final String algorithm) { + if (data == null || data.length == 0) return null; + try { + MessageDigest digest = MessageDigest.getInstance(algorithm); + digest.update(data); + return digest.digest(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "hashTemplate"); + return null; + } + } + + // = + + /** + * HmacMD5 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacMD5 加密后的数据 + */ + public static byte[] encryptHmacMD5(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacMD5"); + } + + /** + * HmacMD5 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacMD5 加密后的数据转十六进制 + */ + public static String encryptHmacMD5ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacMD5ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacMD5 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacMD5 加密后的数据转十六进制 + */ + public static String encryptHmacMD5ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacMD5(data, key)); + } + + // = + + /** + * HmacSHA1 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA1 加密后的数据 + */ + public static byte[] encryptHmacSHA1(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacSHA1"); + } + + /** + * HmacSHA1 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA1 加密后的数据转十六进制 + */ + public static String encryptHmacSHA1ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacSHA1ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacSHA1 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA1 加密后的数据转十六进制 + */ + public static String encryptHmacSHA1ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacSHA1(data, key)); + } + + // = + + /** + * HmacSHA224 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA224 加密后的数据 + */ + public static byte[] encryptHmacSHA224(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacSHA224"); + } + + /** + * HmacSHA224 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA224 加密后的数据转十六进制 + */ + public static String encryptHmacSHA224ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacSHA224ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacSHA224 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA224 加密后的数据转十六进制 + */ + public static String encryptHmacSHA224ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacSHA224(data, key)); + } + + // = + + /** + * HmacSHA256 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA256 加密后的数据 + */ + public static byte[] encryptHmacSHA256(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacSHA256"); + } + + /** + * HmacSHA256 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA256 加密后的数据转十六进制 + */ + public static String encryptHmacSHA256ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacSHA256ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacSHA256 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA256 加密后的数据转十六进制 + */ + public static String encryptHmacSHA256ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacSHA256(data, key)); + } + + // = + + /** + * HmacSHA384 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA384 加密后的数据 + */ + public static byte[] encryptHmacSHA384(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacSHA384"); + } + + /** + * HmacSHA384 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA384 加密后的数据转十六进制 + */ + public static String encryptHmacSHA384ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacSHA384ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacSHA384 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA384 加密后的数据转十六进制 + */ + public static String encryptHmacSHA384ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacSHA384(data, key)); + } + + // = + + /** + * HmacSHA512 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA512 加密后的数据 + */ + public static byte[] encryptHmacSHA512(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacSHA512"); + } + + /** + * HmacSHA512 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA512 加密后的数据转十六进制 + */ + public static String encryptHmacSHA512ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacSHA512ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacSHA512 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA512 加密后的数据转十六进制 + */ + public static String encryptHmacSHA512ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacSHA512(data, key)); + } + + /** + * Hmac 加密模版方法 + * @param data 待加密数据 + * @param key 密钥 + * @param algorithm 算法 + * @return 指定加密算法和密钥, 加密后的数据 + */ + private static byte[] hmacTemplate(final byte[] data, final byte[] key, final String algorithm) { + if (data == null || data.length == 0 || key == null || key.length == 0) return null; + try { + SecretKeySpec secretKey = new SecretKeySpec(key, algorithm); + Mac mac = Mac.getInstance(algorithm); + mac.init(secretKey); + return mac.doFinal(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "hmacTemplate"); + return null; + } + } + + // = + + /** + * DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return DES 加密后的数据 + */ + public static byte[] encryptDES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "DES", transformation, iv, true); + } + + /** + * DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return DES 加密后的数据转 Base64 + */ + public static byte[] encryptDESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return base64Encode(encryptDES(data, key, transformation, iv)); + } + + /** + * DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return DES 加密后的数据转十六进制 + */ + public static String encryptDESToHexString(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return toHexString(encryptDES(data, key, transformation, iv)); + } + + // = + + /** + * DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return DES 解密后的数据 + */ + public static byte[] decryptDES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "DES", transformation, iv, false); + } + + /** + * DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return Base64 解码后, 在进行 DES 解密后的数据 + */ + public static byte[] decryptDESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return decryptDES(base64Decode(data), key, transformation, iv); + } + + /** + * DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 十六进制转换后, 在进行 DES 解密后的数据 + */ + public static byte[] decryptDESToHexString(final String data, final byte[] key, final String transformation, final byte[] iv) { + return decryptDES(decodeHex(data), key, transformation, iv); + } + + // = + + /** + * 3DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 3DES 加密后的数据 + */ + public static byte[] encrypt3DES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "DESede", transformation, iv, true); + } + + /** + * 3DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 3DES 加密后的数据转 Base64 + */ + public static byte[] encrypt3DESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return base64Encode(encrypt3DES(data, key, transformation, iv)); + } + + /** + * 3DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 3DES 加密后的数据转十六进制 + */ + public static String encrypt3DESToHexString(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return toHexString(encrypt3DES(data, key, transformation, iv)); + } + + // = + + /** + * 3DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 3DES 解密后的数据 + */ + public static byte[] decrypt3DES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "DESede", transformation, iv, false); + } + + /** + * 3DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return Base64 解码后, 在进行 3DES 解密后的数据 + */ + public static byte[] decrypt3DESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return decrypt3DES(base64Decode(data), key, transformation, iv); + } + + /** + * 3DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 十六进制转换后, 在进行 3DES 解密后的数据 + */ + public static byte[] decrypt3DESToHexString(final String data, final byte[] key, final String transformation, final byte[] iv) { + return decrypt3DES(decodeHex(data), key, transformation, iv); + } + + // = + + /** + * AES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return AES 加密后的数据 + */ + public static byte[] encryptAES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "AES", transformation, iv, true); + } + + /** + * AES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return AES 加密后的数据转 Base64 + */ + public static byte[] encryptAESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return base64Encode(encryptAES(data, key, transformation, iv)); + } + + /** + * AES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return AES 加密后的数据转十六进制 + */ + public static String encryptAESToHexString(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return toHexString(encryptAES(data, key, transformation, iv)); + } + + // = + + /** + * AES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return AES 解密后的数据 + */ + public static byte[] decryptAES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "AES", transformation, iv, false); + } + + /** + * AES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return Base64 解码后, 在进行 AES 解密后的数据 + */ + public static byte[] decryptAESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return decryptAES(base64Decode(data), key, transformation, iv); + } + + /** + * AES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 十六进制转换后, 在进行 AES 解密后的数据 + */ + public static byte[] decryptAESToHexString(final String data, final byte[] key, final String transformation, final byte[] iv) { + return decryptAES(decodeHex(data), key, transformation, iv); + } + + /** + * 对称加密模版方法 + * @param data 待加解密数据 + * @param key 密钥 + * @param algorithm 算法 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @param isEncrypt 是否加密处理 + * @return 指定加密算法, 加解密后的数据 + */ + private static byte[] symmetricTemplate(final byte[] data, final byte[] key, final String algorithm, + final String transformation, final byte[] iv, final boolean isEncrypt) { + if (data == null || data.length == 0 || key == null || key.length == 0) return null; + try { + SecretKey secretKey; + if ("DES".equals(algorithm)) { + DESKeySpec desKey = new DESKeySpec(key); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); + secretKey = keyFactory.generateSecret(desKey); + } else { + secretKey = new SecretKeySpec(key, algorithm); + } + Cipher cipher = Cipher.getInstance(transformation); + if (iv == null || iv.length == 0) { + cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKey); + } else { + AlgorithmParameterSpec params = new IvParameterSpec(iv); + cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKey, params); + } + return cipher.doFinal(data); + } catch (Throwable e) { + JCLogUtils.eTag(TAG, e, "symmetricTemplate"); + return null; + } + } + + // = + + /** + * RSA 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return RSA 加密后的数据 + */ + public static byte[] encryptRSA(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return rsaTemplate(data, key, isPublicKey, transformation, true); + } + + /** + * RSA 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return RSA 加密后的数据转 Base64 + */ + public static byte[] encryptRSAToBase64(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return base64Encode(encryptRSA(data, key, isPublicKey, transformation)); + } + + /** + * RSA 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return RSA 加密后的数据转十六进制 + */ + public static String encryptRSAToHexString(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return toHexString(encryptRSA(data, key, isPublicKey, transformation)); + } + + // = + + /** + * RSA 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return RSA 解密后的数据 + */ + public static byte[] decryptRSA(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return rsaTemplate(data, key, isPublicKey, transformation, false); + } + + /** + * RSA 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return Base64 解码后, 在进行 RSA 解密后的数据 + */ + public static byte[] decryptRSAToBase64(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return decryptRSA(base64Decode(data), key, isPublicKey, transformation); + } + + /** + * RSA 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return 十六进制转换后, 在进行 RSA 解密后的数据 + */ + public static byte[] decryptRSAToHexString(final String data, final byte[] key, final boolean isPublicKey, final String transformation) { + return decryptRSA(decodeHex(data), key, isPublicKey, transformation); + } + + /** + * RSA 加解密模版方法 + * @param data 待加解密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @param isEncrypt 是否加密处理 + * @return 指定加密算法, 加解密后的数据 + */ + private static byte[] rsaTemplate(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation, final boolean isEncrypt) { + if (data == null || key == null) return null; + try { + int dataLength = data.length; + int keyLength = key.length; + if (dataLength == 0 || keyLength == 0) return null; + + Key rsaKey; + if (isPublicKey) { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key); + rsaKey = KeyFactory.getInstance("RSA").generatePublic(keySpec); + } else { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key); + rsaKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec); + } + if (rsaKey == null) return null; + Cipher cipher = Cipher.getInstance(transformation); + cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, rsaKey); + int maxLen = isEncrypt ? 117 : 128; + int count = dataLength / maxLen; + if (count > 0) { + byte[] ret = new byte[0]; + byte[] buffer = new byte[maxLen]; + int index = 0; + for (int i = 0; i < count; i++) { + System.arraycopy(data, index, buffer, 0, maxLen); + ret = arraycopy(ret, cipher.doFinal(buffer)); + index += maxLen; + } + if (index != dataLength) { + int restLen = dataLength - index; + buffer = new byte[restLen]; + System.arraycopy(data, index, buffer, 0, restLen); + ret = arraycopy(ret, cipher.doFinal(buffer)); + } + return ret; + } else { + return cipher.doFinal(data); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "rsaTemplate"); + } + return null; + } + + // ============ + // = 私有方法 = + // ============ + + /** + * Base64 编码 + * @param input 待编码数据 + * @return Base64 编码后的 byte[] + */ + private static byte[] base64Encode(final byte[] input) { + if (input == null) return null; + return Base64.encode(input, Base64.NO_WRAP); + } + + /** + * Base64 解码 + * @param input 待解码数据 + * @return Base64 解码后的 byte[] + */ + private static byte[] base64Decode(final byte[] input) { + if (input == null) return null; + return Base64.decode(input, Base64.NO_WRAP); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制 String + */ + private static String toHexString(final byte[] data) { + return toHexString(data, HEX_DIGITS); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + /** + * 将十六进制字符串解码 + * @param hex 十六进制字符串 + * @return 十六进制转 ( 解 ) 码后的数据 + */ + private static byte[] decodeHex(final String hex) { + return decodeHex(isEmpty(hex) ? null : hex.toCharArray()); + } + + /** + * 将十六进制字符数组解码 + * @param data 待加密数据 十六进制 char[] + * @return 十六进制转 ( 解 ) 码后的数据 + */ + private static byte[] decodeHex(final char[] data) { + if (data == null) return null; + try { + int len = data.length; + byte[] out = new byte[len >> 1]; + // 十六进制由两个字符组成 + for (int i = 0, j = 0; j < len; i++) { + int d = toDigit(data[j], j) << 4; + j++; + d = d | toDigit(data[j], j); + j++; + out[i] = (byte) (d & 0xFF); + } + return out; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "decodeHex"); + } + return null; + } + + /** + * 将十六进制字符转换成一个整数 + * @param ch 十六进制 char + * @param index 十六进制字符在字符数组中的位置 + * @return 十六进制 转 int 整数 + * @throws Exception 当 ch 不是一个合法的十六进制字符时, 抛出运行时异常 + */ + private static int toDigit(final char ch, final int index) throws Exception { + int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new Exception(String.format("Illegal hexadecimal character %s at index %s", ch, index)); + } + return digit; + } + + // ============== + // = ArrayUtils = + // ============== + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + private static byte[] arraycopy(final byte[] prefix, final byte[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + byte[] arrays = new byte[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/encrypt/EscapeUtils.java b/lib/DevApp/src/main/java/dev/utils/common/encrypt/EscapeUtils.java new file mode 100644 index 0000000000..c5a60ac72c --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/encrypt/EscapeUtils.java @@ -0,0 +1,141 @@ +package dev.utils.common.encrypt; + +/** + * detail: 字符串 ( 编解码 ) 工具类 + * @author Ttt + */ +public final class EscapeUtils { + + private EscapeUtils() { + } + + /** + * 编码 + * @param data 待编码数据 + * @return 编码后的字符串 + */ + public static String escape(final String data) { + if (data == null) return null; + StringBuilder builder = new StringBuilder(); + for (int i = 0, len = data.length(); i < len; i++) { + int ch = data.charAt(i); + if ('A' <= ch && ch <= 'Z') { + builder.append((char) ch); + } else if ('a' <= ch && ch <= 'z') { + builder.append((char) ch); + } else if ('0' <= ch && ch <= '9') { + builder.append((char) ch); + } else if (ch == '-' || ch == '_' || ch == '.' || ch == '!' + || ch == '~' || ch == '*' || ch == '\'' || ch == '(' + || ch == ')') { + builder.append((char) ch); + } else if (ch <= 0x007F) { + builder.append('%'); + builder.append(HEX[ch]); + } else { + builder.append('%'); + builder.append('u'); + builder.append(HEX[(ch >>> 8)]); + builder.append(HEX[(0x00FF & ch)]); + } + } + return builder.toString(); + } + + /** + * 解码 - 本方法不论参数 data 是否经过 escape() 编码, 均能获取正确的 ( 解码 ) 结果 + * @param data 待解码数据 + * @return 解码后的字符串 + */ + public static String unescape(final String data) { + if (data == null) return null; + StringBuilder builder = new StringBuilder(); + int i = 0; + int len = data.length(); + while (i < len) { + int ch = data.charAt(i); + if ('A' <= ch && ch <= 'Z') { + builder.append((char) ch); + } else if ('a' <= ch && ch <= 'z') { + builder.append((char) ch); + } else if ('0' <= ch && ch <= '9') { + builder.append((char) ch); + } else if (ch == '-' || ch == '_' || ch == '.' || ch == '!' + || ch == '~' || ch == '*' || ch == '\'' || ch == '(' + || ch == ')') { + builder.append((char) ch); + } else if (ch == '%') { + int cint = 0; + if ('u' != data.charAt(i + 1)) { + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 1)]; + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 2)]; + i += 2; + } else { + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 2)]; + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 3)]; + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 4)]; + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 5)]; + i += 5; + } + builder.append((char) cint); + } else { + builder.append((char) ch); + } + i++; + } + return builder.toString(); + } + + // = + + // 十六进制 0-255 + private static final String[] HEX = {"00", "01", "02", "03", "04", "05", + "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", "10", + "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", + "1C", "1D", "1E", "1F", "20", "21", "22", "23", "24", "25", "26", + "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", "30", "31", + "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", + "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47", + "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", + "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", + "5E", "5F", "60", "61", "62", "63", "64", "65", "66", "67", "68", + "69", "6A", "6B", "6C", "6D", "6E", "6F", "70", "71", "72", "73", + "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", + "7F", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", + "8A", "8B", "8C", "8D", "8E", "8F", "90", "91", "92", "93", "94", + "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F", + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", + "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3", "B4", "B5", + "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF", "C0", + "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", + "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", + "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", + "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", + "ED", "EE", "EF", "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", + "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"}; + + private static final byte[] BYTE_VALUES = {0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F}; +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/encrypt/MD5Utils.java b/lib/DevApp/src/main/java/dev/utils/common/encrypt/MD5Utils.java new file mode 100644 index 0000000000..6177ca3055 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/encrypt/MD5Utils.java @@ -0,0 +1,219 @@ +package dev.utils.common.encrypt; + +import java.io.File; +import java.io.FileInputStream; +import java.security.DigestInputStream; +import java.security.MessageDigest; + +import dev.utils.JCLogUtils; + +/** + * detail: MD5 加密工具类 + * @author Ttt + *
+ *     Message Digest 消息摘要算法
+ * 
+ */ +public final class MD5Utils { + + private MD5Utils() { + } + + // 日志 TAG + private static final String TAG = MD5Utils.class.getSimpleName(); + + /** + * 加密内容 (32 位小写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + public static String md5(final String data) { + if (data == null) return null; + try { + return md5(data.getBytes()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5"); + } + return null; + } + + /** + * 加密内容 (32 位小写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + public static String md5(final byte[] data) { + if (data == null) return null; + try { + // 获取 MD5 摘要算法的 MessageDigest 对象 + MessageDigest digest = MessageDigest.getInstance("MD5"); + // 使用指定的字节更新摘要 + digest.update(data); + // 获取密文 + return toHexString(digest.digest(), HEX_DIGITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5"); + } + return null; + } + + /** + * 加密内容 (32 位大写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + public static String md5Upper(final String data) { + if (data == null) return null; + try { + return md5Upper(data.getBytes()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5Upper"); + } + return null; + } + + /** + * 加密内容 (32 位大写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + public static String md5Upper(final byte[] data) { + if (data == null) return null; + try { + // 获取 MD5 摘要算法的 MessageDigest 对象 + MessageDigest digest = MessageDigest.getInstance("MD5"); + // 使用指定的字节更新摘要 + digest.update(data); + // 获取密文 + return toHexString(digest.digest(), HEX_DIGITS_UPPER); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5Upper"); + } + return null; + } + + // = + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值 + */ + public static byte[] getFileMD5(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileMD5(file); + } + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String getFileMD5ToHexString(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileMD5ToHexString(file); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String getFileMD5ToHexString(final File file) { + return toHexString(getFileMD5(file)); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值 byte[] + */ + public static byte[] getFileMD5(final File file) { + if (file == null) return null; + DigestInputStream dis = null; + try { + FileInputStream fis = new FileInputStream(file); + MessageDigest digest = MessageDigest.getInstance("MD5"); + dis = new DigestInputStream(fis, digest); + byte[] buffer = new byte[256 * 1024]; + while (true) { + if (!(dis.read(buffer) > 0)) break; + } + digest = dis.getMessageDigest(); + return digest.digest(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileMD5"); + return null; + } finally { + if (dis != null) { + try { + dis.close(); + } catch (Exception e) { + } + } + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + // 用于建立十六进制字符的输出的大写字符数组 + private static final char[] HEX_DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制 String + */ + private static String toHexString(final byte[] data) { + return toHexString(data, HEX_DIGITS); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS}、{@link #HEX_DIGITS_UPPER} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/encrypt/SHAUtils.java b/lib/DevApp/src/main/java/dev/utils/common/encrypt/SHAUtils.java new file mode 100644 index 0000000000..bf96349d5c --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/encrypt/SHAUtils.java @@ -0,0 +1,217 @@ +package dev.utils.common.encrypt; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.MessageDigest; + +import dev.utils.JCLogUtils; + +/** + * detail: SHA 加密工具类 + * @author Ttt + */ +public final class SHAUtils { + + private SHAUtils() { + } + + // 日志 TAG + private static final String TAG = SHAUtils.class.getSimpleName(); + + /** + * 加密内容 SHA1 + * @param data 待加密数据 + * @return SHA1 加密后的字符串 + */ + public static String sha1(final String data) { + return shaHex(data, "SHA-1"); + } + + /** + * 加密内容 SHA224 + * @param data 待加密数据 + * @return SHA224 加密后的字符串 + */ + public static String sha224(final String data) { + return shaHex(data, "SHA-224"); + } + + /** + * 加密内容 SHA256 + * @param data 待加密数据 + * @return SHA256 加密后的字符串 + */ + public static String sha256(final String data) { + return shaHex(data, "SHA-256"); + } + + /** + * 加密内容 SHA384 + * @param data 待加密数据 + * @return SHA384 加密后的字符串 + */ + public static String sha384(final String data) { + return shaHex(data, "SHA-384"); + } + + /** + * 加密内容 SHA512 + * @param data 待加密数据 + * @return SHA512 加密后的字符串 + */ + public static String sha512(final String data) { + return shaHex(data, "SHA-512"); + } + + // = + + /** + * 获取文件 SHA1 值 + * @param filePath 文件路径 + * @return 文件 SHA1 字符串信息 + */ + public static String getFileSHA1(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileSHA(file, "SHA-1"); + } + + /** + * 获取文件 SHA1 值 + * @param file 文件 + * @return 文件 SHA1 字符串信息 + */ + public static String getFileSHA1(final File file) { + return getFileSHA(file, "SHA-1"); + } + + /** + * 获取文件 SHA256 值 + * @param filePath 文件路径 + * @return 文件 SHA256 字符串信息 + */ + public static String getFileSHA256(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileSHA(file, "SHA-256"); + } + + /** + * 获取文件 SHA256 值 + * @param file 文件 + * @return 文件 SHA256 字符串信息 + */ + public static String getFileSHA256(final File file) { + return getFileSHA(file, "SHA-256"); + } + + // = + + /** + * 加密内容 SHA 模板 + * @param data 待加密数据 + * @param algorithm 算法 + * @return SHA 算法加密后的字符串 + */ + public static String shaHex(final String data, final String algorithm) { + if (data == null || algorithm == null) return null; + try { + byte[] bytes = data.getBytes(); + // 获取 SHA-1 摘要算法的 MessageDigest 对象 + MessageDigest digest = MessageDigest.getInstance(algorithm); + // 使用指定的字节更新摘要 + digest.update(bytes); + // 获取密文 + return toHexString(digest.digest(), HEX_DIGITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "shaHex"); + } + return null; + } + + /** + * 获取文件 SHA 值 + * @param file 文件 + * @param algorithm 算法 + * @return 文件指定 SHA 字符串信息 + */ + public static String getFileSHA(final File file, final String algorithm) { + if (file == null || algorithm == null) return null; + InputStream is = null; + try { + is = new FileInputStream(file); + byte[] buffer = new byte[1024]; + MessageDigest digest = MessageDigest.getInstance(algorithm); + int numRead = 0; + while ((numRead = is.read(buffer)) > 0) { + digest.update(buffer, 0, numRead); + } + return toHexString(digest.digest(), HEX_DIGITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileSHA"); + } finally { + if (is != null) { + try { + is.close(); + } catch (Exception e) { + } + } + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java b/lib/DevApp/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java similarity index 59% rename from DevLibUtils/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java rename to lib/DevApp/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java index faf4188ef8..6e0ddf1ec0 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java +++ b/lib/DevApp/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java @@ -8,28 +8,31 @@ import dev.utils.JCLogUtils; /** - * detail: 3DES对称加密(Triple DES、DESede,进行了三重DES加密的算法,对称加密算法) - * Created by Ttt + * detail: 3DES 对称加密工具类 + * @author Ttt + *
+ *     Triple DES、DESede 进行了三重 DES 加密的算法 ( 对称加密算法 )
+ * 
*/ public final class TripleDESUtils { private TripleDESUtils() { } - // 日志TAG + // 日志 TAG private static final String TAG = TripleDESUtils.class.getSimpleName(); /** * 生成密钥 - * @return + * @return 密钥 byte[] */ - public static byte[] initKey(){ + public static byte[] initKey() { try { KeyGenerator keyGen = KeyGenerator.getInstance("DESede"); - keyGen.init(168); // 112 168 + keyGen.init(168); // 112 168 SecretKey secretKey = keyGen.generateKey(); return secretKey.getEncoded(); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "initKey"); } return null; @@ -37,18 +40,18 @@ public static byte[] initKey(){ /** * 3DES 加密 - * @param data - * @param key - * @return + * @param data 待加密数据 + * @param key 密钥 + * @return 加密后的 byte[] */ - public static byte[] encrypt(byte[] data, byte[] key){ + public static byte[] encrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; try { SecretKey secretKey = new SecretKeySpec(key, "DESede"); Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); - byte[] cipherBytes = cipher.doFinal(data); - return cipherBytes; - } catch (Exception e){ + return cipher.doFinal(data); + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "encrypt"); } return null; @@ -56,21 +59,20 @@ public static byte[] encrypt(byte[] data, byte[] key){ /** * 3DES 解密 - * @param data - * @param key - * @return + * @param data 待加密数据 + * @param key 密钥 + * @return 解密后的 byte[] */ - public static byte[] decrypt(byte[] data, byte[] key){ + public static byte[] decrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; try { SecretKey secretKey = new SecretKeySpec(key, "DESede"); Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKey); - byte[] plainBytes = cipher.doFinal(data); - return plainBytes; - } catch (Exception e){ + return cipher.doFinal(data); + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "decrypt"); } return null; } - -} +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/encrypt/XorUtils.java b/lib/DevApp/src/main/java/dev/utils/common/encrypt/XorUtils.java new file mode 100644 index 0000000000..b3de44a8cb --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/encrypt/XorUtils.java @@ -0,0 +1,77 @@ +package dev.utils.common.encrypt; + +/** + * detail: 异或 ( 加密 ) 工具类 + * @author Ttt + *
+ *     位运算可以实现很多高级、高效的运算
+ *     可用于 IM 二进制数据包加密
+ *     1. 能够实现加密
+ *     2. 采用异或加密算法不会改变二进制数据的长度这对二进制数据包封包起到不小的好处
+ *     也可用于记事本等场景
+ *     

+ * 参考链接 + * @see + *
+ */ +public final class XorUtils { + + private XorUtils() { + } + + /** + * 加解密 ( 固定 Key 方式 ) 这种方式 加解密 方法共用 + *
+     *     加密: byte[] bytes = encryptAsFix("123".getBytes());
+     *     解密: String str = new String(encryptAsFix(bytes));
+     * 
+ * @param data 待加解密数据 + * @return 加解密后的数据 byte[] + */ + public static byte[] encryptAsFix(final byte[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + int key = 0x12; + for (int i = 0; i < len; i++) { + data[i] ^= key; + } + return data; + } + + // = + + /** + * 加密 ( 非固定 Key 方式 ) + * @param data 待加密数据 + * @return 加密后的数据 byte[] + */ + public static byte[] encrypt(final byte[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + int key = 0x12; + for (int i = 0; i < len; i++) { + data[i] = (byte) (data[i] ^ key); + key = data[i]; + } + return data; + } + + /** + * 解密 ( 非固定 Key 方式 ) + * @param data 待解密数据 + * @return 解密后的数据 byte[] + */ + public static byte[] decrypt(final byte[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + int key = 0x12; + for (int i = len - 1; i > 0; i--) { + data[i] = (byte) (data[i] ^ data[i - 1]); + } + data[0] = (byte) (data[0] ^ key); + return data; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/random/AliasMethod.java b/lib/DevApp/src/main/java/dev/utils/common/random/AliasMethod.java new file mode 100644 index 0000000000..97af6d609f --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/random/AliasMethod.java @@ -0,0 +1,168 @@ +package dev.utils.common.random; + +/** + * File: AliasMethod.java + * Author: Keith Schwarz (htiek@cs.stanford.edu) + * An implementation of the alias method implemented using Vose's algorithm. + * The alias method allows for efficient sampling of random values from a + * discrete probability distribution (i.e. rolling a loaded die) in O(1) time + * each after O(n) preprocessing time. + * For a complete writeup on the alias method, including the intuition and + * important proofs, please see the article "Darts, Dice, and Coins: Smpling + * from a Discrete Distribution" at + * http://www.keithschwarz.com/darts-dice-coins/ + */ + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.Random; + +/** + * detail: 随机概率采样算法 + * @author Keith Schwarz + *
+ *     @see 
+ *     @see 
+ * 
+ */ +public final class AliasMethod { + + /* The random number generator used to sample from the distribution. */ + private final Random random; + + /* The probability and alias tables. */ + private final int[] alias; + private final double[] probability; + + /** + * Constructs a new AliasMethod to sample from a discrete distribution and + * hand back outcomes based on the probability distribution. + *

+ * Given as input a list of probabilities corresponding to outcomes 0, 1, + * ..., n - 1, this constructor creates the probability and alias tables + * needed to efficiently sample from this distribution. + * @param probabilities The list of probabilities. + */ + public AliasMethod(List probabilities) { + this(probabilities, new Random()); + } + + /** + * Constructs a new AliasMethod to sample from a discrete distribution and + * hand back outcomes based on the probability distribution. + *

+ * Given as input a list of probabilities corresponding to outcomes 0, 1, + * ..., n - 1, along with the random number generator that should be used + * as the underlying generator, this constructor creates the probability + * and alias tables needed to efficiently sample from this distribution. + * @param probabilities The list of probabilities. + * @param random The random number generator + */ + public AliasMethod(List probabilities, Random random) { + /* Begin by doing basic structural checks on the inputs. */ + if (probabilities == null || random == null) + throw new NullPointerException(); + if (probabilities.size() == 0) + throw new IllegalArgumentException("Probability vector must be nonempty."); + + /* Allocate space for the probability and alias tables. */ + probability = new double[probabilities.size()]; + alias = new int[probabilities.size()]; + + /* Store the underlying generator. */ + this.random = random; + + /* Compute the average probability and cache it for later use. */ + final double average = 1.0 / probabilities.size(); + + /* Make a copy of the probabilities list, since we will be making + * changes to it. + */ + probabilities = new ArrayList(probabilities); + + /* Create two stacks to act as worklists as we populate the tables. */ + Deque small = new ArrayDeque<>(); + Deque large = new ArrayDeque<>(); + + /* Populate the stacks with the input probabilities. */ + for (int i = 0; i < probabilities.size(); ++i) { + /* If the probability is below the average probability, then we add + * it to the small list; otherwise we add it to the large list. + */ + if (probabilities.get(i) >= average) + large.add(i); + else + small.add(i); + } + + /* As a note: in the mathematical specification of the algorithm, we + * will always exhaust the small list before the big list. However, + * due to floating point inaccuracies, this is not necessarily true. + * Consequently, this inner loop (which tries to pair small and large + * elements) will have to check that both lists aren't empty. + */ + while (!small.isEmpty() && !large.isEmpty()) { + /* Get the index of the small and the large probabilities. */ + int less = small.removeLast(); + int more = large.removeLast(); + + /* These probabilities have not yet been scaled up to be such that + * 1/n is given weight 1.0. We do this here instead. + */ + probability[less] = probabilities.get(less) * probabilities.size(); + alias[less] = more; + + /* Decrease the probability of the larger one by the appropriate + * amount. + */ + probabilities.set(more, + (probabilities.get(more) + probabilities.get(less)) - average); + + /* If the new probability is less than the average, add it into the + * small list; otherwise add it to the large list. + */ + if (probabilities.get(more) >= 1.0 / probabilities.size()) + large.add(more); + else + small.add(more); + } + + /* At this point, everything is in one list, which means that the + * remaining probabilities should all be 1/n. Based on this, set them + * appropriately. Due to numerical issues, we can't be sure which + * stack will hold the entries, so we empty both. + */ + while (!small.isEmpty()) + probability[small.removeLast()] = 1.0; + while (!large.isEmpty()) + probability[large.removeLast()] = 1.0; + } + + /** + * 获取随机索引 ( 对应几率索引 ) + * Samples a value from the underlying distribution. + * @return A random value sampled from the underlying distribution. + */ + public int next() { + /* Generate a fair die roll to determine which column to inspect. */ + int column = random.nextInt(probability.length); + + /* Generate a biased coin toss to determine which option to pick. */ + boolean coinToss = random.nextDouble() < probability[column]; + + /* Based on the outcome, return either the column or its alias. */ + return coinToss ? column : alias[column]; + } + +// public static void main(String[] args) { +// // 使用方法 +// List lists = new ArrayList<>(); +// lists.add(0.15); // 0.15 几率 +// lists.add(0.85); // 0.85 几率 +// AliasMethod aliasMethod = new AliasMethod(lists); +// int result = aliasMethod.next(); +// // result 0 = 0.15, 1 = 0.85 +// } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadManager.java b/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadManager.java new file mode 100644 index 0000000000..7ad104f776 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadManager.java @@ -0,0 +1,102 @@ +package dev.utils.common.thread; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * detail: 线程池管理工具类 + * @author Ttt + */ +public final class DevThreadManager { + + private DevThreadManager() { + } + + // 默认通用线程池 ( 通过 CPU 自动处理 ) + private static final DevThreadPool sDevThreadPool = new DevThreadPool(DevThreadPool.DevThreadPoolType.CALC_CPU); + // 线程池数据 + private static final LinkedHashMap sThreadMaps = new LinkedHashMap<>(); + // 配置数据 + private static final Map sConfigMaps = new HashMap<>(); + + /** + * 获取 DevThreadManager 实例 + * @param threadNumber 线程数量 + * @return {@link DevThreadPool} + */ + public static synchronized DevThreadPool getInstance(final int threadNumber) { + // 初始化 key + String key = "n_" + threadNumber; + // 如果不为 null, 则直接返回 + DevThreadPool devThreadPool = sThreadMaps.get(key); + if (devThreadPool != null) { + return devThreadPool; + } + devThreadPool = new DevThreadPool(threadNumber); + sThreadMaps.put(key, devThreadPool); + return devThreadPool; + } + + /** + * 获取 DevThreadManager 实例 + * @param key 线程配置 key {@link DevThreadPool.DevThreadPoolType} or int-Integer + * @return {@link DevThreadPool} + */ + public static synchronized DevThreadPool getInstance(final String key) { + // 如果不为 null, 则直接返回 + DevThreadPool devThreadPool = sThreadMaps.get(key); + if (devThreadPool != null) { + return devThreadPool; + } + Object object = sConfigMaps.get(key); + if (object != null) { + try { + // 判断是否属于线程池类型 + if (object instanceof DevThreadPool.DevThreadPoolType) { + devThreadPool = new DevThreadPool((DevThreadPool.DevThreadPoolType) object); + } else if (object instanceof Integer) { + devThreadPool = new DevThreadPool((Integer) object); + } else { // 其他类型, 统一转换 Integer + devThreadPool = new DevThreadPool(Integer.parseInt((String) object)); + } + if (devThreadPool != null) { + sThreadMaps.put(key, devThreadPool); + return devThreadPool; + } + } catch (Exception e) { + return sDevThreadPool; + } + } + return sDevThreadPool; + } + + // = + + /** + * 初始化配置信息 + * @param mapConfigs 线程配置信息 Map + */ + public static void initConfig(final Map mapConfigs) { + if (mapConfigs != null) { + sConfigMaps.putAll(mapConfigs); + } + } + + /** + * 添加配置信息 + * @param key 线程配置 key + * @param value 线程配置 value + */ + public static void putConfig(final String key, final Object value) { + sConfigMaps.put(key, value); + } + + /** + * 移除配置信息 + * @param key 线程配置 key + */ + public static void removeConfig(final String key) { + sConfigMaps.remove(key); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadPool.java b/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadPool.java new file mode 100644 index 0000000000..2a5e68812f --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadPool.java @@ -0,0 +1,456 @@ +package dev.utils.common.thread; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * detail: 线程池 ( 构建类 ) + * @author Ttt + *

+ *     @see 
+ *     

+ * 创建线程池 ( 参数 ) + * 1. 线程池里面管理多少个线程 + * 2. 如果排队满了, 额外的开的线程数 + * 3. 如果线程池没有要执行的任务存活多久 + * 4. 时间的单位 + * 5. 如果 线程池里管理的线程都已经用了, 剩下的任务临时存到 LinkedBlockingQueue 对象中排队 + * public ThreadPoolExecutor(int corePoolSize, + * int maximumPoolSize, + * long keepAliveTime, + * TimeUnit unit, + * BlockingQueue workQueue) { + * this (corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + * Executors.defaultThreadFactory(), defaultHandler); + * } + *
+ */ +public final class DevThreadPool { + + // 线程池对象 + private final ExecutorService mThreadPool; + // 定时任务线程池 + private ScheduledExecutorService mScheduleExec; + + /** + * 构造函数 + * @param threadNumber 线程数量 + */ + public DevThreadPool(int threadNumber) { + // 如果小于等于 0, 则默认使用 1 + if (threadNumber <= 0) { + threadNumber = 1; + } + this.mThreadPool = Executors.newFixedThreadPool(threadNumber); + // 初始化定时器任务 + this.mScheduleExec = Executors.newScheduledThreadPool(threadNumber); + } + + /** + * 构造函数 + * @param threadPool {@link ExecutorService} + */ + public DevThreadPool(final ExecutorService threadPool) { + this.mThreadPool = threadPool; + // 初始化定时器任务 + this.mScheduleExec = Executors.newScheduledThreadPool(getThreads()); + } + + /** + * 构造函数 + * @param devThreadPoolType 线程初始化类型 {@link DevThreadPoolType} + */ + public DevThreadPool(final DevThreadPoolType devThreadPoolType) { + // 初始化定时器任务 + this.mScheduleExec = Executors.newScheduledThreadPool(getThreads()); + // = + if (devThreadPoolType != null) { + switch (devThreadPoolType) { + case SINGLE: + mThreadPool = Executors.newSingleThreadExecutor(); + // 初始化定时器任务 + this.mScheduleExec = Executors.newScheduledThreadPool(1); + break; +// case AUTO_CPU: +// mThreadPool = Executors.newWorkStealingPool(); +// break; + case CALC_CPU: + mThreadPool = Executors.newFixedThreadPool(getThreads()); + break; + case CACHE: + mThreadPool = Executors.newCachedThreadPool(); + break; + default: + mThreadPool = Executors.newFixedThreadPool(getThreads()); + break; + } + } else { + mThreadPool = Executors.newFixedThreadPool(getThreads()); + } + } + + /** + * detail: 线程池初始化枚举类型 + * @author Ttt + *
+     *     @see 
+     *     @see 
+     * 
+ */ + public enum DevThreadPoolType { + + // 如果当前线程意外终止, 会创建一个新线程继续执行任务, 这和我们直接创建线程不同, 也和 newFixedThreadPool(1) 不同 + SINGLE, // newSingleThreadExecutor 获取的是一个单个的线程, 这个线程会保证你的任务执行完成 + + AUTO_CPU, // 根据 CPU 来创建 ( 自定义创建 ) + + CALC_CPU, // 手动计算 CPU 来创建 + + CACHE, // 可缓存线程池 + +// 1 newCachedThreadPool: 创建一个可缓存线程池, 如果线程池长度超过处理需要, 可灵活回收空闲线程, 若无可回收, 则新建线程 +// 2 newFixedThreadPool: 创建一个固定数目的、可重用的线程池 +// 3 newScheduledThreadPool: 创建一个定长线程池, 支持定时及周期性任务执行 +// 4 newSingleThreadExecutor: 创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序 (FIFO、LIFO) 优先级执行 +// 5 newSingleThreadScheduledExcutor: 创建一个单例线程池, 定期或延时执行任务 +// 6 newWorkStealingPool: 创建持有足够线程的线程池来支持给定的并行级别, 并通过使用多个队列, 减少竞争 +// 它需要穿一个并行级别的参数, 如果不传, 则被设定为默认的 CPU 数量 +// 7 ForkJoinPool: 支持大任务分解成小任务的线程池, 这是 Java8 新增线程池, 通常配合 ForkJoinTask 接口的子类 RecursiveAction 或 RecursiveTask 使用 + } + + // = + + /** + * 获取线程数 + * @return {@link DevThreadPool#getCalcThreads()} + */ + public final int getThreads() { + return getCalcThreads(); + } + + /** + * 获取线程数 + * @return 自动计算 CPU 核心数 + */ + public final int getCalcThreads() { + // 获取 CPU 核心数 + int cpuNumber = Runtime.getRuntime().availableProcessors(); + // 如果小于等于 5, 则返回 5 + if (cpuNumber <= 5) { + return 5; + } else { // 大于 5 的情况 + if (cpuNumber * 2 + 1 >= 10) { // 防止线程数量过大, 当大于 10 的时候, 返回 10 + return 10; + } else { // 不大于 10 的时候, 默认返回 支持的数量 * 2 + 1 + return cpuNumber * 2 + 1; + } + } + } + + // = + + /** + * 加入到线程池任务队列 + * @param runnable 线程 + */ + public void execute(final Runnable runnable) { + if (mThreadPool != null && runnable != null) { + mThreadPool.execute(runnable); + } + } + + /** + * 加入到线程池任务队列 + * @param runnables 线程集合 + */ + public void execute(final List runnables) { + if (mThreadPool != null && runnables != null) { + for (Runnable command : runnables) { + if (command != null) { + mThreadPool.execute(command); + } + } + } + } + + /** + * 通过反射, 调用某个类的方法 + * @param method 方法 + * @param object 对象 + */ + public void execute(final Method method, final Object object) { + if (mThreadPool != null && method != null && object != null) { + mThreadPool.execute(new Runnable() { + @Override + public void run() { + try { + method.invoke(object); + } catch (Exception ignore) { + } + } + }); + } + } + + // = + + /** + * shutdown 会等待所有提交的任务执行完成, 不管是正在执行还是保存在任务队列中的已提交任务 + * 待以前提交的任务执行完毕后关闭线程池 + * 启动一次顺序关闭, 执行以前提交的任务, 但不接受新任务 + * 如果已经关闭, 则调用没有作用 + */ + public void shutdown() { + if (mThreadPool != null) { + mThreadPool.shutdown(); + } + } + + /** + * shutdownNow 会尝试中断正在执行的任务 ( 其主要是中断一些指定方法如 sleep 方法 ), 并且停止执行等待队列中提交的任务 + * 试图停止所有正在执行的活动任务, 暂停处理正在等待的任务, 并返回等待执行的任务列表 + * 无法保证能够停止正在处理的活动执行任务, 但是会尽力尝试 + * @return {@link List} + */ + public List shutdownNow() { + if (mThreadPool != null) { + return mThreadPool.shutdownNow(); + } + return null; + } + + /** + * 判断线程池是否已关闭 - isShutDown 当调用 shutdown() 方法后返回为 true + * @return {@code true} yes, {@code false} no + */ + public boolean isShutdown() { + if (mThreadPool != null) { + return mThreadPool.isShutdown(); + } + return false; + } + + /** + * 若关闭后所有任务都已完成, 则返回 true + * 注意除非首先调用 shutdown 或 shutdownNow, 否则 isTerminated 永不为 true + * isTerminated 当调用 shutdown() 方法后, 并且所有提交的任务完成后返回为 true + * @return {@code true} yes, {@code false} no + */ + public boolean isTerminated() { + if (mThreadPool != null) { + return mThreadPool.isTerminated(); + } + return false; + } + + /** + * 请求关闭、发生超时或者当前线程中断 + * 无论哪一个首先发生之后, 都将导致阻塞, 直到所有任务完成执行 + * @param timeout 最长等待时间 + * @param unit 时间单位 + * @return {@code true} 请求成功, {@code false} 请求超时 + * @throws InterruptedException 终端异常 + */ + public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException { + if (mThreadPool != null && unit != null) { + return mThreadPool.awaitTermination(timeout, unit); + } + return false; + } + + /** + * 提交一个 Callable 任务用于执行 + * 如果想立即阻塞任务的等待, 则可以使用 {@code result = threadPool.submit(aCallable).get();} 形式的构造 + * @param task 任务 + * @param 泛型 + * @return 表示任务等待完成的 Future, 该 Future 的 {@code get} 方法在成功完成时将会返回该任务的结果 + */ + public Future submit(final Callable task) { + if (mThreadPool != null && task != null) { + return mThreadPool.submit(task); + } + return null; + } + + /** + * 提交一个 Runnable 任务用于执行 + * @param task 任务 + * @param result 返回的结果 + * @param 泛型 + * @return 表示任务等待完成的 Future, 该 Future 的 {@code get} 方法在成功完成时将会返回该任务的结果 + */ + public Future submit(final Runnable task, final T result) { + if (mThreadPool != null && task != null) { + return mThreadPool.submit(task, result); + } + return null; + } + + /** + * 提交一个 Runnable 任务用于执行 + * @param task 任务 + * @param 未知类型 + * @return 表示任务等待完成的 Future, 该 Future 的 {@code get} 方法在成功完成时将会返回 null 结果 + */ + public Future submit(final Runnable task) { + if (mThreadPool != null && task != null) { + return mThreadPool.submit(task); + } + return null; + } + + /** + * 执行给定的任务 + * 当所有任务完成时, 返回保持任务状态和结果的 Future 列表 + * 返回列表的所有元素的 {@link Future#isDone} 为 {@code true} + * 注意, 可以正常地或通过抛出异常来终止已完成任务 + * 如果正在进行此操作时修改了给定的 collection, 则此方法的结果是不确定的 + * @param tasks 任务集合 + * @param 泛型 + * @return 表示任务的 Future 列表, 列表顺序与给定任务列表的迭代器所生成的顺序相同, 每个任务都已完成 + * @throws InterruptedException 如果等待时发生中断, 在这种情况下取消尚未完成的任务 + */ + public List> invokeAll(final Collection> tasks) throws InterruptedException { + if (mThreadPool != null && tasks != null) { + return mThreadPool.invokeAll(tasks); + } + return null; + } + + /** + * 执行给定的任务 + * 当所有任务完成或超时期满时 ( 无论哪个首先发生 ), 返回保持任务状态和结果的 Future 列表 + * 返回列表的所有元素的 {@link Future#isDone} 为 {@code true} + * 一旦返回后, 即取消尚未完成的任务 + * 注意, 可以正常地或通过抛出异常来终止已完成任务 + * 如果此操作正在进行时修改了给定的 collection, 则此方法的结果是不确定的 + * @param tasks 任务集合 + * @param timeout 最长等待时间 + * @param unit 时间单位 + * @param 泛型 + * @return 表示任务的 Future 列表, 列表顺序与给定任务列表的迭代器所生成的顺序相同 + * 如果操作未超时, 则已完成所有任务, 如果确实超时了, 则某些任务尚未完成 + * @throws InterruptedException 如果等待时发生中断, 在这种情况下取消尚未完成的任务 + */ + public List> invokeAll(final Collection> tasks, final long timeout, + final TimeUnit unit) throws InterruptedException { + if (mThreadPool != null && tasks != null && unit != null) { + return mThreadPool.invokeAll(tasks, timeout, unit); + } + return null; + } + + /** + * 执行给定的任务 + * 如果某个任务已成功完成 ( 也就是未抛出异常 ), 则返回其结果 + * 一旦正常或异常返回后, 则取消尚未完成的任务 + * 如果此操作正在进行时修改了给定的 collection, 则此方法的结果是不确定的 + * @param tasks 任务集合 + * @param 泛型 + * @return 某个任务返回的结果 + * @throws InterruptedException 如果等待时发生中断 + * @throws ExecutionException 如果没有任务成功完成 + */ + public T invokeAny(final Collection> tasks) throws InterruptedException, ExecutionException { + if (mThreadPool != null && tasks != null) { + return mThreadPool.invokeAny(tasks); + } + return null; + } + + /** + * 执行给定的任务 + * 如果在给定的超时期满前某个任务已成功完成 ( 也就是未抛出异常 ), 则返回其结果 + * 一旦正常或异常返回后, 则取消尚未完成的任务 + * 如果此操作正在进行时修改了给定的 collection, 则此方法的结果是不确定的 + * @param tasks 任务集合 + * @param timeout 最长等待时间 + * @param unit 时间单位 + * @param 泛型 + * @return 某个任务返回的结果 + * @throws InterruptedException 如果等待时发生中断 + * @throws ExecutionException 如果没有任务成功完成 + * @throws TimeoutException 如果在所有任务成功完成之前给定的超时期满 + */ + public T invokeAny(final Collection> tasks, final long timeout, final TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + if (mThreadPool != null && tasks != null && unit != null) { + return mThreadPool.invokeAny(tasks, timeout, unit); + } + return null; + } + + // = + + /** + * 延迟执行 Runnable 命令 + * @param command 命令 + * @param delay 延迟时间 + * @param unit 单位 + * @param 未知类型 + * @return 表示挂起任务完成的 ScheduledFuture, 并且其 {@code get()} 方法在完成后将返回 {@code null} + */ + public ScheduledFuture schedule(final Runnable command, final long delay, final TimeUnit unit) { + if (mScheduleExec != null && command != null && unit != null) { + return mScheduleExec.schedule(command, delay, unit); + } + return null; + } + + /** + * 延迟执行 Callable 命令 + * @param callable 命令 + * @param delay 延迟时间 + * @param unit 时间单位 + * @param 泛型 + * @return 可用于提取结果或取消的 ScheduledFuture + */ + public ScheduledFuture schedule(final Callable callable, final long delay, final TimeUnit unit) { + if (mScheduleExec != null && callable != null && unit != null) { + return mScheduleExec.schedule(callable, delay, unit); + } + return null; + } + + /** + * 延迟并循环执行命令 + * @param command 命令 + * @param initialDelay 首次执行的延迟时间 + * @param period 连续执行之间的周期 + * @param unit 时间单位 + * @param 未知类型 + * @return 表示挂起任务完成的 ScheduledFuture, 并且其 {@code get()} 方法在取消后将抛出异常 + */ + public ScheduledFuture scheduleWithFixedRate(final Runnable command, final long initialDelay, final long period, final TimeUnit unit) { + if (mScheduleExec != null && command != null && unit != null) { + return mScheduleExec.scheduleAtFixedRate(command, initialDelay, period, unit); + } + return null; + } + + /** + * 延迟并以固定休息时间循环执行命令 + * @param command 命令 + * @param initialDelay 首次执行的延迟时间 + * @param delay 每一次执行终止和下一次执行开始之间的延迟 + * @param unit 时间单位 + * @param 未知类型 + * @return 表示挂起任务完成的 ScheduledFuture, 并且其 {@code get()} 方法在取消后将抛出异常 + */ + public ScheduledFuture scheduleWithFixedDelay(final Runnable command, final long initialDelay, final long delay, final TimeUnit unit) { + if (mScheduleExec != null && command != null && unit != null) { + return mScheduleExec.scheduleWithFixedDelay(command, initialDelay, delay, unit); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/validator/BankCheckUtils.java b/lib/DevApp/src/main/java/dev/utils/common/validator/BankCheckUtils.java new file mode 100644 index 0000000000..8f81721747 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/validator/BankCheckUtils.java @@ -0,0 +1,741 @@ +package dev.utils.common.validator; + +import dev.utils.JCLogUtils; + +/** + * detail: 银行卡管理工具类 + * @author AbrahamCaiJin + * @author Ttt ( 重写、规范注释、逻辑判断等 ) + *
+ *     当你输入信用卡号码的时候, 有没有担心输错了而造成损失呢, 其实可以不必这么担心
+ *     因为并不是一个随便的信用卡号码都是合法的, 它必须通过 Luhn 算法来验证通过
+ *     

+ * 该校验的过程: + * 1、从卡号最后一位数字开始, 逆向将奇数位 (1、3、5 等等 ) 相加 + * 2、从卡号最后一位数字开始, 逆向将偶数位数字, 先乘以 2 ( 如果乘积为两位数, 则将其减去 9), 再求和 + * 3、将奇数位总和加上偶数位总和, 结果应该可以被 10 整除 + *

+ * 例如, 卡号是: 5432123456788881 + * 则奇数、偶数位 ( 用红色标出 ) 分布: 5432123456788881 + * 奇数位和 = 35 + * 偶数位乘以 2 ( 有些要减去 9) 的结果: 1 6 2 6 1 5 7 7, 求和 = 35 + * 最后 35 + 35 = 70 可以被 10 整除, 认定校验通过 + *
+ */ +public final class BankCheckUtils { + + private BankCheckUtils() { + } + + // 日志 TAG + private static final String TAG = BankCheckUtils.class.getSimpleName(); + + /** + * 校验银行卡卡号是否合法 + * @param cardId 待校验银行卡号 + * @return {@code true} yes, {@code false} no + */ + public static boolean checkBankCard(final String cardId) { + if (cardId == null || cardId.trim().length() == 0) return false; + try { + char bit = getBankCardCheckCode(cardId.substring(0, cardId.length() - 1)); + if (bit == 'N') return false; + return (cardId.charAt(cardId.length() - 1) == bit); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "checkBankCard"); + return false; + } + } + + /** + * 从不含校验位的银行卡卡号采用 Luhm 校验算法获取校验位 + * @param nonCheckCodeCardId 待校验银行卡号 + * @return 银行卡校验位 + */ + public static char getBankCardCheckCode(final String nonCheckCodeCardId) { + try { + if (nonCheckCodeCardId == null + || nonCheckCodeCardId.trim().length() == 0 + || !nonCheckCodeCardId.matches("\\d+")) { + // 如果传的不是数据返回 N + return 'N'; + } + char[] chs = nonCheckCodeCardId.trim().toCharArray(); + int luhmSum = 0; + int len = chs.length; + for (int i = len - 1, j = 0; i >= 0; i--, j++) { + int k = chs[i] - '0'; + if (j % 2 == 0) { + k *= 2; + k = k / 10 + k % 10; + } + luhmSum += k; + } + return (luhmSum % 10 == 0) ? '0' : (char) ((10 - luhmSum % 10) + '0'); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getBankCardCheckCode"); + return 'N'; + } + } + + /** + * 通过银行卡的 前六位确定 判断银行开户行及卡种 + * @param cardBin 待校验银行卡号 + * @return 银行开户行及卡种 + */ + public static String getNameOfBank(final String cardBin) { + if (cardBin == null || cardBin.trim().length() < 6) return ""; + try { + // 通过银行卡的前六位确定 + String cardBin6 = cardBin.trim().substring(0, 6); + int index = -1; + for (int i = 0, len = BANK_BIN.length; i < len; i++) { + if (cardBin6.equals(BANK_BIN[i])) { + index = i; + } + } + if (index == -1) { + return ""; + } + return BANK_NAME[index]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getNameOfBank"); + return ""; + } + } + + // = + + /* + * 银行卡是由「发卡行标识代码 + 自定义 + 校验码」等部分组成的 BIN 号 银联标准卡与以往发行的银行卡最直接的区别就是其卡号前六位数字的不同 + * 银行卡卡号的前六位是用来表示发卡银行或机构的, 称为「发卡行识别码」(Bank Identification Number, 缩写为「BIN」) + * 银联标准卡是由国内各家商业银行 ( 含邮储、信用社 ) 共同发行、符合银联业务规范和技术标准、卡正面右下角带有「银联」标识 + * ( 目前新发行的银联标准卡一定带有国际化的银联新标识, 新发的非银联标准卡使用旧的联网通用银联标识 )、卡号前六位为 622126 至 622925 之一的银行卡 + * 是中国银行卡产业共有的民族品牌 + */ + + // BIN 号 + private static final String[] BANK_BIN = {"621098", "622150", "622151", + "622181", "622188", "955100", "621095", "620062", "621285", + "621798", "621799", "621797", "620529", "622199", "621096", + "621622", "623219", "621674", "623218", "621599", "370246", + "370248", "370249", "427010", "427018", "427019", "427020", + "427029", "427030", "427039", "370247", "438125", "438126", + "451804", "451810", "451811", "458071", "489734", "489735", + "489736", "510529", "427062", "524091", "427064", "530970", + "530990", "558360", "620200", "620302", "620402", "620403", + "620404", "524047", "620406", "620407", "525498", "620409", + "620410", "620411", "620412", "620502", "620503", "620405", + "620408", "620512", "620602", "620604", "620607", "620611", + "620612", "620704", "620706", "620707", "620708", "620709", + "620710", "620609", "620712", "620713", "620714", "620802", + "620711", "620904", "620905", "621001", "620902", "621103", + "621105", "621106", "621107", "621102", "621203", "621204", + "621205", "621206", "621207", "621208", "621209", "621210", + "621302", "621303", "621202", "621305", "621306", "621307", + "621309", "621311", "621313", "621211", "621315", "621304", + "621402", "621404", "621405", "621406", "621407", "621408", + "621409", "621410", "621502", "621317", "621511", "621602", + "621603", "621604", "621605", "621608", "621609", "621610", + "621611", "621612", "621613", "621614", "621615", "621616", + "621617", "621607", "621606", "621804", "621807", "621813", + "621814", "621817", "621901", "621904", "621905", "621906", + "621907", "621908", "621909", "621910", "621911", "621912", + "621913", "621915", "622002", "621903", "622004", "622005", + "622006", "622007", "622008", "622010", "622011", "622012", + "621914", "622015", "622016", "622003", "622018", "622019", + "622020", "622102", "622103", "622104", "622105", "622013", + "622111", "622114", "622200", "622017", "622202", "622203", + "622208", "622210", "622211", "622212", "622213", "622214", + "622110", "622220", "622223", "622225", "622229", "622230", + "622231", "622232", "622233", "622234", "622235", "622237", + "622215", "622239", "622240", "622245", "622224", "622303", + "622304", "622305", "622306", "622307", "622308", "622309", + "622238", "622314", "622315", "622317", "622302", "622402", + "622403", "622404", "622313", "622504", "622505", "622509", + "622513", "622517", "622502", "622604", "622605", "622606", + "622510", "622703", "622715", "622806", "622902", "622903", + "622706", "623002", "623006", "623008", "623011", "623012", + "622904", "623015", "623100", "623202", "623301", "623400", + "623500", "623602", "623803", "623901", "623014", "624100", + "624200", "624301", "624402", "62451804", "62451810", "62451811", + "62458071", "623700", "628288", "624000", "628286", "622206", + "621225", "526836", "513685", "543098", "458441", "620058", + "621281", "622246", "900000", "544210", "548943", "370267", + "621558", "621559", "621722", "621723", "620086", "621226", + "402791", "427028", "427038", "548259", "356879", "356880", + "356881", "356882", "528856", "621618", "620516", "621227", + "621721", "900010", "625330", "625331", "625332", "623062", + "622236", "621670", "524374", "550213", "374738", "374739", + "621288", "625708", "625709", "622597", "622599", "360883", + "360884", "625865", "625866", "625899", "621376", "620054", + "620142", "621428", "625939", "621434", "625987", "621761", + "621749", "620184", "621300", "621378", "625114", "622159", + "621720", "625021", "625022", "621379", "620114", "620146", + "621724", "625918", "621371", "620143", "620149", "621414", + "625914", "621375", "620187", "621433", "625986", "621370", + "625925", "622926", "622927", "622928", "622929", "622930", + "622931", "620124", "620183", "620561", "625116", "622227", + "621372", "621464", "625942", "622158", "625917", "621765", + "620094", "620186", "621719", "621719", "621750", "621377", + "620148", "620185", "621374", "621731", "621781", "552599", + "623206", "621671", "620059", "403361", "404117", "404118", + "404119", "404120", "404121", "463758", "514027", "519412", + "519413", "520082", "520083", "558730", "621282", "621336", + "621619", "622821", "622822", "622823", "622824", "622825", + "622826", "622827", "622828", "622836", "622837", "622840", + "622841", "622843", "622844", "622845", "622846", "622847", + "622848", "622849", "623018", "625996", "625997", "625998", + "628268", "625826", "625827", "548478", "544243", "622820", + "622830", "622838", "625336", "628269", "620501", "621660", + "621661", "621662", "621663", "621665", "621667", "621668", + "621669", "621666", "625908", "625910", "625909", "356833", + "356835", "409665", "409666", "409668", "409669", "409670", + "409671", "409672", "456351", "512315", "512316", "512411", + "512412", "514957", "409667", "518378", "518379", "518474", + "518475", "518476", "438088", "524865", "525745", "525746", + "547766", "552742", "553131", "558868", "514958", "622752", + "622753", "622755", "524864", "622757", "622758", "622759", + "622760", "622761", "622762", "622763", "601382", "622756", + "628388", "621256", "621212", "620514", "622754", "622764", + "518377", "622765", "622788", "621283", "620061", "621725", + "620040", "558869", "621330", "621331", "621332", "621333", + "621297", "377677", "621568", "621569", "625905", "625906", + "625907", "628313", "625333", "628312", "623208", "621620", + "621756", "621757", "621758", "621759", "621785", "621786", + "621787", "621788", "621789", "621790", "621672", "625337", + "625338", "625568", "621648", "621248", "621249", "622750", + "622751", "622771", "622772", "622770", "625145", "620531", + "620210", "620211", "622479", "622480", "622273", "622274", + "621231", "621638", "621334", "625140", "621395", "622725", + "622728", "621284", "421349", "434061", "434062", "436728", + "436742", "453242", "491031", "524094", "526410", "544033", + "552245", "589970", "620060", "621080", "621081", "621466", + "621467", "621488", "621499", "621598", "621621", "621700", + "622280", "622700", "622707", "622966", "622988", "625955", + "625956", "553242", "621082", "621673", "623211", "356896", + "356899", "356895", "436718", "436738", "436745", "436748", + "489592", "531693", "532450", "532458", "544887", "552801", + "557080", "558895", "559051", "622166", "622168", "622708", + "625964", "625965", "625966", "628266", "628366", "625362", + "625363", "628316", "628317", "620021", "620521", "405512", + "601428", "405512", "434910", "458123", "458124", "520169", + "522964", "552853", "601428", "622250", "622251", "521899", + "622254", "622255", "622256", "622257", "622258", "622259", + "622253", "622261", "622284", "622656", "628216", "622252", + "66405512", "622260", "66601428", "955590", "955591", "955592", + "955593", "628218", "622262", "621069", "620013", "625028", + "625029", "621436", "621002", "621335", "433670", "433680", + "442729", "442730", "620082", "622690", "622691", "622692", + "622696", "622698", "622998", "622999", "433671", "968807", + "968808", "968809", "621771", "621767", "621768", "621770", + "621772", "621773", "620527", "356837", "356838", "486497", + "622660", "622662", "622663", "622664", "622665", "622666", + "622667", "622669", "622670", "622671", "622672", "622668", + "622661", "622674", "622673", "620518", "621489", "621492", + "620535", "623156", "621490", "621491", "620085", "623155", + "623157", "623158", "623159", "999999", "621222", "623020", + "623021", "623022", "623023", "622630", "622631", "622632", + "622633", "622615", "622616", "622618", "622622", "622617", + "622619", "415599", "421393", "421865", "427570", "427571", + "472067", "472068", "622620", "621691", "545392", "545393", + "545431", "545447", "356859", "356857", "407405", "421869", + "421870", "421871", "512466", "356856", "528948", "552288", + "622600", "622601", "622602", "517636", "622621", "628258", + "556610", "622603", "464580", "464581", "523952", "545217", + "553161", "356858", "622623", "625911", "377152", "377153", + "377158", "377155", "625912", "625913", "356885", "356886", + "356887", "356888", "356890", "402658", "410062", "439188", + "439227", "468203", "479228", "479229", "512425", "521302", + "524011", "356889", "545620", "545621", "545947", "545948", + "552534", "552587", "622575", "622576", "622577", "622579", + "622580", "545619", "622581", "622582", "622588", "622598", + "622609", "690755", "690755", "545623", "621286", "620520", + "621483", "621485", "621486", "628290", "622578", "370285", + "370286", "370287", "370289", "439225", "518710", "518718", + "628362", "439226", "628262", "625802", "625803", "621299", + "966666", "622909", "622908", "438588", "438589", "461982", + "486493", "486494", "486861", "523036", "451289", "527414", + "528057", "622901", "622902", "622922", "628212", "451290", + "524070", "625084", "625085", "625086", "625087", "548738", + "549633", "552398", "625082", "625083", "625960", "625961", + "625962", "625963", "356851", "356852", "404738", "404739", + "456418", "498451", "515672", "356850", "517650", "525998", + "622177", "622277", "622516", "622517", "622518", "622520", + "622521", "622522", "622523", "628222", "628221", "984301", + "984303", "622176", "622276", "622228", "621352", "621351", + "621390", "621792", "625957", "625958", "621791", "620530", + "625993", "622519", "621793", "621795", "621796", "622500", + "623078", "622384", "940034", "940015", "622886", "622391", + "940072", "622359", "940066", "622857", "940065", "621019", + "622309", "621268", "622884", "621453", "622684", "621016", + "621015", "622950", "622951", "621072", "623183", "623185", + "621005", "622172", "622985", "622987", "622267", "622278", + "622279", "622468", "622892", "940021", "621050", "620522", + "356827", "356828", "356830", "402673", "402674", "438600", + "486466", "519498", "520131", "524031", "548838", "622148", + "622149", "622268", "356829", "622300", "628230", "622269", + "625099", "625953", "625350", "625351", "625352", "519961", + "625839", "421317", "602969", "621030", "621420", "621468", + "623111", "422160", "422161", "622865", "940012", "623131", + "622178", "622179", "628358", "622394", "940025", "621279", + "622281", "622316", "940022", "621418", "512431", "520194", + "621626", "623058", "602907", "622986", "622989", "622298", + "622338", "940032", "623205", "621977", "990027", "622325", + "623029", "623105", "621244", "623081", "623108", "566666", + "622455", "940039", "622466", "628285", "622420", "940041", + "623118", "603708", "622993", "623070", "623069", "623172", + "623173", "622383", "622385", "628299", "603506", "603367", + "622878", "623061", "623209", "628242", "622595", "622303", + "622305", "621259", "622596", "622333", "940050", "621439", + "623010", "621751", "628278", "625502", "625503", "625135", + "622476", "621754", "622143", "940001", "623026", "623086", + "628291", "621532", "621482", "622135", "622152", "622153", + "622154", "622996", "622997", "940027", "623099", "623007", + "940055", "622397", "622398", "940054", "622331", "622426", + "625995", "621452", "628205", "628214", "625529", "622428", + "621529", "622429", "621417", "623089", "623200", "940057", + "622311", "623119", "622877", "622879", "621775", "623203", + "603601", "622137", "622327", "622340", "622366", "622134", + "940018", "623016", "623096", "940049", "622425", "622425", + "621577", "622485", "623098", "628329", "621538", "940006", + "621269", "622275", "621216", "622465", "940031", "621252", + "622146", "940061", "621419", "623170", "622440", "940047", + "940017", "622418", "623077", "622413", "940002", "623188", + "622310", "940068", "622321", "625001", "622427", "940069", + "623039", "628273", "622370", "683970", "940074", "621437", + "628319", "990871", "622308", "621415", "623166", "622132", + "621340", "621341", "622140", "623073", "622147", "621633", + "622301", "623171", "621422", "622335", "622336", "622165", + "622315", "628295", "625950", "621760", "622337", "622411", + "623102", "622342", "623048", "622367", "622392", "623085", + "622395", "622441", "622448", "621413", "622856", "621037", + "621097", "621588", "623032", "622644", "623518", "622870", + "622866", "623072", "622897", "628279", "622864", "621403", + "622561", "622562", "622563", "622167", "622777", "621497", + "622868", "622899", "628255", "625988", "622566", "622567", + "622625", "622626", "625946", "628200", "621076", "504923", + "622173", "622422", "622447", "622131", "940076", "621579", + "622876", "622873", "622962", "622936", "623060", "622937", + "623101", "621460", "622939", "622960", "623523", "621591", + "622961", "628210", "622283", "625902", "621010", "622980", + "623135", "621726", "621088", "620517", "622740", "625036", + "621014", "621004", "622972", "623196", "621028", "623083", + "628250", "623121", "621070", "628253", "622979", "621035", + "621038", "621086", "621498", "621296", "621448", "622945", + "621755", "622940", "623120", "628355", "621089", "623161", + "628339", "621074", "621515", "623030", "621345", "621090", + "623178", "621091", "623168", "621057", "623199", "621075", + "623037", "628303", "621233", "621235", "621223", "621780", + "621221", "623138", "628389", "621239", "623068", "621271", + "628315", "621272", "621738", "621273", "623079", "621263", + "621325", "623084", "621327", "621753", "628331", "623160", + "621366", "621388", "621348", "621359", "621360", "621217", + "622959", "621270", "622396", "622511", "623076", "621391", + "621339", "621469", "621625", "623688", "623113", "621601", + "621655", "621636", "623182", "623087", "621696", "622955", + "622478", "940013", "621495", "621688", "623162", "622462", + "628272", "625101", "622323", "623071", "603694", "622128", + "622129", "623035", "623186", "621522", "622271", "940037", + "940038", "985262", "622322", "628381", "622481", "622341", + "940058", "623115", "621258", "621465", "621528", "622328", + "940062", "625288", "623038", "625888", "622332", "940063", + "623123", "622138", "621066", "621560", "621068", "620088", + "621067", "622531", "622329", "623103", "622339", "620500", + "621024", "622289", "622389", "628300", "625516", "621516", + "622859", "622869", "623075", "622895", "623125", "622947", + "621561", "623095", "621073", "623109", "621361", "623033", + "623207", "622891", "621363", "623189", "623510", "622995", + "621053", "621230", "621229", "622218", "628267", "621392", + "621481", "621310", "621396", "623251", "628351"}; + + // 发卡行·卡种名称 + private static final String[] BANK_NAME = {"邮储银行·绿卡通", "邮储银行·绿卡银联标准卡", + "邮储银行·绿卡银联标准卡", "邮储银行·绿卡专用卡", "邮储银行·绿卡银联标准卡", "邮储银行·绿卡 (银联卡)", + "邮储银行·绿卡 VIP 卡", "邮储银行·银联标准卡", "邮储银行·中职学生资助卡", "邮政储蓄银行·IC 绿卡通 VIP 卡", + "邮政储蓄银行·IC 绿卡通", "邮政储蓄银行·IC 联名卡", "邮政储蓄银行·IC 预付费卡", "邮储银行·绿卡银联标准卡", + "邮储银行·绿卡通", "邮政储蓄银行·武警军人保障卡", "邮政储蓄银行·中国旅游卡 (金卡)", + "邮政储蓄银行·普通高中学生资助卡", "邮政储蓄银行·中国旅游卡 (普卡)", "邮政储蓄银行·福农卡", + "工商银行·牡丹运通卡金卡", "工商银行·牡丹运通卡金卡", "工商银行·牡丹运通卡金卡", + "工商银行·牡丹 VISA 卡 (单位卡)", "工商银行·牡丹 VISA 信用卡", "工商银行·牡丹 VISA 卡 (单位卡)", + "工商银行·牡丹 VISA 信用卡", "工商银行·牡丹 VISA 信用卡", "工商银行·牡丹 VISA 信用卡", + "工商银行·牡丹 VISA 信用卡", "工商银行·牡丹运通卡普通卡", "工商银行·牡丹 VISA 信用卡", + "工商银行·牡丹 VISA 白金卡", "工商银行·牡丹贷记卡 (银联卡)", "工商银行·牡丹贷记卡 (银联卡)", + "工商银行·牡丹贷记卡 (银联卡)", "工商银行·牡丹贷记卡 (银联卡)", "工商银行·牡丹欧元卡", "工商银行·牡丹欧元卡", + "工商银行·牡丹欧元卡", "工商银行·牡丹万事达国际借记卡", "工商银行·牡丹 VISA 信用卡", "工商银行·海航信用卡", + "工商银行·牡丹 VISA 信用卡", "工商银行·牡丹万事达信用卡", "工商银行·牡丹万事达信用卡", + "工商银行·牡丹万事达信用卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹万事达白金卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·海航信用卡个人普卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·灵通卡", "工商银行·牡丹灵通卡", "工商银行·E 时代卡", "工商银行·E 时代卡", + "工商银行·理财金卡", "工商银行·准贷记卡 (个普)", "工商银行·准贷记卡 (个普)", "工商银行·准贷记卡 (个普)", + "工商银行·准贷记卡 (个普)", "工商银行·准贷记卡 (个普)", "工商银行·牡丹灵通卡", "工商银行·准贷记卡 (商普)", + "工商银行·牡丹卡 (商务卡)", "工商银行·准贷记卡 (商金)", "工商银行·牡丹卡 (商务卡)", "工商银行·贷记卡 (个普)", + "工商银行·牡丹卡 (个人卡)", "工商银行·牡丹卡 (个人卡)", "工商银行·牡丹卡 (个人卡)", "工商银行·牡丹卡 (个人卡)", + "工商银行·贷记卡 (个金)", "工商银行·牡丹交通卡", "工商银行·准贷记卡 (个金)", "工商银行·牡丹交通卡", + "工商银行·贷记卡 (商普)", "工商银行·贷记卡 (商金)", "工商银行·牡丹卡 (商务卡)", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹交通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹贷记卡", + "工商银行·牡丹贷记卡", "工商银行·牡丹贷记卡", "工商银行·牡丹贷记卡", "工商银行·牡丹灵通卡", + "工商银行·中央预算单位公务卡", "工商银行·牡丹灵通卡", "工商银行·财政预算单位公务卡", "工商银行·牡丹卡白金卡", + "工商银行·牡丹卡普卡", "工商银行·国航知音牡丹信用卡", "工商银行·国航知音牡丹信用卡", "工商银行·国航知音牡丹信用卡", + "工商银行·国航知音牡丹信用卡", "工商银行·银联标准卡", "工商银行·中职学生资助卡", "工商银行·专用信用消费卡", + "工商银行·牡丹社会保障卡", "中国工商银行·牡丹东航联名卡", "中国工商银行·牡丹东航联名卡", + "中国工商银行·牡丹运通白金卡", "中国工商银行·福农灵通卡", "中国工商银行·福农灵通卡", "工商银行·灵通卡", + "工商银行·灵通卡", "中国工商银行·中国旅行卡", "工商银行·牡丹卡普卡", "工商银行·国际借记卡", + "工商银行·国际借记卡", "工商银行·国际借记卡", "工商银行·国际借记卡", "中国工商银行·牡丹 JCB 信用卡", + "中国工商银行·牡丹 JCB 信用卡", "中国工商银行·牡丹 JCB 信用卡", "中国工商银行·牡丹 JCB 信用卡", + "中国工商银行·牡丹多币种卡", "中国工商银行·武警军人保障卡", "工商银行·预付芯片卡", "工商银行·理财金账户金卡", + "工商银行·灵通卡", "工商银行·牡丹宁波市民卡", "中国工商银行·中国旅游卡", "中国工商银行·中国旅游卡", + "中国工商银行·中国旅游卡", "中国工商银行·借记卡", "中国工商银行·借贷合一卡", "中国工商银行·普通高中学生资助卡", + "中国工商银行·牡丹多币种卡", "中国工商银行·牡丹多币种卡", "中国工商银行·牡丹百夫长信用卡", + "中国工商银行·牡丹百夫长信用卡", "工商银行·工银财富卡", "中国工商银行·中小商户采购卡", + "中国工商银行·中小商户采购卡", "中国工商银行·环球旅行金卡", "中国工商银行·环球旅行白金卡", + "中国工商银行·牡丹工银大来卡", "中国工商银行·牡丹工银大莱卡", "中国工商银行·IC 金卡", "中国工商银行·IC 白金卡", + "中国工商银行·工行 IC 卡 (红卡)", "中国工商银行布鲁塞尔分行·借记卡", "中国工商银行布鲁塞尔分行·预付卡", + "中国工商银行布鲁塞尔分行·预付卡", "中国工商银行金边分行·借记卡", "中国工商银行金边分行·信用卡", + "中国工商银行金边分行·借记卡", "中国工商银行金边分行·信用卡", "中国工商银行加拿大分行·借记卡", + "中国工商银行加拿大分行·借记卡", "中国工商银行加拿大分行·预付卡", "中国工商银行巴黎分行·借记卡", + "中国工商银行巴黎分行·借记卡", "中国工商银行巴黎分行·贷记卡", "中国工商银行法兰克福分行·贷记卡", + "中国工商银行法兰克福分行·借记卡", "中国工商银行法兰克福分行·贷记卡", "中国工商银行法兰克福分行·贷记卡", + "中国工商银行法兰克福分行·借记卡", "中国工商银行法兰克福分行·预付卡", "中国工商银行法兰克福分行·预付卡", + "中国工商银行印尼分行·借记卡", "中国工商银行印尼分行·信用卡", "中国工商银行米兰分行·借记卡", + "中国工商银行米兰分行·预付卡", "中国工商银行米兰分行·预付卡", "中国工商银行阿拉木图子行·借记卡", + "中国工商银行阿拉木图子行·贷记卡", "中国工商银行阿拉木图子行·借记卡", "中国工商银行阿拉木图子行·预付卡", + "中国工商银行万象分行·借记卡", "中国工商银行万象分行·贷记卡", "中国工商银行卢森堡分行·借记卡", + "中国工商银行卢森堡分行·贷记卡", "中国工商银行澳门分行·E 时代卡", "中国工商银行澳门分行·E 时代卡", + "中国工商银行澳门分行·E 时代卡", "中国工商银行澳门分行·理财金账户", "中国工商银行澳门分行·理财金账户", + "中国工商银行澳门分行·理财金账户", "中国工商银行澳门分行·预付卡", "中国工商银行澳门分行·预付卡", + "中国工商银行澳门分行·工银闪付预付卡", "中国工商银行澳门分行·工银银联公司卡", "中国工商银行澳门分行·Diamond", + "中国工商银行阿姆斯特丹·借记卡", "中国工商银行卡拉奇分行·借记卡", "中国工商银行卡拉奇分行·贷记卡", + "中国工商银行新加坡分行·贷记卡", "中国工商银行新加坡分行·贷记卡", "中国工商银行新加坡分行·借记卡", + "中国工商银行新加坡分行·预付卡", "中国工商银行新加坡分行·预付卡", "中国工商银行新加坡分行·借记卡", + "中国工商银行新加坡分行·借记卡", "中国工商银行马德里分行·借记卡", "中国工商银行马德里分行·借记卡", + "中国工商银行马德里分行·预付卡", "中国工商银行马德里分行·预付卡", "中国工商银行伦敦子行·借记卡", + "中国工商银行伦敦子行·工银伦敦借记卡", "中国工商银行伦敦子行·借记卡", "农业银行·金穗贷记卡", "农业银行·中国旅游卡", + "农业银行·普通高中学生资助卡", "农业银行·银联标准卡", "农业银行·金穗贷记卡 (银联卡)", + "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡 (银联卡)", + "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡 (银联卡)", "农业银行·VISA 白金卡", + "农业银行·万事达白金卡", "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡 (银联卡)", + "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡", "农业银行·中职学生资助卡", + "农业银行·专用惠农卡", "农业银行·武警军人保障卡", "农业银行·金穗校园卡 (银联卡)", "农业银行·金穗星座卡 (银联卡)", + "农业银行·金穗社保卡 (银联卡)", "农业银行·金穗旅游卡 (银联卡)", "农业银行·金穗青年卡 (银联卡)", + "农业银行·复合介质金穗通宝卡", "农业银行·金穗海通卡", "农业银行·退役金卡", "农业银行·金穗贷记卡", + "农业银行·金穗贷记卡", "农业银行·金穗通宝卡 (银联卡)", "农业银行·金穗惠农卡", "农业银行·金穗通宝银卡", + "农业银行·金穗通宝卡 (银联卡)", "农业银行·金穗通宝卡 (银联卡)", "农业银行·金穗通宝卡", + "农业银行·金穗通宝卡 (银联卡)", "农业银行·金穗通宝卡 (银联卡)", "农业银行·金穗通宝钻石卡", "农业银行·掌尚钱包", + "农业银行·银联 IC 卡金卡", "农业银行·银联预算单位公务卡金卡", "农业银行·银联 IC 卡白金卡", "农业银行·金穗公务卡", + "中国农业银行贷记卡·IC 普卡", "中国农业银行贷记卡·IC 金卡", "中国农业银行贷记卡·澳元卡", + "中国农业银行贷记卡·欧元卡", "中国农业银行贷记卡·金穗通商卡", "中国农业银行贷记卡·金穗通商卡", + "中国农业银行贷记卡·银联白金卡", "中国农业银行贷记卡·中国旅游卡", "中国农业银行贷记卡·银联 IC 公务卡", + "宁波市农业银行·市民卡 B 卡", "中国银行·联名卡", "中国银行·个人普卡", "中国银行·个人金卡", "中国银行·员工普卡", + "中国银行·员工金卡", "中国银行·理财普卡", "中国银行·理财金卡", "中国银行·理财银卡", "中国银行·理财白金卡", + "中国银行·中行金融 IC 卡白金卡", "中国银行·中行金融 IC 卡普卡", "中国银行·中行金融 IC 卡金卡", + "中国银行·中银 JCB 卡金卡", "中国银行·中银 JCB 卡普卡", "中国银行·员工普卡", "中国银行·个人普卡", + "中国银行·中银威士信用卡员", "中国银行·中银威士信用卡员", "中国银行·个人白金卡", "中国银行·中银威士信用卡", + "中国银行·长城公务卡", "中国银行·长城电子借记卡", "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", + "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", "中国银行·中银威士信用卡员", + "中国银行·长城万事达信用卡", "中国银行·长城万事达信用卡", "中国银行·长城万事达信用卡", "中国银行·长城万事达信用卡", + "中国银行·长城万事达信用卡", "中国银行·中银奥运信用卡", "中国银行·长城信用卡", "中国银行·长城信用卡", + "中国银行·长城信用卡", "中国银行·长城万事达信用卡", "中国银行·长城公务卡", "中国银行·长城公务卡", + "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", "中国银行·长城人民币信用卡", "中国银行·长城人民币信用卡", + "中国银行·长城人民币信用卡", "中国银行·长城信用卡", "中国银行·长城人民币信用卡", "中国银行·长城人民币信用卡", + "中国银行·长城信用卡", "中国银行·银联单币贷记卡", "中国银行·长城信用卡", "中国银行·长城信用卡", + "中国银行·长城信用卡", "中国银行·长城电子借记卡", "中国银行·长城人民币信用卡", "中国银行·银联标准公务卡", + "中国银行·一卡双账户普卡", "中国银行·财互通卡", "中国银行·电子现金卡", "中国银行·长城人民币信用卡", + "中国银行·长城单位信用卡普卡", "中国银行·中银女性主题信用卡", "中国银行·长城单位信用卡金卡", "中国银行·白金卡", + "中国银行·中职学生资助卡", "中国银行·银联标准卡", "中国银行·金融 IC 卡", "中国银行·长城社会保障卡", + "中国银行·世界卡", "中国银行·社保联名卡", "中国银行·社保联名卡", "中国银行·医保联名卡", "中国银行·医保联名卡", + "中国银行·公司借记卡", "中国银行·银联美运顶级卡", "中国银行·长城福农借记卡金卡", "中国银行·长城福农借记卡普卡", + "中国银行·中行金融 IC 卡普卡", "中国银行·中行金融 IC 卡金卡", "中国银行·中行金融 IC 卡白金卡", + "中国银行·长城银联公务 IC 卡白金卡", "中国银行·中银旅游信用卡", "中国银行·长城银联公务 IC 卡金卡", + "中国银行·中国旅游卡", "中国银行·武警军人保障卡", "中国银行·社保联名借记 IC 卡", "中国银行·社保联名借记 IC 卡", + "中国银行·医保联名借记 IC 卡", "中国银行·医保联名借记 IC 卡", "中国银行·借记 IC 个人普卡", + "中国银行·借记 IC 个人金卡", "中国银行·借记 IC 个人普卡", "中国银行·借记 IC 白金卡", "中国银行·借记 IC 钻石卡", + "中国银行·借记 IC 联名卡", "中国银行·普通高中学生资助卡", "中国银行·长城环球通港澳台旅游金卡", + "中国银行·长城环球通港澳台旅游白金卡", "中国银行·中银福农信用卡", "中国银行金边分行·借记卡", + "中国银行雅加达分行·借记卡", "中国银行首尔分行·借记卡", "中国银行澳门分行·人民币信用卡", + "中国银行澳门分行·人民币信用卡", "中国银行澳门分行·中银卡", "中国银行澳门分行·中银卡", "中国银行澳门分行·中银卡", + "中国银行澳门分行·中银银联双币商务卡", "中国银行澳门分行·预付卡", "中国银行澳门分行·澳门中国银行银联预付卡", + "中国银行澳门分行·澳门中国银行银联预付卡", "中国银行澳门分行·熊猫卡", "中国银行澳门分行·财富卡", + "中国银行澳门分行·银联港币卡", "中国银行澳门分行·银联澳门币卡", "中国银行马尼拉分行·双币种借记卡", + "中国银行胡志明分行·借记卡", "中国银行曼谷分行·借记卡", "中国银行曼谷分行·长城信用卡环球通", + "中国银行曼谷分行·借记卡", "建设银行·龙卡准贷记卡", "建设银行·龙卡准贷记卡金卡", "建设银行·中职学生资助卡", + "建设银行·乐当家银卡 VISA", "建设银行·乐当家金卡 VISA", "建设银行·乐当家白金卡", + "建设银行·龙卡普通卡 VISA", "建设银行·龙卡储蓄卡", "建设银行·VISA 准贷记卡 (银联卡)", + "建设银行·VISA 准贷记金卡", "建设银行·乐当家", "建设银行·乐当家", "建设银行·准贷记金卡", + "建设银行·乐当家白金卡", "建设银行·金融复合 IC 卡", "建设银行·银联标准卡", "建设银行·银联理财钻石卡", + "建设银行·金融 IC 卡", "建设银行·理财白金卡", "建设银行·社保 IC 卡", "建设银行·财富卡私人银行卡", + "建设银行·理财金卡", "建设银行·福农卡", "建设银行·武警军人保障卡", "建设银行·龙卡通", "建设银行·银联储蓄卡", + "建设银行·龙卡储蓄卡 (银联卡)", "建设银行·准贷记卡", "建设银行·理财白金卡", "建设银行·理财金卡", + "建设银行·准贷记卡普卡", "建设银行·准贷记卡金卡", "建设银行·龙卡信用卡", "建设银行·建行陆港通龙卡", + "中国建设银行·普通高中学生资助卡", "中国建设银行·中国旅游卡", "中国建设银行·龙卡 JCB 金卡", + "中国建设银行·龙卡 JCB 白金卡", "中国建设银行·龙卡 JCB 普卡", "中国建设银行·龙卡贷记卡公司卡", + "中国建设银行·龙卡贷记卡", "中国建设银行·龙卡国际普通卡 VISA", "中国建设银行·龙卡国际金卡 VISA", + "中国建设银行·VISA 白金信用卡", "中国建设银行·龙卡国际白金卡", "中国建设银行·龙卡国际普通卡 MASTER", + "中国建设银行·龙卡国际金卡 MASTER", "中国建设银行·龙卡万事达金卡", "中国建设银行·龙卡贷记卡", + "中国建设银行·龙卡万事达白金卡", "中国建设银行·龙卡贷记卡", "中国建设银行·龙卡万事达信用卡", + "中国建设银行·龙卡人民币信用卡", "中国建设银行·龙卡人民币信用金卡", "中国建设银行·龙卡人民币白金卡", + "中国建设银行·龙卡 IC 信用卡普卡", "中国建设银行·龙卡 IC 信用卡金卡", "中国建设银行·龙卡 IC 信用卡白金卡", + "中国建设银行·龙卡银联公务卡普卡", "中国建设银行·龙卡银联公务卡金卡", "中国建设银行·中国旅游卡", + "中国建设银行·中国旅游卡", "中国建设银行·龙卡 IC 公务卡", "中国建设银行·龙卡 IC 公务卡", "交通银行·交行预付卡", + "交通银行·世博预付 IC 卡", "交通银行·太平洋互连卡", "交通银行·太平洋万事顺卡", "交通银行·太平洋互连卡 (银联卡)", + "交通银行·太平洋白金信用卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋双币贷记卡", + "交通银行·太平洋白金信用卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋万事顺卡", "交通银行·太平洋人民币贷记卡", + "交通银行·太平洋人民币贷记卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋准贷记卡", "交通银行·太平洋准贷记卡", + "交通银行·太平洋准贷记卡", "交通银行·太平洋准贷记卡", "交通银行·太平洋借记卡", "交通银行·太平洋借记卡", + "交通银行·太平洋人民币贷记卡", "交通银行·太平洋借记卡", "交通银行·太平洋 MORE 卡", "交通银行·白金卡", + "交通银行·交通银行公务卡普卡", "交通银行·太平洋人民币贷记卡", "交通银行·太平洋互连卡", "交通银行·太平洋借记卡", + "交通银行·太平洋万事顺卡", "交通银行·太平洋贷记卡 (银联卡)", "交通银行·太平洋贷记卡 (银联卡)", + "交通银行·太平洋贷记卡 (银联卡)", "交通银行·太平洋贷记卡 (银联卡)", "交通银行·交通银行公务卡金卡", + "交通银行·交银 IC 卡", "交通银行香港分行·交通银行港币借记卡", "交通银行香港分行·港币礼物卡", + "交通银行香港分行·双币种信用卡", "交通银行香港分行·双币种信用卡", "交通银行香港分行·双币卡", + "交通银行香港分行·银联人民币卡", "交通银行澳门分行·银联借记卡", "中信银行·中信借记卡", "中信银行·中信借记卡", + "中信银行·中信国际借记卡", "中信银行·中信国际借记卡", "中信银行·中国旅行卡", "中信银行·中信借记卡 (银联卡)", + "中信银行·中信借记卡 (银联卡)", "中信银行·中信贵宾卡 (银联卡)", "中信银行·中信理财宝金卡", + "中信银行·中信理财宝白金卡", "中信银行·中信钻石卡", "中信银行·中信钻石卡", "中信银行·中信借记卡", + "中信银行·中信理财宝 (银联卡)", "中信银行·中信理财宝 (银联卡)", "中信银行·中信理财宝 (银联卡)", + "中信银行·借记卡", "中信银行·理财宝 IC 卡", "中信银行·理财宝 IC 卡", "中信银行·理财宝 IC 卡", + "中信银行·理财宝 IC 卡", "中信银行·理财宝 IC 卡", "中信银行·主账户复合电子现金卡", "光大银行·阳光商旅信用卡", + "光大银行·阳光商旅信用卡", "光大银行·阳光商旅信用卡", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", + "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", + "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", + "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", + "光大银行·阳光卡 (银联卡)", "光大银行·借记卡普卡", "光大银行·社会保障 IC 卡", "光大银行·IC 借记卡普卡", + "光大银行·手机支付卡", "光大银行·联名 IC 卡普卡", "光大银行·借记 IC 卡白金卡", "光大银行·借记 IC 卡金卡", + "光大银行·阳光旅行卡", "光大银行·借记 IC 卡钻石卡", "光大银行·联名 IC 卡金卡", "光大银行·联名 IC 卡白金卡", + "光大银行·联名 IC 卡钻石卡", "华夏银行·华夏卡 (银联卡)", "华夏银行·华夏白金卡", "华夏银行·华夏普卡", + "华夏银行·华夏金卡", "华夏银行·华夏白金卡", "华夏银行·华夏钻石卡", "华夏银行·华夏卡 (银联卡)", + "华夏银行·华夏至尊金卡 (银联卡)", "华夏银行·华夏丽人卡 (银联卡)", "华夏银行·华夏万通卡", + "民生银行·民生借记卡 (银联卡)", "民生银行·民生银联借记卡-金卡", "民生银行·钻石卡", + "民生银行·民生借记卡 (银联卡)", "民生银行·民生借记卡 (银联卡)", "民生银行·民生借记卡 (银联卡)", + "民生银行·民生借记卡", "民生银行·民生国际卡", "民生银行·民生国际卡 (银卡)", "民生银行·民生国际卡 (欧元卡)", + "民生银行·民生国际卡 (澳元卡)", "民生银行·民生国际卡", "民生银行·民生国际卡", "民生银行·薪资理财卡", + "民生银行·借记卡普卡", "民生银行·民生 MasterCard", "民生银行·民生 MasterCard", + "民生银行·民生 MasterCard", "民生银行·民生 MasterCard", "民生银行·民生 JCB 信用卡", + "民生银行·民生 JCB 金卡", "民生银行·民生贷记卡 (银联卡)", "民生银行·民生贷记卡 (银联卡)", + "民生银行·民生贷记卡 (银联卡)", "民生银行·民生贷记卡 (银联卡)", "民生银行·民生贷记卡 (银联卡)", + "民生银行·民生 JCB 普卡", "民生银行·民生贷记卡 (银联卡)", "民生银行·民生贷记卡 (银联卡)", + "民生银行·民生信用卡 (银联卡)", "民生银行·民生信用卡 (银联卡)", "民生银行·民生银联白金信用卡", + "民生银行·民生贷记卡 (银联卡)", "民生银行·民生银联个人白金卡", "民生银行·公务卡金卡", + "民生银行·民生贷记卡 (银联卡)", "民生银行·民生银联商务信用卡", "民生银行·民 VISA 无限卡", + "民生银行·民生 VISA 商务白金卡", "民生银行·民生万事达钛金卡", "民生银行·民生万事达世界卡", + "民生银行·民生万事达白金公务卡", "民生银行·民生 JCB 白金卡", "民生银行·银联标准金卡", "民生银行·银联芯片普卡", + "民生银行·民生运通双币信用卡普卡", "民生银行·民生运通双币信用卡金卡", "民生银行·民生运通双币信用卡钻石卡", + "民生银行·民生运通双币标准信用卡白金卡", "民生银行·银联芯片金卡", "民生银行·银联芯片白金卡", + "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", + "招商银行·招商银行信用卡", "招商银行·两地一卡通", "招商银行·招行国际卡 (银联卡)", "招商银行·招商银行信用卡", + "招商银行·VISA 商务信用卡", "招商银行·招行国际卡 (银联卡)", "招商银行·招商银行信用卡", + "招商银行·招商银行信用卡", "招商银行·招行国际卡 (银联卡)", "招商银行·世纪金花联名信用卡", + "招商银行·招行国际卡 (银联卡)", "招商银行·招商银行信用卡", "招商银行·万事达信用卡", "招商银行·万事达信用卡", + "招商银行·万事达信用卡", "招商银行·万事达信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", + "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", + "招商银行·一卡通 (银联卡)", "招商银行·万事达信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", + "招商银行·一卡通 (银联卡)", "招商银行·公司卡 (银联卡)", "招商银行·金卡", "招商银行·招行一卡通", + "招商银行·招行一卡通", "招商银行·万事达信用卡", "招商银行·金葵花卡", "招商银行·电子现金卡", + "招商银行·银联 IC 普卡", "招商银行·银联 IC 金卡", "招商银行·银联金葵花 IC 卡", "招商银行·IC 公务卡", + "招商银行·招商银行信用卡", "招商银行信用卡中心·美国运通绿卡", "招商银行信用卡中心·美国运通金卡", + "招商银行信用卡中心·美国运通商务绿卡", "招商银行信用卡中心·美国运通商务金卡", "招商银行信用卡中心·VISA 信用卡", + "招商银行信用卡中心·MASTER 信用卡", "招商银行信用卡中心·MASTER 信用金卡", + "招商银行信用卡中心·银联标准公务卡 (金卡)", "招商银行信用卡中心·VISA 信用卡", + "招商银行信用卡中心·银联标准财政公务卡", "招商银行信用卡中心·芯片 IC 信用卡", "招商银行信用卡中心·芯片 IC 信用卡", + "招商银行香港分行·香港一卡通", "兴业银行·兴业卡 (银联卡)", "兴业银行·兴业卡 (银联标准卡)", + "兴业银行·兴业自然人生理财卡", "兴业银行·兴业智能卡 (银联卡)", "兴业银行·兴业智能卡", + "兴业银行·visa 标准双币个人普卡", "兴业银行·VISA 商务普卡", "兴业银行·VISA 商务金卡", + "兴业银行·VISA 运动白金信用卡", "兴业银行·万事达信用卡 (银联卡)", "兴业银行·VISA 信用卡 (银联卡)", + "兴业银行·加菲猫信用卡", "兴业银行·个人白金卡", "兴业银行·银联信用卡 (银联卡)", "兴业银行·银联信用卡 (银联卡)", + "兴业银行·银联白金信用卡", "兴业银行·银联标准公务卡", "兴业银行·VISA 信用卡 (银联卡)", + "兴业银行·万事达信用卡 (银联卡)", "兴业银行·银联标准贷记普卡", "兴业银行·银联标准贷记金卡", + "兴业银行·银联标准贷记金卡", "兴业银行·银联标准贷记金卡", "兴业银行·兴业信用卡", "兴业银行·兴业信用卡", + "兴业银行·兴业信用卡", "兴业银行·银联标准贷记普卡", "兴业银行·银联标准贷记普卡", "兴业银行·兴业芯片普卡", + "兴业银行·兴业芯片金卡", "兴业银行·兴业芯片白金卡", "兴业银行·兴业芯片钻石卡", "浦东发展银行·浦发 JCB 金卡", + "浦东发展银行·浦发 JCB 白金卡", "浦东发展银行·信用卡 VISA 普通", "浦东发展银行·信用卡 VISA 金卡", + "浦东发展银行·浦发银行 VISA 年青卡", "浦东发展银行·VISA 白金信用卡", "浦东发展银行·浦发万事达白金卡", + "浦东发展银行·浦发 JCB 普卡", "浦东发展银行·浦发万事达金卡", "浦东发展银行·浦发万事达普卡", + "浦东发展银行·浦发单币卡", "浦东发展银行·浦发银联单币麦兜普卡", "浦东发展银行·东方轻松理财卡", + "浦东发展银行·东方 - 轻松理财卡普卡", "浦东发展银行·东方轻松理财卡", "浦东发展银行·东方轻松理财智业金卡", + "浦东发展银行·东方卡 (银联卡)", "浦东发展银行·东方卡 (银联卡)", "浦东发展银行·东方卡 (银联卡)", + "浦东发展银行·公务卡金卡", "浦东发展银行·公务卡普卡", "浦东发展银行·东方卡", "浦东发展银行·东方卡", + "浦东发展银行·浦发单币卡", "浦东发展银行·浦发联名信用卡", "浦东发展银行·浦发银联白金卡", + "浦东发展银行·轻松理财普卡", "浦东发展银行·移动联名卡", "浦东发展银行·轻松理财消贷易卡", + "浦东发展银行·轻松理财普卡 (复合卡)", "浦东发展银行·贷记卡", "浦东发展银行·贷记卡", + "浦东发展银行·东方借记卡 (复合卡)", "浦东发展银行·电子现金卡 (IC 卡)", "浦东发展银行·移动浦发联名卡", + "浦东发展银行·东方 - 标准准贷记卡", "浦东发展银行·轻松理财金卡 (复合卡)", "浦东发展银行·轻松理财白金卡 (复合卡)", + "浦东发展银行·轻松理财钻石卡 (复合卡)", "浦东发展银行·东方卡", "恒丰银行·九州 IC 卡", + "恒丰银行·九州借记卡 (银联卡)", "恒丰银行·九州借记卡 (银联卡)", "天津市商业银行·银联卡 (银联卡)", + "烟台商业银行·金通卡", "潍坊银行·鸢都卡 (银联卡)", "潍坊银行·鸳都卡 (银联卡)", "临沂商业银行·沂蒙卡 (银联卡)", + "临沂商业银行·沂蒙卡 (银联卡)", "日照市商业银行·黄海卡", "日照市商业银行·黄海卡 (银联卡)", "浙商银行·商卡", + "浙商银行·商卡", "渤海银行·浩瀚金卡", "渤海银行·渤海银行借记卡", "渤海银行·金融 IC 卡", + "渤海银行·渤海银行公司借记卡", "星展银行·星展银行借记卡", "星展银行·星展银行借记卡", "恒生银行·恒生通财卡", + "恒生银行·恒生优越通财卡", "新韩银行·新韩卡", "上海银行·慧通钻石卡", "上海银行·慧通金卡", + "上海银行·私人银行卡", "上海银行·综合保险卡", "上海银行·申卡社保副卡 (有折)", "上海银行·申卡社保副卡 (无折)", + "上海银行·白金 IC 借记卡", "上海银行·慧通白金卡 (配折)", "上海银行·慧通白金卡 (不配折)", + "上海银行·申卡 (银联卡)", "上海银行·申卡借记卡", "上海银行·银联申卡 (银联卡)", "上海银行·单位借记卡", + "上海银行·首发纪念版 IC 卡", "上海银行·申卡贷记卡", "上海银行·申卡贷记卡", "上海银行·J 分期付款信用卡", + "上海银行·申卡贷记卡", "上海银行·申卡贷记卡", "上海银行·上海申卡 IC", "上海银行·申卡贷记卡", + "上海银行·申卡贷记卡普通卡", "上海银行·申卡贷记卡金卡", "上海银行·万事达白金卡", "上海银行·万事达星运卡", + "上海银行·申卡贷记卡金卡", "上海银行·申卡贷记卡普通卡", "上海银行·安融卡", "上海银行·分期付款信用卡", + "上海银行·信用卡", "上海银行·个人公务卡", "上海银行·安融卡", "上海银行·上海银行银联白金卡", + "上海银行·贷记 IC 卡", "上海银行·中国旅游卡 (IC 普卡)", "上海银行·中国旅游卡 (IC 金卡)", + "上海银行·中国旅游卡 (IC 白金卡)", "上海银行·万事达钻石卡", "上海银行·淘宝 IC 普卡", "北京银行·京卡借记卡", + "北京银行·京卡 (银联卡)", "北京银行·京卡借记卡", "北京银行·京卡", "北京银行·京卡", "北京银行·借记 IC 卡", + "北京银行·京卡贵宾金卡", "北京银行·京卡贵宾白金卡", "吉林银行·君子兰一卡通 (银联卡)", + "吉林银行·君子兰卡 (银联卡)", "吉林银行·长白山金融 IC 卡", "吉林银行·信用卡", "吉林银行·信用卡", + "吉林银行·公务卡", "镇江市商业银行·金山灵通卡 (银联卡)", "镇江市商业银行·金山灵通卡 (银联卡)", + "宁波银行·银联标准卡", "宁波银行·汇通借记卡", "宁波银行·汇通卡 (银联卡)", "宁波银行·明州卡", + "宁波银行·汇通借记卡", "宁波银行·汇通国际卡银联双币卡", "宁波银行·汇通国际卡银联双币卡", "平安银行·新磁条借记卡", + "平安银行·平安银行 IC 借记卡", "平安银行·万事顺卡", "平安银行·平安银行借记卡", "平安银行·平安银行借记卡", + "平安银行·万事顺借记卡", "焦作市商业银行·月季借记卡 (银联卡)", "焦作市商业银行·月季城市通 (银联卡)", + "焦作市商业银行·中国旅游卡", "温州银行·金鹿卡", "汉口银行·九通卡 (银联卡)", "汉口银行·九通卡", + "汉口银行·借记卡", "汉口银行·借记卡", "盛京银行·玫瑰卡", "盛京银行·玫瑰 IC 卡", "盛京银行·玫瑰 IC 卡", + "盛京银行·玫瑰卡", "盛京银行·玫瑰卡", "盛京银行·玫瑰卡 (银联卡)", "盛京银行·玫瑰卡 (银联卡)", + "盛京银行·盛京银行公务卡", "洛阳银行·都市一卡通 (银联卡)", "洛阳银行·都市一卡通 (银联卡)", "洛阳银行·--", + "大连银行·北方明珠卡", "大连银行·人民币借记卡", "大连银行·金融 IC 借记卡", "大连银行·大连市社会保障卡", + "大连银行·借记 IC 卡", "大连银行·借记 IC 卡", "大连银行·大连市商业银行贷记卡", "大连银行·大连市商业银行贷记卡", + "大连银行·银联标准公务卡", "苏州市商业银行·姑苏卡", "杭州商业银行·西湖卡", "杭州商业银行·西湖卡", + "杭州商业银行·借记 IC 卡", "杭州商业银行·", "南京银行·梅花信用卡公务卡", "南京银行·梅花信用卡商务卡", + "南京银行·梅花贷记卡 (银联卡)", "南京银行·梅花借记卡 (银联卡)", "南京银行·白金卡", "南京银行·商务卡", + "东莞市商业银行·万顺通卡 (银联卡)", "东莞市商业银行·万顺通卡 (银联卡)", "东莞市商业银行·万顺通借记卡", + "东莞市商业银行·社会保障卡", "乌鲁木齐市商业银行·雪莲借记 IC 卡", "乌鲁木齐市商业银行·乌鲁木齐市公务卡", + "乌鲁木齐市商业银行·福农卡贷记卡", "乌鲁木齐市商业银行·福农卡准贷记卡", "乌鲁木齐市商业银行·雪莲准贷记卡", + "乌鲁木齐市商业银行·雪莲贷记卡 (银联卡)", "乌鲁木齐市商业银行·雪莲借记 IC 卡", + "乌鲁木齐市商业银行·雪莲借记卡 (银联卡)", "乌鲁木齐市商业银行·雪莲卡 (银联卡)", "绍兴银行·兰花 IC 借记卡", + "绍兴银行·社保 IC 借记卡", "绍兴银行·兰花公务卡", "成都商业银行·芙蓉锦程福农卡", "成都商业银行·芙蓉锦程天府通卡", + "成都商业银行·锦程卡 (银联卡)", "成都商业银行·锦程卡金卡", "成都商业银行·锦程卡定活一卡通金卡", + "成都商业银行·锦程卡定活一卡通", "成都商业银行·锦程力诚联名卡", "成都商业银行·锦程力诚联名卡", + "成都商业银行·锦程卡 (银联卡)", "抚顺银行·借记 IC 卡", "临商银行·借记卡", "宜昌市商业银行·三峡卡 (银联卡)", + "宜昌市商业银行·信用卡 (银联卡)", "葫芦岛市商业银行·一通卡", "葫芦岛市商业银行·一卡通 (银联卡)", + "天津市商业银行·津卡", "天津市商业银行·津卡贷记卡 (银联卡)", "天津市商业银行·贷记 IC 卡", "天津市商业银行·--", + "天津银行·商务卡", "宁夏银行·宁夏银行公务卡", "宁夏银行·宁夏银行福农贷记卡", "宁夏银行·如意卡 (银联卡)", + "宁夏银行·宁夏银行福农借记卡", "宁夏银行·如意借记卡", "宁夏银行·如意 IC 卡", "宁夏银行·宁夏银行如意借记卡", + "宁夏银行·中国旅游卡", "齐商银行·金达卡 (银联卡)", "齐商银行·金达借记卡 (银联卡)", "齐商银行·金达 IC 卡", + "徽商银行·黄山卡", "徽商银行·黄山卡", "徽商银行·借记卡", "徽商银行·徽商银行中国旅游卡 (安徽)", + "徽商银行合肥分行·黄山卡", "徽商银行芜湖分行·黄山卡 (银联卡)", "徽商银行马鞍山分行·黄山卡 (银联卡)", + "徽商银行淮北分行·黄山卡 (银联卡)", "徽商银行安庆分行·黄山卡 (银联卡)", "重庆银行·长江卡 (银联卡)", + "重庆银行·长江卡 (银联卡)", "重庆银行·长江卡", "重庆银行·借记 IC 卡", "哈尔滨银行·丁香一卡通 (银联卡)", + "哈尔滨银行·丁香借记卡 (银联卡)", "哈尔滨银行·丁香卡", "哈尔滨银行·福农借记卡", + "无锡市商业银行·太湖金保卡 (银联卡)", "丹东银行·借记 IC 卡", "丹东银行·丹东银行公务卡", "兰州银行·敦煌卡", + "南昌银行·金瑞卡 (银联卡)", "南昌银行·南昌银行借记卡", "南昌银行·金瑞卡", "晋商银行·晋龙一卡通", + "晋商银行·晋龙一卡通", "晋商银行·晋龙卡 (银联卡)", "青岛银行·金桥通卡", "青岛银行·金桥卡 (银联卡)", + "青岛银行·金桥卡 (银联卡)", "青岛银行·金桥卡", "青岛银行·借记 IC 卡", "吉林银行·雾凇卡 (银联卡)", + "吉林银行·雾凇卡 (银联卡)", "南通商业银行·金桥卡 (银联卡)", "南通商业银行·金桥卡 (银联卡)", + "日照银行·黄海卡、财富卡借记卡", "鞍山银行·千山卡 (银联卡)", "鞍山银行·千山卡 (银联卡)", "鞍山银行·千山卡", + "青海银行·三江银行卡 (银联卡)", "青海银行·三江卡", "台州银行·大唐贷记卡", "台州银行·大唐准贷记卡", + "台州银行·大唐卡 (银联卡)", "台州银行·大唐卡", "台州银行·借记卡", "台州银行·公务卡", + "泉州银行·海峡银联卡 (银联卡)", "泉州银行·海峡储蓄卡", "泉州银行·海峡银联卡 (银联卡)", "泉州银行·海峡卡", + "泉州银行·公务卡", "昆明商业银行·春城卡 (银联卡)", "昆明商业银行·春城卡 (银联卡)", + "昆明商业银行·富滇 IC 卡 (复合卡)", "阜新银行·借记 IC 卡", "嘉兴银行·南湖借记卡 (银联卡)", "廊坊银行·白金卡", + "廊坊银行·金卡", "廊坊银行·银星卡 (银联卡)", "廊坊银行·龙凤呈祥卡", "内蒙古银行·百灵卡 (银联卡)", + "内蒙古银行·成吉思汗卡", "湖州市商业银行·百合卡", "湖州市商业银行·", "沧州银行·狮城卡", + "南宁市商业银行·桂花卡 (银联卡)", "包商银行·雄鹰卡 (银联卡)", "包商银行·包头市商业银行借记卡", + "包商银行·雄鹰贷记卡", "包商银行·包商银行内蒙古自治区公务卡", "包商银行·贷记卡", "包商银行·借记卡", + "连云港市商业银行·金猴神通借记卡", "威海商业银行·通达卡 (银联卡)", "威海市商业银行·通达借记 IC 卡", + "攀枝花市商业银行·攀枝花卡 (银联卡)", "攀枝花市商业银行·攀枝花卡", "绵阳市商业银行·科技城卡 (银联卡)", + "泸州市商业银行·酒城卡 (银联卡)", "泸州市商业银行·酒城 IC 卡", "大同市商业银行·云冈卡 (银联卡)", + "三门峡银行·天鹅卡 (银联卡)", "广东南粤银行·南珠卡 (银联卡)", "张家口市商业银行·好运 IC 借记卡", + "桂林市商业银行·漓江卡 (银联卡)", "龙江银行·福农借记卡", "龙江银行·联名借记卡", "龙江银行·福农借记卡", + "龙江银行·龙江 IC 卡", "龙江银行·社会保障卡", "龙江银行·--", "江苏长江商业银行·长江卡", + "徐州市商业银行·彭城借记卡 (银联卡)", "南充市商业银行·借记 IC 卡", "南充市商业银行·熊猫团团卡", + "莱商银行·银联标准卡", "莱芜银行·金凤卡", "莱商银行·借记 IC 卡", "德阳银行·锦程卡定活一卡通", + "德阳银行·锦程卡定活一卡通金卡", "德阳银行·锦程卡定活一卡通", "唐山市商业银行·唐山市城通卡", + "曲靖市商业银行·珠江源卡", "曲靖市商业银行·珠江源 IC 卡", "温州银行·金鹿信用卡", "温州银行·金鹿信用卡", + "温州银行·金鹿公务卡", "温州银行·贷记 IC 卡", "汉口银行·汉口银行贷记卡", "汉口银行·汉口银行贷记卡", + "汉口银行·九通香港旅游贷记普卡", "汉口银行·九通香港旅游贷记金卡", "汉口银行·贷记卡", "汉口银行·九通公务卡", + "江苏银行·聚宝借记卡", "江苏银行·月季卡", "江苏银行·紫金卡", "江苏银行·绿扬卡 (银联卡)", + "江苏银行·月季卡 (银联卡)", "江苏银行·九州借记卡 (银联卡)", "江苏银行·月季卡 (银联卡)", + "江苏银行·聚宝惠民福农卡", "江苏银行·江苏银行聚宝 IC 借记卡", "江苏银行·聚宝 IC 借记卡 VIP 卡", + "长治市商业银行·长治商行银联晋龙卡", "承德市商业银行·热河卡", "承德银行·借记 IC 卡", "德州银行·长河借记卡", + "德州银行·--", "遵义市商业银行·社保卡", "遵义市商业银行·尊卡", "邯郸市商业银行·邯银卡", + "邯郸市商业银行·邯郸银行贵宾 IC 借记卡", "安顺市商业银行·黄果树福农卡", "安顺市商业银行·黄果树借记卡", + "江苏银行·紫金信用卡 (公务卡)", "江苏银行·紫金信用卡", "江苏银行·天翼联名信用卡", "平凉市商业银行·广成卡", + "玉溪市商业银行·红塔卡", "玉溪市商业银行·红塔卡", "浙江民泰商业银行·金融 IC 卡", "浙江民泰商业银行·民泰借记卡", + "浙江民泰商业银行·金融 IC 卡 C 卡", "浙江民泰商业银行·银联标准普卡金卡", "浙江民泰商业银行·商惠通", + "上饶市商业银行·三清山卡", "东营银行·胜利卡", "泰安市商业银行·岱宗卡", "泰安市商业银行·市民一卡通", + "浙江稠州商业银行·义卡", "浙江稠州商业银行·义卡借记 IC 卡", "浙江稠州商业银行·公务卡", "自贡市商业银行·借记 IC 卡", + "自贡市商业银行·锦程卡", "鄂尔多斯银行·天骄公务卡", "鹤壁银行·鹤卡", "许昌银行·连城卡", "铁岭银行·龙凤卡", + "乐山市商业银行·大福卡", "乐山市商业银行·--", "长安银行·长长卡", "长安银行·借记 IC 卡", + "重庆三峡银行·财富人生卡", "重庆三峡银行·借记卡", "石嘴山银行·麒麟借记卡", "石嘴山银行·麒麟借记卡", + "石嘴山银行·麒麟公务卡", "盘锦市商业银行·鹤卡", "盘锦市商业银行·盘锦市商业银行鹤卡", "平顶山银行·平顶山银行公务卡", + "朝阳银行·鑫鑫通卡", "朝阳银行·朝阳银行福农卡", "朝阳银行·红山卡", "宁波东海银行·绿叶卡", + "遂宁市商业银行·锦程卡", "遂宁是商业银行·金荷卡", "保定银行·直隶卡", "保定银行·直隶卡", + "凉山州商业银行·锦程卡", "凉山州商业银行·金凉山卡", "漯河银行·福卡", "漯河银行·福源卡", "漯河银行·福源公务卡", + "达州市商业银行·锦程卡", "新乡市商业银行·新卡", "晋中银行·九州方圆借记卡", "晋中银行·九州方圆卡", + "驻马店银行·驿站卡", "驻马店银行·驿站卡", "驻马店银行·公务卡", "衡水银行·金鼎卡", "衡水银行·借记 IC 卡", + "周口银行·如愿卡", "周口银行·公务卡", "阳泉市商业银行·金鼎卡", "阳泉市商业银行·金鼎卡", + "宜宾市商业银行·锦程卡", "宜宾市商业银行·借记 IC 卡", "库尔勒市商业银行·孔雀胡杨卡", "雅安市商业银行·锦城卡", + "雅安市商业银行·--", "安阳银行·安鼎卡", "信阳银行·信阳卡", "信阳银行·公务卡", "信阳银行·信阳卡", + "华融湘江银行·华融卡", "华融湘江银行·华融卡", "营口沿海银行·祥云借记卡", "景德镇商业银行·瓷都卡", + "哈密市商业银行·瓜香借记卡", "湖北银行·金牛卡", "湖北银行·汉江卡", "湖北银行·借记卡", "湖北银行·三峡卡", + "湖北银行·至尊卡", "湖北银行·金融 IC 卡", "西藏银行·借记 IC 卡", "新疆汇和银行·汇和卡", "广东华兴银行·借记卡", + "广东华兴银行·华兴银联公司卡", "广东华兴银行·华兴联名 IC 卡", "广东华兴银行·华兴金融 IC 借记卡", "濮阳银行·龙翔卡", + "宁波通商银行·借记卡", "甘肃银行·神舟兴陇借记卡", "甘肃银行·甘肃银行神州兴陇 IC 卡", "枣庄银行·借记 IC 卡", + "本溪市商业银行·借记卡", "盛京银行·医保卡", "上海农商银行·如意卡 (银联卡)", "上海农商银行·如意卡 (银联卡)", + "上海农商银行·鑫通卡", "上海农商银行·国际如意卡", "上海农商银行·借记 IC 卡", + "常熟市农村商业银行·粒金贷记卡 (银联卡)", "常熟市农村商业银行·公务卡", "常熟市农村商业银行·粒金准贷卡", + "常熟农村商业银行·粒金借记卡 (银联卡)", "常熟农村商业银行·粒金 IC 卡", "常熟农村商业银行·粒金卡", + "深圳农村商业银行·信通卡 (银联卡)", "深圳农村商业银行·信通商务卡 (银联卡)", "深圳农村商业银行·信通卡", + "深圳农村商业银行·信通商务卡", "广州农村商业银行·福农太阳卡", "广东南海农村商业银行·盛通卡", + "广东南海农村商业银行·盛通卡 (银联卡)", "佛山顺德农村商业银行·恒通卡 (银联卡)", "佛山顺德农村商业银行·恒通卡", + "佛山顺德农村商业银行·恒通卡 (银联卡)", "江阴农村商业银行·暨阳公务卡", "江阴市农村商业银行·合作贷记卡 (银联卡)", + "江阴农村商业银行·合作借记卡", "江阴农村商业银行·合作卡 (银联卡)", "江阴农村商业银行·暨阳卡", + "重庆农村商业银行·江渝借记卡 VIP 卡", "重庆农村商业银行·江渝 IC 借记卡", "重庆农村商业银行·江渝乡情福农卡", + "东莞农村商业银行·信通卡 (银联卡)", "东莞农村商业银行·信通卡 (银联卡)", "东莞农村商业银行·信通信用卡", + "东莞农村商业银行·信通借记卡", "东莞农村商业银行·贷记 IC 卡", "张家港农村商业银行·一卡通 (银联卡)", + "张家港农村商业银行·一卡通 (银联卡)", "张家港农村商业银行·", "北京农村商业银行·信通卡", "北京农村商业银行·惠通卡", + "北京农村商业银行·凤凰福农卡", "北京农村商业银行·惠通卡", "北京农村商业银行·中国旅行卡", "北京农村商业银行·凤凰卡", + "天津农村商业银行·吉祥商联 IC 卡", "天津农村商业银行·信通借记卡 (银联卡)", "天津农村商业银行·借记 IC 卡", + "鄞州农村合作银行·蜜蜂借记卡 (银联卡)", "宁波鄞州农村合作银行·蜜蜂电子钱包 (IC)", + "宁波鄞州农村合作银行·蜜蜂 IC 借记卡", "宁波鄞州农村合作银行·蜜蜂贷记 IC 卡", "宁波鄞州农村合作银行·蜜蜂贷记卡", + "宁波鄞州农村合作银行·公务卡", "成都农村商业银行·福农卡", "成都农村商业银行·福农卡", + "珠海农村商业银行·信通卡 (银联卡)", "太仓农村商业银行·郑和卡 (银联卡)", "太仓农村商业银行·郑和 IC 借记卡", + "无锡农村商业银行·金阿福", "无锡农村商业银行·借记 IC 卡", "黄河农村商业银行·黄河卡", + "黄河农村商业银行·黄河富农卡福农卡", "黄河农村商业银行·借记 IC 卡", "天津滨海农村商业银行·四海通卡", + "天津滨海农村商业银行·四海通 e 芯卡", "武汉农村商业银行·汉卡", "武汉农村商业银行·汉卡", + "武汉农村商业银行·中国旅游卡", "江南农村商业银行·阳湖卡 (银联卡)", "江南农村商业银行·天天红火卡", + "江南农村商业银行·借记 IC 卡", "海口联合农村商业银行·海口联合农村商业银行合卡", "湖北嘉鱼吴江村镇银行·垂虹卡", + "福建建瓯石狮村镇银行·玉竹卡", "浙江平湖工银村镇银行·金平卡", "重庆璧山工银村镇银行·翡翠卡", + "重庆农村商业银行·银联标准贷记卡", "重庆农村商业银行·公务卡", "南阳村镇银行·玉都卡", + "晋中市榆次融信村镇银行·魏榆卡", "三水珠江村镇银行·珠江太阳卡", "东营莱商村镇银行·绿洲卡", "建设银行·单位结算卡", + "玉溪市商业银行·红塔卡"}; +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/validator/IDCardUtils.java b/lib/DevApp/src/main/java/dev/utils/common/validator/IDCardUtils.java new file mode 100644 index 0000000000..f740bdc3f4 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/validator/IDCardUtils.java @@ -0,0 +1,660 @@ +package dev.utils.common.validator; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: 居民身份证工具类 + * @author AbrahamCaiJin + * @author Ttt ( 重写、规范注释、逻辑判断等 ) + */ +public final class IDCardUtils { + + private IDCardUtils() { + } + + // 日志 TAG + private static final String TAG = IDCardUtils.class.getSimpleName(); + // 加权因子 + private static final int[] POWER = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; + // 身份证最少位数 + public static final int CHINA_ID_MIN_LENGTH = 15; + // 身份证最大位数 + public static final int CHINA_ID_MAX_LENGTH = 18; + // 省份编码 + private static Map sCityCodeMaps = new HashMap<>(); + // 台湾身份首字母对应数字 + private static Map sTWFirstCodeMaps = new HashMap<>(); + // 香港身份首字母对应数字 + private static Map sHKFirstCodeMaps = new HashMap<>(); + + static { + sCityCodeMaps.put("11", "北京"); + sCityCodeMaps.put("12", "天津"); + sCityCodeMaps.put("13", "河北"); + sCityCodeMaps.put("14", "山西"); + sCityCodeMaps.put("15", "内蒙古"); + sCityCodeMaps.put("21", "辽宁"); + sCityCodeMaps.put("22", "吉林"); + sCityCodeMaps.put("23", "黑龙江"); + sCityCodeMaps.put("31", "上海"); + sCityCodeMaps.put("32", "江苏"); + sCityCodeMaps.put("33", "浙江"); + sCityCodeMaps.put("34", "安徽"); + sCityCodeMaps.put("35", "福建"); + sCityCodeMaps.put("36", "江西"); + sCityCodeMaps.put("37", "山东"); + sCityCodeMaps.put("41", "河南"); + sCityCodeMaps.put("42", "湖北"); + sCityCodeMaps.put("43", "湖南"); + sCityCodeMaps.put("44", "广东"); + sCityCodeMaps.put("45", "广西"); + sCityCodeMaps.put("46", "海南"); + sCityCodeMaps.put("50", "重庆"); + sCityCodeMaps.put("51", "四川"); + sCityCodeMaps.put("52", "贵州"); + sCityCodeMaps.put("53", "云南"); + sCityCodeMaps.put("54", "西藏"); + sCityCodeMaps.put("61", "陕西"); + sCityCodeMaps.put("62", "甘肃"); + sCityCodeMaps.put("63", "青海"); + sCityCodeMaps.put("64", "宁夏"); + sCityCodeMaps.put("65", "新疆"); + sCityCodeMaps.put("71", "台湾"); + sCityCodeMaps.put("81", "香港"); + sCityCodeMaps.put("82", "澳门"); + sCityCodeMaps.put("91", "国外"); + sTWFirstCodeMaps.put("A", 10); + sTWFirstCodeMaps.put("B", 11); + sTWFirstCodeMaps.put("C", 12); + sTWFirstCodeMaps.put("D", 13); + sTWFirstCodeMaps.put("E", 14); + sTWFirstCodeMaps.put("F", 15); + sTWFirstCodeMaps.put("G", 16); + sTWFirstCodeMaps.put("H", 17); + sTWFirstCodeMaps.put("J", 18); + sTWFirstCodeMaps.put("K", 19); + sTWFirstCodeMaps.put("L", 20); + sTWFirstCodeMaps.put("M", 21); + sTWFirstCodeMaps.put("N", 22); + sTWFirstCodeMaps.put("P", 23); + sTWFirstCodeMaps.put("Q", 24); + sTWFirstCodeMaps.put("R", 25); + sTWFirstCodeMaps.put("S", 26); + sTWFirstCodeMaps.put("T", 27); + sTWFirstCodeMaps.put("U", 28); + sTWFirstCodeMaps.put("V", 29); + sTWFirstCodeMaps.put("X", 30); + sTWFirstCodeMaps.put("Y", 31); + sTWFirstCodeMaps.put("W", 32); + sTWFirstCodeMaps.put("Z", 33); + sTWFirstCodeMaps.put("I", 34); + sTWFirstCodeMaps.put("O", 35); + sHKFirstCodeMaps.put("A", 1); + sHKFirstCodeMaps.put("B", 2); + sHKFirstCodeMaps.put("C", 3); + sHKFirstCodeMaps.put("R", 18); + sHKFirstCodeMaps.put("U", 21); + sHKFirstCodeMaps.put("Z", 26); + sHKFirstCodeMaps.put("X", 24); + sHKFirstCodeMaps.put("W", 23); + sHKFirstCodeMaps.put("O", 15); + sHKFirstCodeMaps.put("N", 14); + } + + /** + * 身份证校验规则, 验证 15 位身份编码是否合法 + * @param idCard 待验证身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean validateIdCard15(final String idCard) { + // 属于数字, 并且长度为 15 位数 + if (isNumber(idCard) && idCard.length() == CHINA_ID_MIN_LENGTH) { + // 获取省份编码 + String provinceCode = idCard.substring(0, 2); + if (sCityCodeMaps.get(provinceCode) == null) return false; + // 获取出生日期 + String birthCode = idCard.substring(6, 12); + Date birthDate = null; + try { + birthDate = new SimpleDateFormat("yy").parse(birthCode.substring(0, 2)); + } catch (ParseException e) { + JCLogUtils.eTag(TAG, e, "validateIdCard15"); + } + Calendar calendar = Calendar.getInstance(); + if (birthDate != null) calendar.setTime(birthDate); + // 判断是否有效日期 + return validateDateSmllerThenNow(calendar.get(Calendar.YEAR), + Integer.valueOf(birthCode.substring(2, 4)), + Integer.valueOf(birthCode.substring(4, 6))); + } + return false; + } + + /** + * 身份证校验规则, 验证 18 位身份编码是否合法 + * @param idCard 待验证身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean validateIdCard18(final String idCard) { + if (idCard != null && idCard.length() == CHINA_ID_MAX_LENGTH) { + // 前 17 位 + String code17 = idCard.substring(0, 17); + // 第 18 位 + String code18 = idCard.substring(17, CHINA_ID_MAX_LENGTH); + // 判断前 17 位是否数字 + if (isNumber(code17)) { + try { + int[] cardArys = converCharToInt(code17.toCharArray()); + int sum17 = getPowerSum(cardArys); + // 获取校验位 + String str = getCheckCode18(sum17); + // 判断最后一位是否一样 + if (str.length() > 0 && str.equalsIgnoreCase(code18)) { + return true; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "validateIdCard18"); + } + } + } + return false; + } + + /** + * 将 15 位身份证号码转换为 18 位 + * @param idCard 15 位身份编码 + * @return 18 位身份编码 + */ + public static String convert15CardTo18(final String idCard) { + // 属于数字, 并且长度为 15 位数 + if (isNumber(idCard) && idCard.length() == CHINA_ID_MIN_LENGTH) { + String idCard18; + Date birthDate = null; + // 获取出生日期 + String birthday = idCard.substring(6, 12); + try { + birthDate = new SimpleDateFormat("yyMMdd").parse(birthday); + } catch (ParseException e) { + JCLogUtils.eTag(TAG, e, "convert15CardTo18"); + } + Calendar calendar = Calendar.getInstance(); + if (birthDate != null) calendar.setTime(birthDate); + try { + // 获取出生年 ( 完全表现形式, 如: 2010) + String year = String.valueOf(calendar.get(Calendar.YEAR)); + // 保存省市区信息 + 年 + 月日 + 后续信息 ( 顺序位、性别等 ) + idCard18 = idCard.substring(0, 6) + year + idCard.substring(8); + // 转换字符数组 + int[] cardArys = converCharToInt(idCard18.toCharArray()); + int sum17 = getPowerSum(cardArys); + // 获取校验位 + String str = getCheckCode18(sum17); + // 判断长度, 拼接校验位 + return (str.length() > 0) ? (idCard18 + str) : null; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "convert15CardTo18"); + } + } + return null; + } + + /** + * 验证台湾身份证号码 + * @param idCard 身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean validateTWCard(final String idCard) { + // 台湾身份证 10 位 + if (idCard == null || idCard.length() != 10) return false; + try { + // 第一位英文 不同县市 + String start = idCard.substring(0, 1); + String mid = idCard.substring(1, 9); + String end = idCard.substring(9, 10); + Integer iStart = sTWFirstCodeMaps.get(start); + Integer sum = iStart / 10 + (iStart % 10) * 9; + char[] chars = mid.toCharArray(); + Integer iflag = 8; + for (char c : chars) { + sum = sum + Integer.valueOf(c + "") * iflag; + iflag--; + } + return (sum % 10 == 0 ? 0 : 10 - sum % 10) == Integer.valueOf(end); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "validateTWCard"); + } + return false; + } + + /** + * 验证香港身份证号码 ( 部份特殊身份证无法检查 ) + * 身份证前 2 位为英文字符, 如果只出现一个英文字符则表示第一位是空格, 对应数字 58 前 2 位英文字符 A-Z 分别对应数字 10-35 + * 最后一位校验码为 0-9 的数字加上字符 "A", "A" 代表 10 + * 将身份证号码全部转换为数字, 分别对应乘 9-1 相加的总和, 整除 11 则证件号码有效 + * @param idCard 身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean validateHKCard(final String idCard) { + if (isEmpty(idCard)) return false; + try { + String card = idCard.replaceAll("[\\(|\\)]", ""); + Integer sum = 0; + if (card.length() == 9) { + sum = ((int) card.substring(0, 1).toUpperCase().toCharArray()[0] - 55) * 9 + ((int) card.substring(1, 2).toUpperCase().toCharArray()[0] - 55) * 8; + card = card.substring(1, 9); + } else { + sum = 522 + ((int) card.substring(0, 1).toUpperCase().toCharArray()[0] - 55) * 8; + } + String mid = card.substring(1, 7); + String end = card.substring(7, 8); + char[] chars = mid.toCharArray(); + Integer iflag = 7; + for (char c : chars) { + sum = sum + Integer.valueOf(c + "") * iflag; + iflag--; + } + if (end.toUpperCase().equals("A")) { + sum = sum + 10; + } else { + sum = sum + Integer.valueOf(end); + } + return (sum % 11 == 0); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "validateHKCard"); + } + return false; + } + + /** + * 判断 10 位数的身份证号, 是否合法 + * @param idCard 身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static String[] validateIdCard10(final String idCard) { + if (isEmpty(idCard)) return null; + String[] info = new String[3]; + info[0] = "N"; // 默认未知地区 + info[1] = "N"; // 默认未知性别 + info[2] = "false"; // 默认非法 + try { + String card = idCard.replaceAll("[\\(|\\)]", ""); + // 获取身份证长度 + int cardLength = card.length(); + // 属于 8, 9, 10 长度范围内 + if (cardLength >= 8 || cardLength <= 10) { + if (idCard.matches("^[a-zA-Z][0-9]{9}$")) { // 台湾 + info[0] = "台湾"; + String char2 = idCard.substring(1, 2); + if (char2.equals("1")) { + info[1] = "M"; + } else if (char2.equals("2")) { + info[1] = "F"; + } else { + info[1] = "N"; + info[2] = "false"; + return info; + } + info[2] = validateTWCard(idCard) ? "true" : "false"; + } else if (idCard.matches("^[1|5|7][0-9]{6}\\(?[0-9A-Z]\\)?$")) { // 澳门 + info[0] = "澳门"; + info[1] = "N"; + // TODO + } else if (idCard.matches("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?$")) { // 香港 + info[0] = "香港"; + info[1] = "N"; + info[2] = validateHKCard(idCard) ? "true" : "false"; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "validateIdCard10"); + } + return info; + } + + /** + * 验证身份证是否合法 + * @param idCard 身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean validateCard(final String idCard) { + if (isEmpty(idCard)) return false; + String card = idCard.trim(); + if (validateIdCard18(card)) return true; + if (validateIdCard15(card)) return true; + String[] cardStrs = validateIdCard10(card); + return (cardStrs != null && "true".equals(cardStrs[2])); + } + + /** + * 根据身份编号获取年龄 + * @param idCard 身份编号 + * @return 年龄 + */ + public static int getAgeByIdCard(final String idCard) { + if (isEmpty(idCard)) return 0; + try { + String idCardStr = idCard; + // 属于 15 位身份证, 则转换为 18 位 + if (idCardStr.length() == CHINA_ID_MIN_LENGTH) { + idCardStr = convert15CardTo18(idCard); + } + // 属于 18 位身份证才处理 + if (idCardStr.length() == CHINA_ID_MAX_LENGTH) { + String year = idCardStr.substring(6, 10); + // 获取当前年份 + int currentYear = Calendar.getInstance().get(Calendar.YEAR); + // 当前年份 - 出生年份 + return currentYear - Integer.valueOf(year); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAgeByIdCard"); + } + return 0; + } + + /** + * 根据身份编号获取生日 + * @param idCard 身份编号 + * @return 生日 (yyyyMMdd) + */ + public static String getBirthByIdCard(final String idCard) { + if (isEmpty(idCard)) return null; + try { + String idCardStr = idCard; + // 属于 15 位身份证, 则转换为 18 位 + if (idCardStr.length() == CHINA_ID_MIN_LENGTH) { + idCardStr = convert15CardTo18(idCard); + } + // 属于 18 位身份证才处理 + if (idCardStr.length() == CHINA_ID_MAX_LENGTH) { + return idCardStr.substring(6, 14); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getBirthByIdCard"); + } + return null; + } + + /** + * 根据身份编号获取生日 + * @param idCard 身份编号 + * @return 生日 (yyyyMMdd) + */ + public static String getBirthdayByIdCard(final String idCard) { + // 获取生日 + String birth = getBirthByIdCard(idCard); + // 进行处理 + if (birth != null) { + try { + return birth.replaceAll("(\\d{4})(\\d{2})(\\d{2})", "$1-$2-$3"); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getBirthdayByIdCard"); + } + } + return null; + } + + /** + * 根据身份编号获取生日 ( 年份 ) + * @param idCard 身份编号 + * @return 生日 (yyyy) + */ + public static String getYearByIdCard(final String idCard) { + // 获取生日 + String birth = getBirthByIdCard(idCard); + // 进行处理 + if (birth != null) { + try { + return birth.substring(0, 4); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getYearByIdCard"); + } + } + return null; + } + + /** + * 根据身份编号获取生日 ( 月份 ) + * @param idCard 身份编号 + * @return 生日 (MM) + */ + public static String getMonthByIdCard(final String idCard) { + // 获取生日 + String birth = getBirthByIdCard(idCard); + // 进行处理 + if (birth != null) { + try { + return birth.substring(4, 6); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getMonthByIdCard"); + } + } + return null; + } + + /** + * 根据身份编号获取生日 ( 天数 ) + * @param idCard 身份编号 + * @return 生日 (dd) + */ + public static String getDateByIdCard(final String idCard) { + // 获取生日 + String birth = getBirthByIdCard(idCard); + // 进行处理 + if (birth != null) { + try { + return birth.substring(6, 8); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDateByIdCard"); + } + } + return null; + } + + /** + * 根据身份编号获取性别 + * @param idCard 身份编号 + * @return 性别 男 (M)、女 (F)、未知 (N) + */ + public static String getGenderByIdCard(final String idCard) { + if (isEmpty(idCard)) return null; + try { + String idCardStr = idCard; + // 属于 15 位身份证, 则转换为 18 位 + if (idCardStr.length() == CHINA_ID_MIN_LENGTH) { + idCardStr = convert15CardTo18(idCard); + } + // 属于 18 位身份证才处理 + if (idCardStr.length() == CHINA_ID_MAX_LENGTH) { + // 获取第 17 位性别信息 + String cardNumber = idCardStr.substring(16, 17); + // 奇数为男, 偶数为女 + return (Integer.parseInt(cardNumber) % 2 == 0) ? "F" : "M"; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getGenderByIdCard"); + } + // 默认未知 + return "N"; + } + + /** + * 根据身份编号获取户籍省份 + * @param idCard 身份编码 + * @return 省级编码 + */ + public static String getProvinceByIdCard(final String idCard) { + if (isEmpty(idCard)) return null; + try { + // 身份证长度 + int idCardLength = idCard.length(); + // 属于 15 位身份证、或 18 位身份证 + if (idCardLength == CHINA_ID_MIN_LENGTH || idCardLength == CHINA_ID_MAX_LENGTH) { + return sCityCodeMaps.get(idCard.substring(0, 2)); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getProvinceByIdCard"); + } + return null; + } + + /** + * 将身份证的每位和对应位的加权因子相乘之后, 再获取和值 + * @param data byte[] 数据 + * @return 身份证编码, 加权引子 + */ + public static int getPowerSum(final int[] data) { + if (data == null) return 0; + int len = data.length; + if (len == 0) return 0; + int powerLength = POWER.length; + int sum = 0; + if (powerLength == len) { + for (int i = 0; i < len; i++) { + for (int j = 0; j < powerLength; j++) { + if (i == j) { + sum = sum + data[i] * POWER[j]; + } + } + } + } + return sum; + } + + /** + * 将 POWER 和值与 11 取模获取余数进行校验码判断 + * @param sum {@link IDCardUtils#getPowerSum} + * @return 校验位 + */ + public static String getCheckCode18(final int sum) { + String code = ""; + switch (sum % 11) { + case 10: + code = "2"; + break; + case 9: + code = "3"; + break; + case 8: + code = "4"; + break; + case 7: + code = "5"; + break; + case 6: + code = "6"; + break; + case 5: + code = "7"; + break; + case 4: + code = "8"; + break; + case 3: + code = "9"; + break; + case 2: + code = "x"; + break; + case 1: + code = "0"; + break; + case 0: + code = "1"; + break; + } + return code; + } + + // ============ + // = 私有方法 = + // ============ + + /** + * 将字符数组转换成数字数组 + * @param data char[] + * @return int[] + */ + private static int[] converCharToInt(final char[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + try { + int[] arrays = new int[len]; + for (int i = 0; i < len; i++) { + arrays[i] = Integer.parseInt(String.valueOf(data[i])); + } + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "converCharToInt"); + } + return null; + } + + /** + * 验证小于当前日期 是否有效 + * @param yearData 待校验的日期 ( 年 ) + * @param monthData 待校验的日期 ( 月 1-12) + * @param dayData 待校验的日期 ( 日 ) + * @return {@code true} yes, {@code false} no + */ + private static boolean validateDateSmllerThenNow(final int yearData, final int monthData, final int dayData) { + int year = Calendar.getInstance().get(Calendar.YEAR); + int datePerMonth; + int MIN = 1930; + if (yearData < MIN || yearData >= year) { + return false; + } + if (monthData < 1 || monthData > 12) { + return false; + } + switch (monthData) { + case 4: + case 6: + case 9: + case 11: + datePerMonth = 30; + break; + case 2: + boolean dm = ((yearData % 4 == 0 && yearData % 100 != 0) || (yearData % 400 == 0)) && (yearData > MIN && yearData < year); + datePerMonth = dm ? 29 : 28; + break; + default: + datePerMonth = 31; + } + return (dayData >= 1) && (dayData <= datePerMonth); + } + + /** + * 检验数字 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isNumber(final String str) { + return !isEmpty(str) && str.matches("^[0-9]*$"); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java b/lib/DevApp/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java new file mode 100644 index 0000000000..5f53c67399 --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java @@ -0,0 +1,236 @@ +package dev.utils.common.validator; + +import java.util.regex.Pattern; + +import dev.utils.JCLogUtils; + +/** + * detail: 检验联系 ( 手机号, 座机 ) 工具类 + * @author Ttt + *
+ *     @see 
+ *     

+ * 验证手机号是否正确 + * 移动: 134 135 136 137 138 139 147 148 150 151 152 157 158 159 165 172 178 182 183 184 187 188 198 + * 联通: 130 131 132 145 146 155 156 166 171 175 176 185 186 + * 电信: 133 149 153 173 174 177 180 181 189 191 199 + * 卫星通信: 1349 + * 虚拟运营商: 170 + *
+ */ +public final class ValiToPhoneUtils { + + private ValiToPhoneUtils() { + } + + // 日志 TAG + private static final String TAG = ValiToPhoneUtils.class.getSimpleName(); + + /** + * 中国手机号格式验证, 在输入可以调用该方法, 点击发送验证码, 使用 isPhone + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneCheck(final String phone) { + return match(CHAIN_PHONE_FORMAT_CHECK, phone); + } + + /** + * 是否中国手机号 + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhone(final String phone) { + return match(CHINA_PHONE_PATTERN, phone); + } + + /** + * 是否中国电信手机号码 + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneToChinaTelecom(final String phone) { + return match(CHINA_TELECOM_PATTERN, phone); + } + + /** + * 是否中国联通手机号码 + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneToChinaUnicom(final String phone) { + return match(CHINA_UNICOM_PATTERN, phone); + } + + /** + * 是否中国移动手机号码 + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneToChinaMobile(final String phone) { + return match(CHINA_MOBILE_PATTERN, phone); + } + + /** + * 判断是否香港手机号 + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneToHkMobile(final String phone) { + return match(HK_PHONE_PATTERN, phone); + } + + /** + * 验证电话号码的格式 + * @param phone 待校验的号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneCallNum(final String phone) { + return match(PHONE_CALL_PATTERN, phone); + } + + // ============== + // = 手机号判断 = + // ============== + + // 简单手机号码校验 校验手机号码的长度和 1 开头 ( 是否 11 位 ) + public static final String CHAIN_PHONE_FORMAT_CHECK = "^(?:\\+86)?1\\d{10}$"; + + // 中国手机号正则 + public static final String CHINA_PHONE_PATTERN; + + // 中国电信号码正则 + public static final String CHINA_TELECOM_PATTERN; + + // 中国联通号码正则 + public static final String CHINA_UNICOM_PATTERN; + + // 中国移动号码正则 + public static final String CHINA_MOBILE_PATTERN; + + // 香港手机号码正则 香港手机号码 8 位数, 5|6|8|9 开头 + 7 位任意数 + public static final String HK_PHONE_PATTERN = "^(5|6|8|9)\\d{7}$"; + + // ============ + // = 座机判断 = + // ============ + + // 座机电话格式验证 + public static final String PHONE_CALL_PATTERN = "^(?:\\(\\d{3,4}\\)|\\d{3,4}-)?\\d{7,8}(?:-\\d{1,4})?$"; + + static { + + // ============ + // = 中国电信 = + // ============ + + // 电信: 133 149 153 173 174 177 180 181 189 191 199 + // 进行拼接字符串, 便于理解 + StringBuilder builder = new StringBuilder(); + builder.append("^13[3]{1}\\d{8}$"); // 13 开头 + builder.append("|"); // 或 + builder.append("^14[9]{1}\\d{8}$"); // 14 开头 + builder.append("|"); + builder.append("^15[3]{1}\\d{8}$"); // 15 开头 + builder.append("|"); + builder.append("^17[3,4,7]{1}\\d{8}$"); // 17 开头 + builder.append("|"); + builder.append("^18[0,1,9]{1}\\d{8}$"); // 18 开头 + builder.append("|"); + builder.append("^19[1,9]{1}\\d{8}$"); // 19 开头 + // 手机正则 + CHINA_TELECOM_PATTERN = builder.toString(); + + // ============ + // = 中国联通 = + // ============ + + // 联通: 130 131 132 145 146 155 156 166 171 175 176 185 186 + // 进行拼接字符串, 便于理解 + builder = new StringBuilder(); + builder.append("^13[0,1,2]{1}\\d{8}$"); // 13 开头 + builder.append("|"); // 或 + builder.append("^14[5,6]{1}\\d{8}$"); // 14 开头 + builder.append("|"); + builder.append("^15[5,6]{1}\\d{8}$"); // 15 开头 + builder.append("|"); + builder.append("^16[6]{1}\\d{8}$"); // 16 开头 + builder.append("|"); + builder.append("^17[1,5,6]{1}\\d{8}$"); // 17 开头 + builder.append("|"); + builder.append("^18[5,6]{1}\\d{8}$"); // 18 开头 + // 手机正则 + CHINA_UNICOM_PATTERN = builder.toString(); + + // ============ + // = 中国移动 = + // ============ + + // 移动: 134 135 136 137 138 139 147 148 150 151 152 157 158 159 165 172 178 182 183 184 187 188 198 + // 进行拼接字符串, 便于理解 + builder = new StringBuilder(); + builder.append("^13[4,5,6,7,8,9]{1}\\d{8}$"); // 13 开头 + builder.append("|"); // 或 + builder.append("^14[7,8]{1}\\d{8}$"); // 14 开头 + builder.append("|"); + builder.append("^15[0,1,2,7,8,9]{1}\\d{8}$"); // 15 开头 + builder.append("|"); + builder.append("^16[5]{1}\\d{8}$"); // 16 开头 + builder.append("|"); + builder.append("^17[2,8]{1}\\d{8}$"); // 17 开头 + builder.append("|"); + builder.append("^18[2,3,4,7,8]{1}\\d{8}$"); // 18 开头 + builder.append("|"); + builder.append("^19[8]{1}\\d{8}$"); // 19 开头 + // 手机正则 + CHINA_MOBILE_PATTERN = builder.toString(); + + /** + * 验证手机号是否正确 + * 移动: 134 135 136 137 138 139 147 148 150 151 152 157 158 159 165 172 178 182 183 184 187 188 198 + * 联通: 130 131 132 145 146 155 156 166 171 175 176 185 186 + * 电信: 133 149 153 173 174 177 180 181 189 191 199 + * 卫星通信: 1349 + * 虚拟运营商: 170 + */ + CHINA_PHONE_PATTERN = "^13[\\d]{9}$|^14[5,6,7,8,9]{1}\\d{8}$|^15[^4]{1}\\d{8}$|^16[5,6]{1}\\d{8}$|^17[0,1,2,3,4,5,6,7,8]{1}\\d{8}$|^18[\\d]{9}$|^19[1,8,9]{1}\\d{8}$"; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + // ================== + // = ValidatorUtils = + // ================== + + /** + * 通用匹配函数 + * @param regex 正则表达式 + * @param input 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean match(final String regex, final String input) { + if (!isEmpty(input)) { + try { + return Pattern.matches(regex, input); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "match"); + } + } + return false; + } +} \ No newline at end of file diff --git a/lib/DevApp/src/main/java/dev/utils/common/validator/ValidatorUtils.java b/lib/DevApp/src/main/java/dev/utils/common/validator/ValidatorUtils.java new file mode 100644 index 0000000000..2ea3a9f38e --- /dev/null +++ b/lib/DevApp/src/main/java/dev/utils/common/validator/ValidatorUtils.java @@ -0,0 +1,284 @@ +package dev.utils.common.validator; + +import java.util.regex.Pattern; + +import dev.utils.JCLogUtils; + +/** + * detail: 校验工具类 + * @author Ttt + */ +public final class ValidatorUtils { + + private ValidatorUtils() { + } + + // 日志 TAG + private static final String TAG = ValidatorUtils.class.getSimpleName(); + + // 正则表达式: 空格 + public static final String REGEX_SPACE = "\\s"; + + // 正则表达式: 验证数字 + public static final String REGEX_NUMBER = "^[0-9]*$"; + + // 正则表达式: 验证数字或包含小数点 + public static final String REGEX_NUMBER_OR_DECIMAL = "^[0-9]*[.]?[0-9]*$"; + + // 正则表达式: 验证是否包含数字 + public static final String REGEX_CONTAIN_NUMBER = ".*\\d+.*"; + + // 正则表达式: 验证是否数字或者字母 + public static final String REGEX_NUMBER_OR_LETTER = "^[A-Za-z0-9]+$"; + + // 正则表达式: 验证是否全是字母 + public static final String REGEX_LETTER = "^[A-Za-z]+$"; + + // 正则表达式: 不能输入特殊字符 ^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$ + public static final String REGEX_SPECIAL = "^[\\u4E00-\\u9FA5A-Za-z0-9]+$"; + + // 正则表达式: 验证微信号 ^[a-zA-Z]{1}[-_a-zA-Z0-9]{5,19}+$ + public static final String REGEX_WX = "^[a-zA-Z]{1}[-_a-zA-Z0-9]{5,19}+$"; + + // 正则表达式: 验证真实姓名 ^[\u4e00-\u9fa5]+(·[\u4e00-\u9fa5]+)*$ + public static final String REGEX_REALNAME = "^[\\u4e00-\\u9fa5]+(•[\\u4e00-\\u9fa5]*)*$|^[\\u4e00-\\u9fa5]+(·[\\u4e00-\\u9fa5]*)*$"; + + // 正则表达式: 验证昵称 + public static final String REGEX_NICKNAME = "^[\\u4E00-\\u9FA5A-Za-z0-9_]+$"; + + // 正则表达式: 验证用户名 ( 不包含中文和特殊字符 ) 如果用户名使用手机号码或邮箱 则结合手机号验证和邮箱验证 + public static final String REGEX_USERNAME = "^[a-zA-Z]\\w{5,17}$"; + + // 正则表达式: 验证密码 ( 不包含特殊字符 ) + public static final String REGEX_PASSWORD = "^[a-zA-Z0-9]{6,18}$"; + + // 正则表达式: 验证邮箱 + public static final String REGEX_EMAIL = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; + + // 正则表达式: 验证 URL + public static final String REGEX_URL = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?"; + + // 正则表达式: 验证 IP 地址 + public static final String REGEX_IP_ADDR = "(2[5][0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})"; + + /** + * 通用匹配函数 + * @param regex 正则表达式 + * @param input 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean match(final String regex, final String input) { + if (!isEmpty(input)) { + try { + return Pattern.matches(regex, input); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "match"); + } + } + return false; + } + + // = + + /** + * 检验数字 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNumber(final String str) { + return match(REGEX_NUMBER, str); + } + + /** + * 检验数字或包含小数点 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNumberDecimal(final String str) { + return match(REGEX_NUMBER_OR_DECIMAL, str); + } + + /** + * 判断字符串是不是全是字母 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLetter(final String str) { + return match(REGEX_LETTER, str); + } + + /** + * 判断字符串是不是包含数字 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContainNumber(final String str) { + return match(REGEX_CONTAIN_NUMBER, str); + } + + /** + * 判断字符串是不是只含字母和数字 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNumberLetter(final String str) { + return match(REGEX_NUMBER_OR_LETTER, str); + } + + /** + * 检验特殊符号 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSpec(final String str) { + return match(REGEX_SPECIAL, str); + } + + /** + * 检验微信号 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isWx(final String str) { + return match(REGEX_WX, str); + } + + /** + * 检验真实姓名 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isRealName(final String str) { + return match(REGEX_REALNAME, str); + } + + /** + * 校验昵称 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNickName(final String str) { + return match(REGEX_NICKNAME, str); + } + + /** + * 校验用户名 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isUserName(final String str) { + return match(REGEX_USERNAME, str); + } + + /** + * 校验密码 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPassword(final String str) { + return match(REGEX_PASSWORD, str); + } + + /** + * 校验邮箱 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEmail(final String str) { + return match(REGEX_EMAIL, str); + } + + /** + * 校验 URL + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isUrl(final String str) { + if (!isEmpty(str)) { + return match(REGEX_URL, str.toLowerCase()); + } + return false; + } + + /** + * 校验 IP 地址 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isIPAddress(final String str) { + return match(REGEX_IP_ADDR, str); + } + + // = + +// // http://blog.csdn.net/myfuturein/article/details/6885216 +// [\\u0391-\\uFFE5] 匹配双字节字符 ( 汉字 + 符号 ) +// [\\u4e00-\\u9fa5] 注意只匹配汉字, 不匹配双字节字符 + + // 正则表达式: 验证汉字 + public static final String REGEX_CHINESE = "^[\u4e00-\u9fa5]+$"; + // 正则表达式: 验证汉字 ( 含双角符号 ) + public static final String REGEX_CHINESE_ALL = "^[\u0391-\uFFE5]+$"; + // 正则表达式: 验证汉字 ( 含双角符号 ) + public static final String REGEX_CHINESE_ALL2 = "[\u0391-\uFFE5]"; + + /** + * 校验汉字 ( 无符号, 纯汉字 ) + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isChinese(final String str) { + return match(REGEX_CHINESE, str); + } + + /** + * 判断字符串是不是全是中文 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isChineseAll(final String str) { + return match(REGEX_CHINESE_ALL, str); + } + + /** + * 判断字符串中包含中文、包括中文字符标点等 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContainChinese(final String str) { + if (!isEmpty(str)) { + try { + int length; + if (str != null && (length = str.length()) != 0) { + char[] dChar = str.toCharArray(); + for (int i = 0; i < length; i++) { + boolean flag = String.valueOf(dChar[i]).matches(REGEX_CHINESE_ALL2); + if (flag) { + return true; + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isContainChinese"); + } + } + return false; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } +} \ No newline at end of file diff --git a/DevLibUtils/src/main/res/anim/dev_fade_in.xml b/lib/DevApp/src/main/res/anim/dev_fade_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_fade_in.xml rename to lib/DevApp/src/main/res/anim/dev_fade_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_fade_out.xml b/lib/DevApp/src/main/res/anim/dev_fade_out.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_fade_out.xml rename to lib/DevApp/src/main/res/anim/dev_fade_out.xml diff --git a/DevLibUtils/src/main/res/anim/dev_magnify_fade_in.xml b/lib/DevApp/src/main/res/anim/dev_magnify_fade_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_magnify_fade_in.xml rename to lib/DevApp/src/main/res/anim/dev_magnify_fade_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_magnify_fade_out.xml b/lib/DevApp/src/main/res/anim/dev_magnify_fade_out.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_magnify_fade_out.xml rename to lib/DevApp/src/main/res/anim/dev_magnify_fade_out.xml diff --git a/DevLibUtils/src/main/res/anim/dev_magnify_left_top_in.xml b/lib/DevApp/src/main/res/anim/dev_magnify_left_top_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_magnify_left_top_in.xml rename to lib/DevApp/src/main/res/anim/dev_magnify_left_top_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_magnify_right_top_in.xml b/lib/DevApp/src/main/res/anim/dev_magnify_right_top_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_magnify_right_top_in.xml rename to lib/DevApp/src/main/res/anim/dev_magnify_right_top_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_push_down_in.xml b/lib/DevApp/src/main/res/anim/dev_push_down_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_push_down_in.xml rename to lib/DevApp/src/main/res/anim/dev_push_down_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_push_down_out.xml b/lib/DevApp/src/main/res/anim/dev_push_down_out.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_push_down_out.xml rename to lib/DevApp/src/main/res/anim/dev_push_down_out.xml diff --git a/DevLibUtils/src/main/res/anim/dev_push_left_in.xml b/lib/DevApp/src/main/res/anim/dev_push_left_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_push_left_in.xml rename to lib/DevApp/src/main/res/anim/dev_push_left_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_push_left_out.xml b/lib/DevApp/src/main/res/anim/dev_push_left_out.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_push_left_out.xml rename to lib/DevApp/src/main/res/anim/dev_push_left_out.xml diff --git a/DevLibUtils/src/main/res/anim/dev_push_right_in.xml b/lib/DevApp/src/main/res/anim/dev_push_right_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_push_right_in.xml rename to lib/DevApp/src/main/res/anim/dev_push_right_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_push_right_out.xml b/lib/DevApp/src/main/res/anim/dev_push_right_out.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_push_right_out.xml rename to lib/DevApp/src/main/res/anim/dev_push_right_out.xml diff --git a/DevLibUtils/src/main/res/anim/dev_push_up_in.xml b/lib/DevApp/src/main/res/anim/dev_push_up_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_push_up_in.xml rename to lib/DevApp/src/main/res/anim/dev_push_up_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_push_up_out.xml b/lib/DevApp/src/main/res/anim/dev_push_up_out.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_push_up_out.xml rename to lib/DevApp/src/main/res/anim/dev_push_up_out.xml diff --git a/DevLibUtils/src/main/res/anim/dev_rotate_fade_in.xml b/lib/DevApp/src/main/res/anim/dev_rotate_fade_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_rotate_fade_in.xml rename to lib/DevApp/src/main/res/anim/dev_rotate_fade_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_rotate_fade_out.xml b/lib/DevApp/src/main/res/anim/dev_rotate_fade_out.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_rotate_fade_out.xml rename to lib/DevApp/src/main/res/anim/dev_rotate_fade_out.xml diff --git a/DevLibUtils/src/main/res/anim/dev_shrink_fade_in.xml b/lib/DevApp/src/main/res/anim/dev_shrink_fade_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_shrink_fade_in.xml rename to lib/DevApp/src/main/res/anim/dev_shrink_fade_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_shrink_fade_out.xml b/lib/DevApp/src/main/res/anim/dev_shrink_fade_out.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_shrink_fade_out.xml rename to lib/DevApp/src/main/res/anim/dev_shrink_fade_out.xml diff --git a/DevLibUtils/src/main/res/anim/dev_shrink_left_bottom_in.xml b/lib/DevApp/src/main/res/anim/dev_shrink_left_bottom_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_shrink_left_bottom_in.xml rename to lib/DevApp/src/main/res/anim/dev_shrink_left_bottom_in.xml diff --git a/DevLibUtils/src/main/res/anim/dev_shrink_right_bottom_in.xml b/lib/DevApp/src/main/res/anim/dev_shrink_right_bottom_in.xml similarity index 100% rename from DevLibUtils/src/main/res/anim/dev_shrink_right_bottom_in.xml rename to lib/DevApp/src/main/res/anim/dev_shrink_right_bottom_in.xml diff --git a/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_frame.png b/lib/DevApp/src/main/res/drawable-hdpi/dev_toast_frame.9.png similarity index 100% rename from DevLibUtils/src/main/res/drawable-hdpi/dev_toast_frame.png rename to lib/DevApp/src/main/res/drawable-hdpi/dev_toast_frame.9.png diff --git a/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_clear_white.png b/lib/DevApp/src/main/res/drawable-hdpi/dev_toast_icon_error_white.png similarity index 100% rename from DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_clear_white.png rename to lib/DevApp/src/main/res/drawable-hdpi/dev_toast_icon_error_white.png diff --git a/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_info_outline_white.png b/lib/DevApp/src/main/res/drawable-hdpi/dev_toast_icon_info_white.png similarity index 100% rename from DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_info_outline_white.png rename to lib/DevApp/src/main/res/drawable-hdpi/dev_toast_icon_info_white.png diff --git a/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_check_white.png b/lib/DevApp/src/main/res/drawable-hdpi/dev_toast_icon_success_white.png similarity index 100% rename from DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_check_white.png rename to lib/DevApp/src/main/res/drawable-hdpi/dev_toast_icon_success_white.png diff --git a/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_warning_outline_white.png b/lib/DevApp/src/main/res/drawable-hdpi/dev_toast_icon_warning_white.png similarity index 100% rename from DevLibUtils/src/main/res/drawable-hdpi/dev_toast_ic_warning_outline_white.png rename to lib/DevApp/src/main/res/drawable-hdpi/dev_toast_icon_warning_white.png diff --git a/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_frame.png b/lib/DevApp/src/main/res/drawable-xhdpi/dev_toast_frame.9.png similarity index 100% rename from DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_frame.png rename to lib/DevApp/src/main/res/drawable-xhdpi/dev_toast_frame.9.png diff --git a/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_clear_white.png b/lib/DevApp/src/main/res/drawable-xhdpi/dev_toast_icon_error_white.png similarity index 100% rename from DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_clear_white.png rename to lib/DevApp/src/main/res/drawable-xhdpi/dev_toast_icon_error_white.png diff --git a/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_info_outline_white.png b/lib/DevApp/src/main/res/drawable-xhdpi/dev_toast_icon_info_white.png similarity index 100% rename from DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_info_outline_white.png rename to lib/DevApp/src/main/res/drawable-xhdpi/dev_toast_icon_info_white.png diff --git a/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_check_white.png b/lib/DevApp/src/main/res/drawable-xhdpi/dev_toast_icon_success_white.png similarity index 100% rename from DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_check_white.png rename to lib/DevApp/src/main/res/drawable-xhdpi/dev_toast_icon_success_white.png diff --git a/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_warning_outline_white.png b/lib/DevApp/src/main/res/drawable-xhdpi/dev_toast_icon_warning_white.png similarity index 100% rename from DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_warning_outline_white.png rename to lib/DevApp/src/main/res/drawable-xhdpi/dev_toast_icon_warning_white.png diff --git a/DevLibUtils/src/main/res/drawable/dialog_transparent.9.png b/lib/DevApp/src/main/res/drawable/dialog_transparent.9.png similarity index 100% rename from DevLibUtils/src/main/res/drawable/dialog_transparent.9.png rename to lib/DevApp/src/main/res/drawable/dialog_transparent.9.png diff --git a/DevLibUtils/src/main/res/layout/dev_toast_layout.xml b/lib/DevApp/src/main/res/layout/dev_toast_layout.xml similarity index 100% rename from DevLibUtils/src/main/res/layout/dev_toast_layout.xml rename to lib/DevApp/src/main/res/layout/dev_toast_layout.xml diff --git a/DevLibUtils/src/main/res/raw/dev_beep.ogg b/lib/DevApp/src/main/res/raw/dev_beep.ogg similarity index 100% rename from DevLibUtils/src/main/res/raw/dev_beep.ogg rename to lib/DevApp/src/main/res/raw/dev_beep.ogg diff --git a/DevLibUtils/src/main/res/values-zh-rCN/strings.xml b/lib/DevApp/src/main/res/values-zh-rCN/strings.xml similarity index 86% rename from DevLibUtils/src/main/res/values-zh-rCN/strings.xml rename to lib/DevApp/src/main/res/values-zh-rCN/strings.xml index 7f1d83c047..bf12284871 100644 --- a/DevLibUtils/src/main/res/values-zh-rCN/strings.xml +++ b/lib/DevApp/src/main/res/values-zh-rCN/strings.xml @@ -10,9 +10,9 @@ SHA256 首次安装时间 最近更新时间 - app 最低支持版本 - app 兼容sdk版本 - Apk 安装包大小 + App 最低支持版本 + App 兼容sdk版本 + APK 安装包大小 有效时间 签名是否过期 已过期 diff --git a/DevLibUtils/src/main/res/values/color.xml b/lib/DevApp/src/main/res/values/color.xml similarity index 92% rename from DevLibUtils/src/main/res/values/color.xml rename to lib/DevApp/src/main/res/values/color.xml index 74328c04ef..3a0dd0d6ab 100644 --- a/DevLibUtils/src/main/res/values/color.xml +++ b/lib/DevApp/src/main/res/values/color.xml @@ -4,6 +4,7 @@ #00000000 + #000000 @@ -16,10 +17,33 @@ #B2000000 #CC000000 #E5000000 + #FFFFFF + + #19FFFFFF + #33FFFFFF + #4CFFFFFF + #66FFFFFF + #7FFFFFFF + #99FFFFFF + #B2FFFFFF + #CCFFFFFF + #E5FFFFFF + #808080 + + #19808080 + #33808080 + #4C808080 + #66808080 + #7F808080 + #99808080 + #B2808080 + #CC808080 + #E5808080 + #FF0000 diff --git a/DevLibUtils/src/main/res/values/strings.xml b/lib/DevApp/src/main/res/values/strings.xml similarity index 87% rename from DevLibUtils/src/main/res/values/strings.xml rename to lib/DevApp/src/main/res/values/strings.xml index 2ff064c298..3d0774e1d9 100644 --- a/DevLibUtils/src/main/res/values/strings.xml +++ b/lib/DevApp/src/main/res/values/strings.xml @@ -1,11 +1,11 @@ to - Pack name - APP md5 signatures + App Pack name + App md5 signatures App version code App version name - apk file path + Apk file path SHA1 SHA256 First install time diff --git a/DevLibUtils/src/main/res/values/styles.xml b/lib/DevApp/src/main/res/values/styles.xml similarity index 98% rename from DevLibUtils/src/main/res/values/styles.xml rename to lib/DevApp/src/main/res/values/styles.xml index aeace20609..00187494e9 100644 --- a/DevLibUtils/src/main/res/values/styles.xml +++ b/lib/DevApp/src/main/res/values/styles.xml @@ -2,7 +2,7 @@ - + diff --git a/lib/DevApp/src/main/res/xml/dev_app_provider_paths.xml b/lib/DevApp/src/main/res/xml/dev_app_provider_paths.xml new file mode 100644 index 0000000000..0c40bb34af --- /dev/null +++ b/lib/DevApp/src/main/res/xml/dev_app_provider_paths.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/DevApp/utils_readme/USE_CONFIG.md b/lib/DevApp/utils_readme/USE_CONFIG.md new file mode 100644 index 0000000000..9c944485b6 --- /dev/null +++ b/lib/DevApp/utils_readme/USE_CONFIG.md @@ -0,0 +1,63 @@ + +## 初始化 + +```java +/** + * detail: 全局Application + * @author Ttt + */ +public class BaseApplication extends Application { + + // 日志 TAG + private final String LOG_TAG = BaseApplication.class.getSimpleName(); + + @Override + public void onCreate() { + super.onCreate(); + + // 初始化工具类 + DevUtils.init(this.getApplicationContext()); + // = 初始化日志配置 = + // 设置默认Logger配置 + LogConfig logConfig = new LogConfig(); + logConfig.logLevel = LogLevel.DEBUG; + logConfig.tag = LOG_TAG; + logConfig.sortLog = true; // 美化日志, 边框包围 + DevLogger.init(logConfig); + // 打开 lib 内部日志 - 线上环境, 不调用方法就行 + DevUtils.openLog(); + DevUtils.openDebug(); + } +} +``` + +# 配置与使用相关 - [目录](https://github.com/afkT/DevUtils/tree/master/app/src/main/java/com/dev/utils) + +## [DevLogger 日志工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/logger/DevLogger.md) + +## [DevToast Toast工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/toast/DevToast.md) + +## [ToastTintUtils Toast美化工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/toast/ToastTintUtils.md) + +## [SnackbarUtils Snackbar工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/snackbar/SnackbarUtils.md) + +## [FileRecordUtils、AnalysisRecordUtils、DevLoggerUtils 日志、异常文件记录保存工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/record/FileRecord.md) + +## [SharedUtils - SharedPreferences工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/share/SharedUtils.md) + +## [ShapeUtils - Shape工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/shape/ShapeUtils.md) + +## [DevCache - 缓存工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/cache/DevCache.md) + +## [TimerManager 定时器工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/timer/TimerManager.md) + +## [DevThreadManager - 线程工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/thread/DevThreadManager.md) + +## [DevMediaManager 多媒体工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/media/DevMediaManager.md) + +## [WifiHotUtils - Wifi热点工具类文档](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/wifi/WifiHotUtils.md) + + +# == 使用相关 == + +## [TextViewUtils - 字体计算工具类使用](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/text/TextCalcUse.java) diff --git a/lib/DevApp/utils_readme/cache/DevCache.md b/lib/DevApp/utils_readme/cache/DevCache.md new file mode 100644 index 0000000000..2a18fbc315 --- /dev/null +++ b/lib/DevApp/utils_readme/cache/DevCache.md @@ -0,0 +1,66 @@ +# Cache 工具类 + +#### 使用演示类 [CacheUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/cache/CacheUse.java) 介绍了配置参数及使用 + +#### 项目类结构 - [包目录](https://github.com/afkT/DevUtils/tree/master/lib/DevApp/src/main/java/dev/utils/app/cache) + +* 缓存工具类([DevCache.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/cache/DevCache.java)):缓存工具类, 提供各种保存数据方法 + +* 缓存管理类([DevCacheManager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/cache/DevCacheManager.java)):内部缓存管理类 + +* 缓存处理工具类([DevCacheUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/cache/DevCacheUtils.java)):内部缓存处理工具类, 判断是否过期等各种方法 + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| getCacheDir | 获取缓存地址 | +| get | 默认缓存地址 | +| put | 保存 String 数据到缓存中 | +| getAsString | 读取 String 数据 | +| getAsJSONObject | 读取 JSONObject 数据 | +| getAsJSONArray | 读取 JSONArray 数据 | +| getAsBinary | 获取 byte[] 数据 | +| getAsObject | 读取 Serializable 数据 | +| getAsBitmap | 读取 Bitmap 数据 | +| getAsDrawable | 读取 Drawable 数据 | +| file | 获取缓存文件 | +| remove | 移除某个key | +| clear | 清除所有数据 | + +#### 使用方法 +```java +final Context mContext = DevUtils.getContext(); + +// 初始化 +CacheVo cacheVo = new CacheVo("测试持久化"); +// 打印信息 +LogPrintUtils.dTag(TAG, "保存前: " + cacheVo.toString()); +// 保存数据 +DevCache.get(mContext).put("ctv", cacheVo); +// 重新获取 +CacheVo ctv = (CacheVo) DevCache.get(mContext).getAsObject("ctv"); +// 打印获取后的数据 +DevLogger.dTag(TAG, "保存后: " + ctv.toString()); +// 设置保存有效时间 5秒 +DevCache.get(mContext).put("ctva", new CacheVo("测试有效时间"), 1); + +// 保存到指定文件夹下 +DevCache.get(new File(SDCardUtils.getSDCardPath(), "Cache")).put("key", "保存数据"); + +// 延迟后 +new Thread(new Runnable() { + @Override + public void run() { + try { + // 延迟 1.5 已经过期再去获取 + Thread.sleep(1500); + // 获取数据 + CacheVo ctva = (CacheVo) DevCache.get(mContext).getAsObject("ctva"); + // 判断是否过期 + DevLogger.dTag(TAG, "是否过期: " + (ctva == null)); + } catch (Exception e) { + } + } +}).start(); +``` \ No newline at end of file diff --git a/lib/DevApp/utils_readme/logger/DevLogger.md b/lib/DevApp/utils_readme/logger/DevLogger.md new file mode 100644 index 0000000000..7d653f4d8a --- /dev/null +++ b/lib/DevApp/utils_readme/logger/DevLogger.md @@ -0,0 +1,232 @@ +# DevLogger 日志工具类 + +#### 使用演示类 [LoggerUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/logger/LoggerUse.java) 介绍了配置参数及使用 + +#### 项目类结构 - [包目录](https://github.com/afkT/DevUtils/tree/master/lib/DevApp/src/main/java/dev/utils/app/logger) + +* 日志操作类([DevLogger](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/DevLogger.java)):日志操作类(对外公开直接调用), 直接调用日志输入类方法 + +* 日志操作工具类([DevLoggerUtils](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/DevLoggerUtils.java)):提供常用日志配置快捷获取方法、以及日志存储方法等 + +* 日志接口([IPrinter](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/IPrinter.java)):主要编写可以被外部调用接口, 以及可以操作的类型 + +* 日志输出类([LoggerPrinter](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/LoggerPrinter.java)):实现日志接口, 并且对对应的方法, 进行处理, 最终打印 + +* 日志设置([LogConfig](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/LogConfig.java)):该类主要控制日志输出方式, 以及是否输入日志, 堆栈方法等 + +* 日志配置([LogConstants](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/LogConstants.java)):该类主要是常量配置信息 + +* 日志级别([LogLevel](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/LogLevel.java)):该类主要控制日志级别 + +* 日志内部工具类([Utils](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/Utils.java)):内部快捷操作工具类(获取应用信息、设备信息、存储日志等) + + +#### 框架亮点 + +* 支持全局日志统一配置, 以及部分日志个性化配置 + +* 支持无限长度日志打印, 无 Logcat 4000 字符限制 + +* 支持可变参数传参, 任意个数打印参数 + +* 支持 JSON、XML 字符串解析、格式化打印 + +* 支持无 Tag 快捷打印、以及全局配置 Tag + +* 支持显示行号、线程、类、方法信息等打印, 以及堆栈信息跟踪、偏移打印等 + +* 支持全局控制打印级别, 防止信息泄露, 以及控制是否打印日志 + +* 支持存储日志信息到文件中(含手机设备信息、应用版本信息), 并且可追加顶部、底部信息等 + +* 美化日志, 与系统日志打印格式区分, 清晰快捷找到关键日志 + +* 支持输出含当前类及行号和函数名等堆栈信息, 点击跳转 + + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| other | 使用单次其他日志配置 | +| getLogConfig | 获取日志配置信息 | +| init | 初始化日志配置信息(使用默认配置) | +| d | 打印 Log.DEBUG | +| e | 打印 Log.ERROR | +| w | 打印 Log.WARN | +| i | 打印 Log.INFO | +| v | 打印 Log.VERBOSE | +| wtf | 打印 Log.ASSERT | +| json | 格式化 JSON 格式数据, 并打印 | +| xml | 格式化 XML 格式数据, 并打印 | +| dTag | 打印 Log.DEBUG | +| eTag | 打印 Log.ERROR | +| wTag | 打印 Log.WARN | +| iTag | 打印 Log.INFO | +| vTag | 打印 Log.VERBOSE | +| wtfTag | 打印 Log.ASSERT | +| jsonTag | 格式化 JSON 格式数据, 并打印 | +| xmlTag | 格式化 XML 格式数据, 并打印 | + + +#### 全局配置 + +```java +// = 在BaseApplication 中调用 = +// 初始化日志配置 +LogConfig logConfig = new LogConfig(); +// 堆栈方法总数(显示经过的方法) +logConfig.methodCount = 3; +// 堆栈方法索引偏移(0 = 最新经过调用的方法信息, 偏移则往上推, 如 1 = 倒数第二条经过调用的方法信息) +logConfig.methodOffset = 0; +// 是否输出全部方法(在特殊情况下, 如想要打印全部经过的方法, 但是不知道经过的总数) +logConfig.outputMethodAll = false; +// 显示日志线程信息(特殊情况, 显示经过的线程信息, 具体情况如上) +logConfig.displayThreadInfo = false; +// 是否排序日志(格式化后) +logConfig.sortLog = false; // 是否美化日志, 边框包围 +// 日志级别 +logConfig.logLevel = LogLevel.DEBUG; +// 设置 TAG (特殊情况使用, 不使用全部的 TAG 时, 如单独输出在某个 TAG 下) +logConfig.tag = "BaseLog"; +// 进行初始化配置, 这样设置后, 默认全部日志都使用改配置, 特殊使用 DevLogger.other(config).d(xxx); +DevLogger.init(logConfig); +// 进行初始化配置, 在DevUtils.init() 内部调用了 +// DevLoggerUtils.init(mContext); // 日志操作工具类, 快捷获取 LogConfig、以及保存日志到文件中等 +``` + + +#### 配置事项 +```java +// 发布的时候, 默认不需要打印日志则修改为 +LogConfig logConfig = new LogConfig(); +logConfig.logLevel = LogLevel.NONE; // 全部不打印 +DevLogger.init(logConfig); // 该方法设置全局默认日志配置 + +// 还有一种情况, 部分日志发布的时候不打印, 但是有部分异常信息需要打印, 则单独使用配置 +DevLoggerUtils.getReleaseLogConfig(TAG) // 使用封装好的线上配置都行 +DevLoggerUtils.getReleaseLogConfig(TAG, LogLevel) // 使用封装好的线上配置都行 +DevLogger.init(DevLoggerUtils.getReleaseLogConfig(TAG)); + +// DevLoggerUtils 内部包含 常用日志配置快捷获取方法、以及日志存储方法等 +``` + + +#### 打印日志 +```java +// 无 Tag 快捷打印 (使用全局 LogConfig.tag) +DevLogger.v("测试数据 - v"); +DevLogger.d("测试数据 - d"); +DevLogger.i("测试数据 - i"); +DevLogger.w("测试数据 - w"); +DevLogger.e("错误 - e"); +DevLogger.wtf("测试数据 - wtf"); + +// 使用 自定义 Tag 打印日志 +DevLogger.vTag(tag, "测试数据 - v"); +DevLogger.dTag(tag, "测试数据 - d"); +DevLogger.iTag(tag, "测试数据 - i"); +DevLogger.wTag(tag, "测试数据 - w"); +DevLogger.eTag(tag, "错误 - e"); +DevLogger.wtfTag(tag, "测试数据 - wtf"); + +// 占位符(其他类型, 一样) +DevLogger.d("%s测试占位符数据 - d%s", new Object[]{"1.", " - Format"}); +// = +DevLogger.dTag(tag, "%s测试占位符数据 - d%s", new Object[]{"1.", " - Format"}); + +// 打印 JSON、XML 格式字符串数据 +// JSON对象 +DevLogger.json(TestData.SMALL_SON_WITH_NO_LINE_BREAK); +DevLogger.jsonTag(tag, TestData.SMALL_SON_WITH_NO_LINE_BREAK); +// XML数据 +DevLogger.xml(TestData.XML_DATA); +DevLogger.xmlTag(tag, TestData.XML_DATA); +``` + + +#### 打印日志(自定义配置) +```java +// 初始化日志配置 +LogConfig logConfig = new LogConfig(); +// 是否排序日志(格式化后) +logConfig.sortLog = true; +// 日志级别 +logConfig.logLevel = LogLevel.DEBUG; +// 设置 TAG (特殊情况使用, 不使用全部的 TAG 时, 如单独输出在某个 TAG 下) +logConfig.tag = "SAD"; +// 打印日志信息 +DevLogger.other(logConfig).e("new Config - e"); +DevLogger.other(logConfig).e(new Exception("报错"), "new Config - e"); +DevLogger.other(logConfig).eTag(tag, "new Config - e"); +DevLogger.other(logConfig).eTag(tag, new Exception("报错"), "new Config - e"); + +// 有 Tag 优先使用自定义 Tag, 无 Tag 才使用 LogConfig.tag +DevLogger.other(logConfig).eTag(tag, "new Config - e"); +``` + + +#### 存储日志文件 +```java +// ==================== +// = 存储异常日志信息 = +// ==================== + +try { + String s = null; + s.indexOf("c"); +} catch (NullPointerException e) { + // 打印格式化后的日志信息 + DevLogger.other(DevLoggerUtils.getSortLogConfig("LogPro")).e(e, "s = null"); + // 保存的路径 + String fileName = LOG_SD_PATH + System.currentTimeMillis() + ".log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fileName); + // = + // 保存自定义头部、底部信息 + DevLoggerUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log"); + // = + // 保存的路径 + fileName = LOG_SD_PATH + System.currentTimeMillis() + "_orgs.log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fileName); +} + +// ================ +// = 存储日志信息 = +// ================ + +// 保存文件名 +String fileName = System.currentTimeMillis() + ".log"; +// 保存日志 +DevLoggerUtils.saveLog("保存自定义信息日志", LOG_SD_PATH, fileName); + +// = 保存日志, 包含头部信息、底部信息 = +// 保存文件名 +fileName = System.currentTimeMillis() + ".log"; +// 保存日志 +DevLoggerUtils.saveLog("保存自定义信息日志", "头部", "底部", LOG_SD_PATH, fileName); +``` + + +# 预览 + +***XML、JSON 格式化打印*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/logger/log_xml_json.png) + +***打印堆栈信息*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/logger/log_default.png) + +***打印异常信息*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/logger/log_error.png) + +***正常打印*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/logger/log_other.png) + +***保存日志文件*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/logger/log_file_error.png) \ No newline at end of file diff --git a/lib/DevApp/utils_readme/logger/log_default.png b/lib/DevApp/utils_readme/logger/log_default.png new file mode 100644 index 0000000000..4230f87d49 Binary files /dev/null and b/lib/DevApp/utils_readme/logger/log_default.png differ diff --git a/lib/DevApp/utils_readme/logger/log_error.png b/lib/DevApp/utils_readme/logger/log_error.png new file mode 100644 index 0000000000..47b9b0e842 Binary files /dev/null and b/lib/DevApp/utils_readme/logger/log_error.png differ diff --git a/lib/DevApp/utils_readme/logger/log_file_error.png b/lib/DevApp/utils_readme/logger/log_file_error.png new file mode 100644 index 0000000000..77cbe5600e Binary files /dev/null and b/lib/DevApp/utils_readme/logger/log_file_error.png differ diff --git a/lib/DevApp/utils_readme/logger/log_other.png b/lib/DevApp/utils_readme/logger/log_other.png new file mode 100644 index 0000000000..214b6b9562 Binary files /dev/null and b/lib/DevApp/utils_readme/logger/log_other.png differ diff --git a/lib/DevApp/utils_readme/logger/log_xml_json.png b/lib/DevApp/utils_readme/logger/log_xml_json.png new file mode 100644 index 0000000000..9055432c82 Binary files /dev/null and b/lib/DevApp/utils_readme/logger/log_xml_json.png differ diff --git a/lib/DevApp/utils_readme/media/DevMediaManager.md b/lib/DevApp/utils_readme/media/DevMediaManager.md new file mode 100644 index 0000000000..0839600812 --- /dev/null +++ b/lib/DevApp/utils_readme/media/DevMediaManager.md @@ -0,0 +1,186 @@ +# 多媒体工具类 + +#### 使用演示类 [MediaUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/media/MediaUse.java) 介绍了配置参数及使用 + +#### 项目类结构 - [包目录](https://github.com/afkT/DevUtils/tree/master/lib/DevApp/src/main/java/dev/utils/app/player) + +* 多媒体管理类([DevMediaManager](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/player/DevMediaManager.java)):MediaPlayer 统一管理类, 全局使用一个 MediaPlayer + +* 视频播放控制类([DevVideoPlayerControl](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/player/DevVideoPlayerControl.java)):视频播放控制器, 快捷播放视频工具类 + + +#### 框架亮点 + +* 单例 MediaPlayer, 全局统一管理, 防止多个多媒体资源同时存在后台播放 + +* 快捷封装各种通用方法, 以及监听事件处理, 控制处理 MediaPlayer + +* 支持使用 MediaSet 抽象类设置 MediaPlayer 其他配置方法等, 以及是否循环播放等 + +* 支持快捷播放 raw、assets、本地SDCard、http 等路径, 多媒体文件 + + +## API 文档 + +* **MediaPlayer 统一管理类 ->** [DevMediaManager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/player/DevMediaManager.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 DevMediaManager 实例 | +| setAudioStreamType | 设置流类型 | +| playPrepareRaw | 播放 Raw 资源 | +| playPrepareAssets | 播放 Assets 资源 | +| playPrepare | 预加载播放 - (file-path or http/rtsp URL) http 资源、本地资源 | +| isPlaying | 是否播放中 | +| pause | 暂停操作 | +| stop | 停止操作 - 销毁 MediaPlayer | +| isIgnoreWhat | 是否忽略错误类型 | +| onError | 播放出错回调 | +| onVideoSizeChanged | 视频大小改变通知 | +| onPrepared | 使用 mMediaPlayer.prepareAsync() 异步播放准备成功回调 | +| onCompletion | 视频播放结束回调 | +| onBufferingUpdate | MediaPlayer 缓冲更新回调 | +| onSeekComplete | 滑动加载完成回调 | +| setMeidaListener | 设置 MediaPlayer 回调事件 | +| isNullMediaPlayer | 判断 MediaPlayer 是否为 null | +| isNotNullMediaPlayer | 判断 MediaPlayer 是否不为 null | +| getMediaPlayer | 获取 MediaPlayer 对象 | +| setMediaPlayer | 设置 MediaPlayer 对象 | +| setTAG | 设置日志打印 TAG | +| getVolume | 获取播放音量 | +| setVolume | 设置播放音量 | +| getPlayRawId | 获取播放资源 id | +| getPlayUri | 获取播放地址 | +| getVideoWidth | 获取视频宽度 | +| getVideoHeight | 获取视频高度 | +| getCurrentPosition | 获取播放时间 | +| getDuration | 获取资源总时间 | +| getPlayPercent | 获取播放进度百分比 | +| isLooping | 是否循环播放 - 默认不循环 | +| setMediaConfig | 设置播放配置 | + + +* **视频播放控制器 ->** [DevVideoPlayerControl.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/player/DevVideoPlayerControl.java) + +| 方法 | 注释 | +| :- | :- | +| surfaceChanged | Surface 改变通知 | +| surfaceCreated | Surface 创建 | +| surfaceDestroyed | Surface 销毁 | +| onPrepared | 准备完成回调 | +| onCompletion | 播放完成/结束 | +| onBufferingUpdate | 缓存进度 | +| onSeekComplete | 滑动进度加载成功 | +| onError | 异常回调 | +| onVideoSizeChanged | 视频大小改变通知 | +| setMediaListener | 设置播放监听事件 | +| pausePlayer | 暂停播放 | +| stopPlayer | 停止播放 | +| startPlayer | 开始播放 | +| getSurfaceview | 获取 SurfaceView | +| isPlaying | 是否播放中 | +| isAutoPlay | 判断是否自动播放 | +| setAutoPlay | 设置自动播放 | +| getPlayUri | 获取播放地址 | +| getVideoWidth | 获取视频宽度 | +| getVideoHeight | 获取视频高度 | +| getCurrentPosition | 获取播放时间 | +| getDuration | 获取资源总时间 | +| getPlayPercent | 获取播放进度百分比 | + + +#### 使用方法 +```java +// 设置 TAG, 打印日志使用 +DevMediaManager.getInstance().setTAG(TAG); +// 设置音量 +DevMediaManager.getInstance().setVolume(50); +// 设置流类型 +DevMediaManager.getInstance().setAudioStreamType(AudioManager.STREAM_MUSIC); + +// 获取播放音量 +DevMediaManager.getInstance().getVolume(); +// 获取当前播放的地址 +DevMediaManager.getInstance().getPlayUri(); +// 获取播放的资源id +DevMediaManager.getInstance().getPlayRawId(); +// 获取 当前播放时间 +DevMediaManager.getInstance().getCurrentPosition(); +// 获取资源总时间 +DevMediaManager.getInstance().getDuration(); +// 获取播放进度百分比 +DevMediaManager.getInstance().getPlayPercent(); +// 获取 MediaPlayer 对象 +DevMediaManager.getInstance().getMediaPlayer(); + +// 获取播放的视频高度 +DevMediaManager.getInstance().getVideoHeight(); +// 获取播放的视频宽度 +DevMediaManager.getInstance().getVideoWidth(); + +// 是否播放中 +DevMediaManager.getInstance().isPlaying(); +// 停止操作 +DevMediaManager.getInstance().stop(); +// 暂停操作 +DevMediaManager.getInstance().pause(); + +// 设置事件监听 +DevMediaManager.getInstance().setMeidaListener(new DevMediaManager.MediaListener() { + @Override + public void onPrepared() { + if (DevMediaManager.getInstance().isNotNullMediaPlayer()) { + // 播放操作 + DevMediaManager.getInstance().getMediaPlayer().start(); + } + } + + @Override + public void onCompletion() { + } + + @Override + public void onBufferingUpdate(int percent) { + } + + @Override + public void onSeekComplete() { + } + + @Override + public void onError(int what, int extra) { + } + + @Override + public void onVideoSizeChanged(int width, int height) { + } +}); + +// = + +// 播放音频 +DevMediaManager.getInstance().playPrepareRaw(R.raw.dev_beep); +DevMediaManager.getInstance().playPrepareAssets("a.mp3"); +DevMediaManager.getInstance().playPrepare(SDCardUtils.getSDCardPath() + "/a.mp3"); +DevMediaManager.getInstance().playPrepare("http://xxx.mp3"); +DevMediaManager.getInstance().playPrepare(new DevMediaManager.MediaSet() { + @Override + public void setMediaConfig(MediaPlayer mediaPlayer) throws Exception { + mediaPlayer.setDataSource("xxx"); + } +}); // 自由设置信息 + +// = + +SurfaceView surfaceView = null; +// 播放视频 +DevVideoPlayerControl control = new DevVideoPlayerControl(surfaceView); +control.startPlayer(SDCardUtils.getSDCardPath() + "/video_3.mp4"); +control.startPlayer("http://xxx.mp4"); +control.startPlayer(new DevMediaManager.MediaSet() { + @Override + public void setMediaConfig(MediaPlayer mediaPlayer) throws Exception { + mediaPlayer.setDataSource("xxx"); + } +}); // 自由设置信息 +``` \ No newline at end of file diff --git a/lib/DevApp/utils_readme/record/FileRecord.md b/lib/DevApp/utils_readme/record/FileRecord.md new file mode 100644 index 0000000000..39b992c50b --- /dev/null +++ b/lib/DevApp/utils_readme/record/FileRecord.md @@ -0,0 +1,147 @@ +# 日志、异常文件记录保存工具类文档 + +#### 使用演示类 [FileRecordUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/record/FileRecordUse.java) 介绍了配置参数及使用 + +> 三个工具类, 实际上是两个工具类的差异 ( DevLoggerUtils/FileRecordUtils、AnalysisRecordUtils) + +> DevLoggerUtils 内部的 Utils, 实际和 FileRecordUtils 代码相同, 使用方式一致 + +#### 项目类结构 + +* 日志操作工具类([DevLoggerUtils](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/DevLoggerUtils.java)):提供常用日志配置快捷获取方法、以及日志存储方法等 + +* 文件记录工具类([FileRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/FileRecordUtils.java)):同 DevLoggerUtils 一样, 专门用于信息存储工具类 + +* 分析记录工具类([AnalysisRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/AnalysisRecordUtils.java)):专业记录信息, 并存储方便分析, 支持存储目录、时间段保存 + +## API 文档 + +* **日志操作工具类 ->** [DevLoggerUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/logger/DevLoggerUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化调用方法(获取版本号) | +| getReleaseLogConfig | 获取 Release Log 配置(打印线程信息、显示方法总数 3、从 0 开始、不进行排序、默认只打印 ERROR 级别日志) | +| getDebugLogConfig | 获取 Debug Log 配置(打印线程信息、显示方法总数 3、从 0 开始、不进行排序、默认只打印 ERROR 级别日志) | +| getSortLogConfig | 获取 Log 配置(打印线程信息、显示方法总数 3、从 0 开始、并且美化日志信息、默认打印 DEBUG 级别及以上日志) | +| getLogConfig | 获取 Log 配置 | +| saveErrorLog | 保存异常日志 | +| saveLog | 保存日志 | + +* **文件记录工具类 ->** [FileRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/FileRecordUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化调用方法 | +| saveErrorLog | 保存异常日志 | +| saveLog | 保存日志 | + + +* **分析记录工具类 ->** [AnalysisRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/AnalysisRecordUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化操作 | +| record | 日志记录 | +| isHandler | 是否处理日志记录 | +| setHandler | 设置是否处理日志记录 | +| isAppendSpace | 是否追加空格 | +| setAppendSpace | 设置是否追加空格 | +| getLogFolderName | 获取文件日志名 | +| setLogFolderName | 设置日志文件夹名 | +| getLogStoragePath | 获取日志存储路径 | +| setLogStoragePath | 设置日志存储路径 | +| getStoragePath | 获取存储路径 | +| getFileName | 获取日志文件名 | +| getFileFunction | 获取日志文件记录的功能 | +| getFileIntervalTime | 获取日志文件记录间隔时间 | +| getFolderName | 获取日志文件夹名 | +| obtain | 获取记录分析文件信息 | +| getLogPath | 获取日志地址 | +| getIntervalTimeFolder | 获取时间间隔 - 文件夹 | + +#### DevLoggerUtils、FileRecordUtils 工具类 - 使用方法 +```java +try { + String s = null; + s.indexOf("c"); +} catch (NullPointerException e) { + + // = DevLoggerUtils 使用方法 = + + // 保存的路径 + String fileName = LOG_SD_PATH + System.currentTimeMillis() + ".log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fileName); + // = + // 保存自定义头部、底部信息 + DevLoggerUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log"); + // = + // 保存的路径 + fileName = LOG_SD_PATH + System.currentTimeMillis() + "_orgs.log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fileName); + + // 保存日志信息 + DevLoggerUtils.saveLog("日志内容", LOG_SD_PATH, System.currentTimeMillis() + ".log"); + // 保存日志信息 + DevLoggerUtils.saveLog("日志内容", "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log"); + + // = FileRecordUtils 使用方法 = + + FileRecordUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true); + + FileRecordUtils.saveLog("日志内容", "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true); +} +``` + +#### AnalysisRecordUtils 工具类 - 使用方法 +```java +// 默认存储到 android/data/包名/cache文件/, 可以自己特殊设置 +AnalysisRecordUtils.setLogStoragePath(SDCardUtils.getSDCardPath()); + +// 设置存储文件夹名 +AnalysisRecordUtils.setLogFolderName(AnalysisRecordUtils.getLogFolderName() + "/v" + AppUtils.getAppVersionName()); + +// AnalysisRecordUtils.HH、MM、SS 以对应的时间, 创建文件夹 HH_23/MM_13/SS_01 依此类推, 放到对应文件夹, 不传则放到当日文件夹下 +AnalysisRecordUtils.FileInfo fileInfo = AnalysisRecordUtils.FileInfo.obtain("test_log.txt", "测试记录", AnalysisRecordUtils.HH); + +// 存储路径、存储文件夹、文件名、记录功能提示、时间间隔、是否处理日志记录(是否保存) +// FileInfo(String storagePath, String folderName, String fileName, String fileFunction, @AnalysisRecordUtils.TIME int fileIntervalTime, boolean handler) + +// = FileInfo 配置 = + +fileInfo = AnalysisRecordUtils.FileInfo.obtain("test_log.txt", "测试记录"); + +fileInfo = AnalysisRecordUtils.FileInfo.obtain("TempRecord","test_log.txt", "测试记录"); + +fileInfo = AnalysisRecordUtils.FileInfo.obtain(SDCardUtils.getSDCardPath(),"TempRecord","test_log.txt", "测试记录"); + +// = + +fileInfo = AnalysisRecordUtils.FileInfo.obtain("test_log.txt", "测试记录", AnalysisRecordUtils.HH); + +fileInfo = AnalysisRecordUtils.FileInfo.obtain("TempRecord","test_log.txt", "测试记录", AnalysisRecordUtils.MM); + +fileInfo = AnalysisRecordUtils.FileInfo.obtain(SDCardUtils.getSDCardPath(),"TempRecord","test_log.txt", "测试记录", AnalysisRecordUtils.SS); + +// = + +// 存储到 android/data/包名/LogFolderName/2018-08-23/LogFolderName/xxx/log.txt +AnalysisRecordUtils.record(fileInfo, "日志内容"); + +// 存储到 sdcard/LogFolderName/2018-08-23/SDRecord/xxx/log.txt +AnalysisRecordUtils.record(AnalysisRecordUtils.FileInfo.obtain(SDCardUtils.getSDCardPath(),"SDRecord","sd_log.txt", "根目录保存", AnalysisRecordUtils.HH), + "日志内容"); + +// 存储到 sdcard/特殊地址/LogFolderName/2018-08-23/OtherRecord/xxx/log.txt +AnalysisRecordUtils.record(AnalysisRecordUtils.FileInfo.obtain(SDCardUtils.getSDCardPath() + "/特殊地址","OtherRecord","log.txt", "临时地址", AnalysisRecordUtils.HH), + "日志内容"); + +// = + +// 保存错误信息 +NullPointerException nullPointerException = new NullPointerException("报错啦, null 异常啊"); +// 记录日志 +AnalysisRecordUtils.record(fileInfo, ErrorUtils.getThrowable(nullPointerException)); +``` \ No newline at end of file diff --git a/lib/DevApp/utils_readme/shape/ShapeUtils.md b/lib/DevApp/utils_readme/shape/ShapeUtils.md new file mode 100644 index 0000000000..5985846773 --- /dev/null +++ b/lib/DevApp/utils_readme/shape/ShapeUtils.md @@ -0,0 +1,66 @@ +# Shape 工具类文档 + +#### 使用演示类 [ShapeUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/shape/ShapeUse.java) 介绍了配置参数及使用 + +> 1. https://blog.csdn.net/tanghongchang123/article/details/80283686 +> 2. https://www.cnblogs.com/popfisher/p/5606690.html +> 3. https://www.cnblogs.com/dongdong230/p/4183079.html +> 4. https://www.cnblogs.com/zhongle/archive/2012/08/28/2659902.html +> 5. https://www.2cto.com/kf/201601/456024.html + +#### 项目类结构 + +* Shape 工具类([ShapeUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/ShapeUtils.java)):Shape 效果工具类, 动态使用(圆角、渐变、描边) 等 + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| getDrawable | 获取 GradientDrawable | +| setDrawable | 设置 Drawable 背景 | +| newBuilder | 创建新的 Shape Builder 对象 | +| newBuilderToLeft | 创建新的 Shape Builder 对象 | +| newBuilderToRight | 创建新的 Shape Builder 对象 | +| newBuilderToGradient | 创建渐变的 Shape Builder 对象 | +| build | 获取 Shape 工具类 | +| setRadius | 设置圆角 | +| setRadiusLeft | 设置圆角 | +| setRadiusRight | 设置圆角 | +| setCornerRadii | 内部处理方法 | +| setColor | 设置背景色(填充铺满) | +| setStroke | 设置边框颜色 | +| setSize | 设置大小 | + +#### 使用方法 +```java +Button vid_btn1 = null; + +// 默认就设置背景色 +ShapeUtils.Builder builder = new ShapeUtils.Builder(); +builder.setRadiusLeft(10f).setColor(R.color.black); +ImageViewUtils.setBackground(vid_btn1, builder.build().getDrawable()); + +// 设置点击效果 +GradientDrawable drawable1 = ShapeUtils.newBuilder(10f, R.color.black).setStroke(5, R.color.green).build().getDrawable(); +GradientDrawable drawable2 = ShapeUtils.newBuilder(10f, R.color.sky_blue).setStroke(5, R.color.grey).build().getDrawable(); + +ImageViewUtils.setBackground(vid_btn1, StateListUtils.newSelector(drawable2, drawable1)); // 设置点击 View 背景变色, 不用写 shape xml 文件 +vid_btn1.setTextColor(StateListUtils.createColorStateList(R.color.red, R.color.white)); // 设置点击字体变色 + +// 设置渐变 +View vid_view1 = null; +// int[] colors = new int[]{ Color.RED, Color.BLUE, Color.GREEN }; + +int[] colors = new int[3]; +colors[0] = ContextCompat.getColor(DevUtils.getContext(), R.color.black); +colors[1] = ContextCompat.getColor(DevUtils.getContext(), R.color.sky_blue); +colors[2] = ContextCompat.getColor(DevUtils.getContext(), R.color.orange); + +// ShapeUtils.newBuilderToGradient(GradientDrawable.Orientation.BR_TL, colors).build().setDrawable(vid_view1); + +GradientDrawable drawable = ShapeUtils.newBuilderToGradient(GradientDrawable.Orientation.BR_TL, colors).build().getDrawable(); +// drawable.setGradientType(GradientDrawable.LINEAR_GRADIENT); // 线性渐变, 这是默认设置 +// drawable.setGradientType(GradientDrawable.RADIAL_GRADIENT); // 放射性渐变, 以开始色为中心 +drawable.setGradientType(GradientDrawable.SWEEP_GRADIENT); // 扫描线式的渐变 +ImageViewUtils.setBackground(vid_view1, drawable); +``` \ No newline at end of file diff --git a/lib/DevApp/utils_readme/share/SharedUtils.md b/lib/DevApp/utils_readme/share/SharedUtils.md new file mode 100644 index 0000000000..e89d8251be --- /dev/null +++ b/lib/DevApp/utils_readme/share/SharedUtils.md @@ -0,0 +1,82 @@ +# SharedPreferences 工具类 + +#### 使用演示类 [ShareUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/share/ShareUse.java) 介绍了配置参数及使用 + +> 1. apply没有返回值而 commit返回boolean表明修改是否提交成功 +> 2. apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘 +> 3. apply方法不会提示任何失败的提示 apply的效率高一些, 如果没有必要确认是否提交成功建议使用apply + +#### 项目类结构 - [包目录](https://github.com/afkT/DevUtils/tree/master/lib/DevApp/src/main/java/dev/utils/app/share) + +* SharedPreferences 工具类([SPUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/share/SPUtils.java)):SP 操作工具类, 实现 IPreferenceHolder 初始化方法 + +* IPreference 持有类([IPreferenceHolder.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/share/IPreferenceHolder.java)):IPreference 持有类, 内部返回实现类 IPreference + +* IPreference 接口类([IPreference.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/share/IPreference.java)):主要是正常操作方法接口类 + +* PreferenceImpl 接口实现类([PreferenceImpl.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/share/PreferenceImpl.java)):实现 IPreference 接口, SharedPreferences 操作接口具体实现类 + +* SharedPreferences 快捷使用工具类([SharedUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/share/SharedUtils.java)):内部实现 SPUtils, 直接进行使用 put/get 等 + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| init | 初始化操作 | +| put | 保存数据 | +| putAll | 保存 Map 集合(只能是 Integer、Long、Boolean、Float、String、Set) | +| get | 根据 key 获取数据 | +| getAll | 获取全部数据 | +| remove | 移除数据 | +| removeAll | 移除集合的数据 | +| contains | 是否存在 key | +| clear | 清除全部数据 | +| getInt | 获取 int 类型的数据 | +| getFloat | 获取 float 类型的数据 | +| getLong | 获取 long 类型的数据 | +| getBoolean | 获取 boolean 类型的数据 | +| getString | 获取 String 类型的数据 | +| getSet | 获取 Set 类型的数据 | + +#### 使用方法 +```java +// 具体实现方法 基于 PreferenceImpl 实现 + +// 存在可调用的方法 IPreference + +// SharedUtils 二次分装 SPUtils, 直接调用 + +// 在DevUtils.init 中初始化了, 实际可以不调用 +SharedUtils.init(DevUtils.getContext()); + +SharedUtils.put("aa", "aa"); +SharedUtils.put("ac", 123); + +// =========== +// = SPUtils = +// =========== + +// 想要自定义 模式, 名字等 +SPUtils.getPreference(DevUtils.getContext()).put("aa", 1); +SPUtils.getPreference(DevUtils.getContext(), "xxx").put("aa", 1); +SPUtils.getPreference(DevUtils.getContext(), "xxxxx", Context.MODE_PRIVATE).put("aa", 1); + + +// 默认值如下 +switch (type) { + case INTEGER: + return preferences.getInt(key, -1); + case FLOAT: + return preferences.getFloat(key, -1f); + case BOOLEAN: + return preferences.getBoolean(key, false); + case LONG: + return preferences.getLong(key, -1L); + case STRING: + return preferences.getString(key, null); + case STRING_SET: + return preferences.getStringSet(key, null); + default: // 默认取出String类型的数据 + return null; +} +``` \ No newline at end of file diff --git a/lib/DevApp/utils_readme/snackbar/SnackbarUtils.md b/lib/DevApp/utils_readme/snackbar/SnackbarUtils.md new file mode 100644 index 0000000000..1fb2f8235b --- /dev/null +++ b/lib/DevApp/utils_readme/snackbar/SnackbarUtils.md @@ -0,0 +1,455 @@ +# Snackbar 工具类 + +#### 使用演示类 [SnackbarUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/snackbar/SnackbarUse.java) 介绍了配置参数及使用 + +#### 项目类结构 + +* Snackbar 工具类([SnackbarUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/SnackbarUtils.java)):Snackbar 二次封装工具类 + +* Snackbar Style 样式抽象类(SnackbarUtils.Style):主要决定 Snackbar 显示效果样式, 可继承该抽象类, 重写需要的方法, 并通过 setStyle 设置 + +* Snackbar Style 样式构建类(SnackbarUtils.StyleBuilder):该类继承 SnackbarUtils.Style, 并且增加 set 属性方法, 并且可通过 Style 创建 StyleBuilder 并引用其样式配置 + +#### 框架亮点 + +* 支持通过 view/window/fragment/activity 构建 Snackbar 且可通过链式调用 + +* 支持自动区分资源 stringId, 支持可变参数传参, 自动格式化显示文本内容 + +* 使用 WeakReference 防止内存溢出, 并且支持 Snackbar 释放处理 + +* 支持 addView, above, bellow, 在指定的 view 上/下 方位置显示, 以及显示区域自动计算处理 + +* 支持手动关闭 Snackbar, 以及 Snackbar 事件监听处理等 + +* 支持自定义样式, 可配置样式属性比较全面, 高度自由可配置 + +* 支持样式构建引用, 在统一的样式上构建使用, 并快捷修改全局样式 + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| with | 获取 SnackbarUtils 对象 | +| getStyle | 获取样式 | +| setStyle | 设置样式 | +| getSnackbar | 获取 Snackbar | +| getSnackbarView | 获取 Snackbar View | +| getTextView | 获取 Snackbar TextView(snackbar_text) | +| getActionButton | 获取 Snackbar Action Button(snackbar_action) | +| addView | 向 Snackbar 布局中添加 View (Google 不建议, 复杂的布局应该使用 DialogFragment 进行展示) | +| setCallback | 设置 Snackbar 展示完成 及 隐藏完成 的监听 | +| setAction | 设置 Action 按钮文字内容及点击监听 | +| dismiss | 关闭 Snackbar | +| showShort | 显示 Short Snackbar | +| showLong | 显示 Long Snackbar | +| showIndefinite | 显示 Indefinite Snackbar (无限时, 一直显示) | +| setSnackbarStyle | 设置 Snackbar 样式配置 | +| getShadowMargin | 获取阴影边距 | +| setShadowMargin | 设置阴影边距 | +| isAutoCalc | 判断是否自动计算边距 (如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示) | +| setAutoCalc | 设置是否自动计算边距 (如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示) | +| above | 设置 Snackbar 显示在指定 View 的上方 | +| bellow | 设置 Snackbar 显示在指定 View 的下方 | + + +### 调用方法 + +#### 获取 SnackbarUtils + +> SnackbarUtils.with(view/window/fragment/activity); + +#### 显示 Snackbar + +> SnackbarUtils.with(view).showShort/showLong/showIndefinite(R.string.xxx、String); + +#### 关闭 Snackbar + +> SnackbarUtils.with(view).dismiss(isSetNull); // 是否销毁 Snackbar + +#### 设置 Action Button + +> SnackbarUtils.with(view).setAction(clickListener, R.string.xxx、String); + +#### 设置样式 + +> SnackbarUtils.with(view).setStyle(style); + + + +### 使用方法 +```java +// ================================================ +// = 只能通过以下四种方式 获取 SnackbarUtils 对象 = +// ================================================ + +SnackbarUtils.with(view); + +SnackbarUtils.with(window); + +SnackbarUtils.with(fragment); + +SnackbarUtils.with(activity); + + +// ================ +// = 获取相关方法 = +// ================ + +// = 获取 View = + +// 获取 Snackbar 底层 View +View snackbarView = SnackbarUtils.with(view).getSnackbarView(); + +// 获取 Snackbar TextView(snackbar_text) - 左侧 文本TextView +TextView textView = SnackbarUtils.with(view).getTextView(); + +// 获取 Snackbar Action Button(snackbar_action) - 右侧 Button +Button actionButton = SnackbarUtils.with(view).getActionButton(); + +// = + +// 获取 Snackbar 对象 +Snackbar snackbar = SnackbarUtils.with(view).getSnackbar(); + +// 获取 View 阴影边距大小 - View 自带阴影 +int shadowMargin = SnackbarUtils.with(view).getShadowMargin(); + +// 获取 是否自动计算边距 (如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示) +boolean autoCalc = SnackbarUtils.with(view).isAutoCalc(); // 只有调用 above / bellow 该属性才有意义 + +// 获取 Snackbar 显示效果样式配置信息 +SnackbarUtils.StyleBuilder styleBuilder = SnackbarUtils.with(view).getStyle(); + + +// ================ +// = 设置相关方法 = +// ================ + +// 设置 View 阴影边距大小 +SnackbarUtils.with(view).setShadowMargin(2); + +// 设置是否自动计算边距 (如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示) +SnackbarUtils.with(view).setAutoCalc(true); // 只有调用 above / bellow 该属性才有意义 + +// 设置 Snackbar 显示效果样式 +SnackbarUtils snackbarUtils = SnackbarUtils.with(view).setStyle(style); + +// = 快捷设置指定样式效果的 Snackbar = + +// 设置 Snackbar 显示效果为 当前 SnackbarUtils 对象使用的样式 +Snackbar snackbar1 = SnackbarUtils.with(view).setSnackbarStyle(snackbar); + +// 设置 Snackbar 显示效果为 自定义样式效果 +Snackbar snackbar2 = SnackbarUtils.with(view).setSnackbarStyle(snackbar, style); + + +// = 设置 Action Button 文案等 = + +// 设置 Snackbar Action Button(snackbar_action) 文案 +SnackbarUtils.with(view).setAction(R.string.app_name); + +// 设置 Snackbar Action Button(snackbar_action) 文案 - 支持格式化字符串 +SnackbarUtils.with(view).setAction(R.string.app_name, "1", 2); + +// 设置 Snackbar Action Button(snackbar_action) 文案 +SnackbarUtils.with(view).setAction("撤销"); + +// 设置 Snackbar Action Button(snackbar_action) 文案 - 支持格式化字符串 +SnackbarUtils.with(view).setAction("撤销 - %s", "3"); + +// 设置 Snackbar Action Button(snackbar_action) 文案以及点击事件 +SnackbarUtils.with(view).setAction(clickListener, R.string.app_name); + +// 设置 Snackbar Action Button(snackbar_action) 文案以及点击事件 +SnackbarUtils.with(view).setAction(clickListener, "撤销"); + + +// = 设置 事件相关 = + +// 设置 Snackbar 展示完成 及 隐藏完成 的监听 +SnackbarUtils.with(view).setCallback(new Snackbar.Callback() { + @Override + public void onShown(Snackbar sb) { + super.onShown(sb); + // Snackbar 显示 + } + + @Override + public void onDismissed(Snackbar transientBottomBar, int event) { + super.onDismissed(transientBottomBar, event); + // Snackbar 关闭 + } +}); + +// ============ +// = 操作方法 = +// ============ + +// = 关闭 = + +// 关闭显示 Snackbar +SnackbarUtils.with(view).dismiss(); + +// 关闭显示 Snackbar, 但不销毁 Snackbar +SnackbarUtils.with(view).dismiss(false); + +// = 显示 - 支持 String、R.string.xx 以及格式化字符串 = + +// 显示 Short Snackbar +SnackbarUtils.with(view).showShort("已收藏该消息!"); + +// 显示 Long Snackbar +SnackbarUtils.with(view).showLong("已收藏该消息!"); + +// 显示 Indefinite Snackbar (无限时, 一直显示) +SnackbarUtils.with(view).showIndefinite("已收藏该消息!"); + +// = 显示区域 = + +// 设置是否自动计算边距 (如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示) +// setAutoCalc 只有调用 above / bellow 该属性才有意义 + +// 设置 Snackbar 显示在指定 View 的上方, 并且向上边距 20 +SnackbarUtils.with(view).above(targetView, 20); + +// 设置 Snackbar 显示在指定 View 的下方, 并且向下边距 5 +SnackbarUtils.with(view).bellow(targetView, 5); + +// 向 Snackbar 布局中添加 View (Google 不建议, 复杂的布局应该使用 DialogFragment 进行展示) +SnackbarUtils.with(view).addView(newTextView, 0); + +// 向 Snackbar 布局中添加 View (Google 不建议, 复杂的布局应该使用 DialogFragment 进行展示) +SnackbarUtils.with(view).addView(viewId, 1); + +// = 结合使用 = + +// 只有调用了 showXxx, 才会进行设置样式, 并且显示 +SnackbarUtils.with(view) + .addView(viewId, 0) + .setStyle(new NightStyle()) + .setAction(new View.OnClickListener() { + @Override + public void onClick(View v) { + + } + }, "撤销") + .bellow(targetView, 0) + .setCallback(new Snackbar.Callback() { + @Override + public void onDismissed(Snackbar transientBottomBar, int event) { + } + }).setAutoCalc(true) + .showShort("已收藏该消息!"); + +// = + +// 通过已有样式创建 StyleBuilder 并修改样式效果使用 +SnackbarUtils.StyleBuilder styleBuilder1 = new SnackbarUtils.StyleBuilder(style); +styleBuilder1.setActionColor(Color.RED); +SnackbarUtils.with(view).setStyle(styleBuilder1).showShort("已收藏该消息!"); + +// 修改默认样式中的部分展示效果 +SnackbarUtils snackbarUtils1 = SnackbarUtils.with(view); +SnackbarUtils.StyleBuilder styleBuilder2 = snackbarUtils1.getStyle(); +styleBuilder2.setActionColor(Color.BLACK); +snackbarUtils1.setStyle(styleBuilder2); +``` + + +#### 自定义样式 +```java +/** + * detail: 自定义样式 - 可参照下方实现方法, 进行配置 + * @author Ttt + */ +class NightStyle extends SnackbarUtils.Style { + @Override + public int getTextColor() { + return Color.WHITE; + } + + @Override + public float getRootAlpha() { + return 0.5f; + } +} +``` + + +```java +/** + * detail: 样式相关 + * @author Ttt + */ +SnackbarUtils.Style style = new SnackbarUtils.Style() { + + // ============ + // = RootView = + // ============ + + /** + * RootView 的重心 + * @return + */ + public int getRootGravity() { + return 0; + } + + /** + * RootView 背景圆角大小 + * @return + */ + public float getRootCornerRadius() { + return 0f; + } + + /** + * RootView 背景着色颜色 + * @return + */ + public int getRootBackgroundTintColor() { + return 0; + } + + /** + * RootView 背景图片 + * @return + */ + public Drawable getRootBackground() { + return null; + } + + /** + * RootView margin 边距 - new int[] { left, top, right, bottom } + * @return + */ + public int[] getRootMargin() { + return null; + } + + /** + * RootView 透明度 + * @return + */ + public float getRootAlpha() { + return 1.0f; + } + + // = snackbar_text TextView 相关 = + + /** + * TextView 的重心 + * @return + */ + public int getTextGravity() { + return 0; + } + + /** + * TextView 文本颜色 + * @return + */ + public int getTextColor() { + return 0; + } + + /** + * TextView 字体大小 + * @return + */ + public float getTextSize() { + return 0f; + } + + /** + * TextView 最大行数 + * @return + */ + public int getTextMaxLines() { + return 0; + } + + /** + * TextView Ellipsize 效果 + * @return + */ + public TextUtils.TruncateAt getTextEllipsize() { + return null; + } + + /** + * TextView 字体样式 + * @return + */ + public Typeface getTextTypeface() { + return null; + } + + /** + * TextView padding 边距 - new int[] { left, top, right, bottom } + * @return + */ + public int[] getTextPadding() { + return null; + } + + // = snackbar_action Button 相关 = + + /** + * Action Button 的重心 + * @return + */ + public int getActionGravity() { + return 0; + } + + /** + * Action Button 文本颜色 + * @return + */ + public int getActionColor() { + return 0; + } + + /** + * Action Button 字体大小 + * @return + */ + public float getActionSize() { + return 0f; + } + + /** + * Action Button padding 边距 - new int[] { left, top, right, bottom } + * @return + */ + public int[] getActionPadding() { + return null; + } + + /** + * Action Button 背景圆角大小 + * @return + */ + public float getActionCornerRadius() { + return 0f; + } + + /** + * Action Button 背景着色颜色 + * @return + */ + public int getActionBackgroundTintColor() { + return 0; + } + + /** + * Action Button 背景图片 + * @return + */ + public Drawable getActionBackground() { + return null; + } +}; +``` \ No newline at end of file diff --git a/lib/DevApp/utils_readme/thread/DevThreadManager.md b/lib/DevApp/utils_readme/thread/DevThreadManager.md new file mode 100644 index 0000000000..1aa63470c4 --- /dev/null +++ b/lib/DevApp/utils_readme/thread/DevThreadManager.md @@ -0,0 +1,64 @@ +# 线程工具类 + +#### 使用演示类 [ThreadUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/thread/ThreadUse.java) 介绍了配置参数及使用 + +#### 项目类结构 - [包目录](https://github.com/afkT/DevUtils/tree/master/lib/DevApp/src/main/java/dev/utils/common/thread) + +* 线程池管理 - 开发类([DevThreadManager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadManager.java)):内部封装 DevThreadPool 配置处理, 方便直接使用 + +* 线程池 - 开发类([DevThreadPool.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadPool.java)):具体线程池操作方法、线程处理等 + +## API 文档 + +* **线程池管理 - 开发类 ->** [DevThreadManager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadManager.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 DevThreadManager 实例 | +| initConfig | 初始化配置信息 | +| putConfig | 添加配置信息 | +| removeConfig | 移除配置信息 | + + +* **线程池 - 开发类 ->** [DevThreadPool.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/common/thread/DevThreadPool.java) + +| 方法 | 注释 | +| :- | :- | +| execute | 加入到线程池任务队列 | +| shutdown | shutdown 会等待所有提交的任务执行完成, 不管是正在执行还是保存在任务队列中的已提交任务 | +| shutdownNow | shutdownNow会尝试中断正在执行的任务(其主要是中断一些指定方法如sleep方法), 并且停止执行等待队列中提交的任务 | +| isShutdown | 判断线程池是否已关闭 = isShutDown当调用shutdown()方法后返回为 true | +| isTerminated | 若关闭后所有任务都已完成, 则返回 true. | +| awaitTermination | 请求关闭、发生超时或者当前线程中断 | +| submit | 提交一个Callable任务用于执行 | +| invokeAll | 执行给定的任务 | +| invokeAny | 执行给定的任务 | +| schedule | 延迟执行Runnable命令 | +| scheduleWithFixedRate | 延迟并循环执行命令 | +| scheduleWithFixedDelay | 延迟并以固定休息时间循环执行命令 | + +#### 使用方法 +```java +Runnable runnable = new Runnable() { + @Override + public void run() { + + } +}; + +// = 优先判断 10个线程数, 的线程池是否存在, 不存在则创建, 存在则复用 = +DevThreadManager.getInstance(10).execute(runnable); + +// 与上面 传入 int 是完全不同的线程池 +DevThreadManager.getInstance("10").execute(runnable); + +// 可以先增加配置 +DevThreadManager.putConfig("QPQP", new DevThreadPool(DevThreadPool.DevThreadPoolType.CALC_CPU)); +// 使用配置的信息 +DevThreadManager.getInstance("QPQP").execute(runnable); + + +DevThreadManager.putConfig("QQQQQQ", 10); +// 使用配置的信息 +DevThreadManager.getInstance("QQQQQQ").execute(runnable); +``` \ No newline at end of file diff --git a/lib/DevApp/utils_readme/timer/TimerManager.md b/lib/DevApp/utils_readme/timer/TimerManager.md new file mode 100644 index 0000000000..9600b7d8ae --- /dev/null +++ b/lib/DevApp/utils_readme/timer/TimerManager.md @@ -0,0 +1,184 @@ +# 定时器工具类 + +#### 使用演示类 [TimerUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/timer/TimerUse.java) 介绍了配置参数及使用 + +```java +/** + * 主要是为了控制整个项目的定时器, 防止定时器混乱, 或者忘记关闭等情况, 以及减少初始化等操作代码 + * 主要实现是 AbsTimer、TimerTask 这两个类 + * AbsTimer 定时器抽象类, 对外提供该类对象以及内部方法便于内部实现方法的隐藏, 以达到对定时器任务的控制处理 + * TimerTask 内部私有类, 实现了具体的定时器操作以及代码控制等, 防止外部直接 new 导致定时器混乱 + *

+ * 如果外部想要实现定时器, 但是通过内部 ArrayList 控制, 也可以通过实现 AbsTimer 接口, 内部的 startTimer()、closeTimer() 进行了对 AbsTimer 的保存、标记等操作 + * 需要注意的是, 实现 start(close)Timer() 方法, 必须保留 super.start(close)Timer() 内部 ArrayList 进行了操作, 而不对外开放(不需要主动调用) + *

+ * startTimer() 主要进行添加到 ArrayList, 并且标记不需要回收 + * closeTimer() 不直接操作 remove, 防止出现 ConcurrentModificationException 异常, 而是做一个标记, 便于后续回收 + */ +``` + +#### 项目类结构 + +* 定时器工具类([TimerManager](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/assist/manager/TimerManager.java)):定时器工具类 + +* TimerManager.AbsTimer:定时器抽象类, 对外提供该类对象以及内部方法, 便于内部实现方法的隐藏, 以及达到对定时器任务的控制处理 + +* TimerManager.TimerTask:内部私有类, 实现了具体的定时器操作, 以及代码控制等, 防止外部直接 new, 导致定时器混乱 + + +#### 框架亮点 + +* 控制整个项目定时器, 防止定时器混乱、忘记关闭等情况, 统一控制管理 + +* 内部自动添加定时器到集合中, 便于项目控制处理 + +* 支持关闭指定 mark timer、all timer, 获取指定 Timer + +* 内部封装 Timer, 支持获取执行次数、是否无限循环、通知what、通知Obj、mark标记等通用功能 + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| gc | 回收资源 | +| timerSize | 获取全部任务总数 | +| getTimer | 获取属于对应字符串标记的定时器任务(优先获取符合的) | +| closeAll | 关闭全部任务 | +| closeInfiniteTask | 关闭所有无限循环的任务 | +| closeMark | 关闭所有符合对应的字符串标记的定时器任务 | +| createTimer | 创建定时器 ( 立即执行、无限循环、通知默认 what ) | +| getMarkId | 获取标记id | +| getMarkStr | 获取标记字符串 | +| setMarkId | 设置标记id | +| setMarkStr | 设置标记字符串 | +| startTimer | 运行定时器 | +| closeTimer | 关闭定时器 | +| isRunTimer | 判断是否运行中 | +| getTriggerNumber | 获取已经触发的次数 | +| getTriggerLimit | 获取允许触发的上限次数 | +| isTriggerEnd | 是否触发结束(到达最大次数) | +| isInfinite | 是否无限循环 | +| setHandler | 设置通知的Handler | +| setNotifyWhat | 设置通知的What | +| setNotifyObject | 设置通知的Obj | +| setTime | 设置时间 | +| setTriggerLimit | 设置触发次数上限 | + + +#### 使用方法 +```java +// 创建定时器 ( 立即执行、无限循环、通知默认 what ) +public static TimerManager.AbsTimer createTimer(Handler handler, long period) { + return createTimer(handler, TimerManager.AbsTimer.TIMER_NOTIFY_WHAT, 0L, period, -1); +} + +// 创建定时器 ( 无限循环、通知默认 what ) +public static TimerManager.AbsTimer createTimer(Handler handler, long delay, long period) { + return createTimer(handler, TimerManager.AbsTimer.TIMER_NOTIFY_WHAT, delay, period, -1); +} + +// 创建定时器 ( 立即执行、通知默认 what ) +public static TimerManager.AbsTimer createTimer(Handler handler, long period, int triggerLimit) { + return createTimer(handler, TimerManager.AbsTimer.TIMER_NOTIFY_WHAT, 0L, period, triggerLimit); +} + +// 创建定时器 ( 立即执行、无限循环 ) +public static TimerManager.AbsTimer createTimer(Handler handler, int what, long period) { + return createTimer(handler, what, 0L, period, -1); +} + +// 创建定时器 ( 无限循环 ) +public static TimerManager.AbsTimer createTimer(Handler handler, int what, long delay, long period) { + return createTimer(handler, what, delay, period, -1); +} + +// 创建定时器 ( 立即执行 ) +public static TimerManager.AbsTimer createTimer(Handler handler, int what, long period, int triggerLimit) { + return createTimer(handler, what, 0L, period, triggerLimit); +} + +/** + * 创建定时器 + * @param handler 通知的Handler + * @param what 通知的what + * @param delay 延迟时间 - 多少毫秒后开始执行 + * @param period 循环时间 - 每隔多少秒执行一次 + * @param triggerLimit 触发次数上限(-1 表示无限循环) + * @return + */ +public static TimerManager.AbsTimer createTimer(Handler handler, int what, long delay, long period, int triggerLimit) { + return new TimerManager.TimerTask(handler, what, delay, period, triggerLimit); +} + +// 初始化定时器任务 +absTimer = TimerManager.createTimer(new Handler() { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + + // 获取触发次数 + int number = absTimer.getTriggerNumber(); + // 触发次数 + if (number == 1) { + DevLogger.dTag(TAG, "第一次触发, 0.5秒延迟"); + } else { + DevLogger.dTag(TAG, "每隔2秒触发一次, 触发次数: " + number); + } + } +}, NOTIFY, 500L, 2000L, -1); +// 开始定时 +absTimer.startTimer(); + + +Handler handler = new Handler() { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + + switch (msg.what) { + case NOTIFY: + // 获取触发次数 + DevLogger.dTag(TAG, "已经触发次数: " + absTimer.getTriggerNumber()); + DevLogger.dTag(TAG, "触发上限次数: " + absTimer.getTriggerLimit()); + // 判断是否触发结束 + if (absTimer.isTriggerEnd()) { + DevLogger.dTag(TAG, "触发结束"); + } + break; + } + } +}; + +// 配置参数 - 意思是 一开始0秒直接触发第一次, 然后后面每隔60秒触发一次, 通过Handler通知 NOTIFY 常量 (-1表示无限次) +absTimer.setTriggerLimit(-1).setTime(0, 60 * 1000).setNotifyWhat(NOTIFY); + +// 配置参数 - 一秒钟后进行触发, 然后每隔1秒循环触发(但是触发一次 TriggerLimit 限制了次数), 并通过设置的Handler通知 对应传入的 What +absTimer.setHandler(handler).setTriggerLimit(1).setTime(1000, 1000).setNotifyWhat(NOTIFY); + +// 配置参数 - 3秒钟后进行触发, 然后每隔3秒循环触发(但是触发10次 TriggerLimit 限制了次数), 并通过设置的Handler通知 对应传入的 What, 并且开始定时器 +absTimer.setHandler(handler).setTriggerLimit(10).setTime(3000, 3000).setNotifyWhat(NOTIFY).startTimer(); + +// 开始运行定时器 +absTimer.startTimer(); + +// 关闭定时器 +absTimer.closeTimer(); + +// 判断是否运行中 +absTimer.isRunTimer(); + +// 关闭所有符合对应的标记id的定时器任务 +TimerManager.closeMark(id); + +// 关闭所有符合对应的字符串标记的定时器任务 +TimerManager.closeMark("mark"); + +// 关闭所有无限循环的任务 +TimerManager.closeInfiniteTask(); + +// 关闭全部任务 +TimerManager.closeAll(); + +// 回收资源 - 回收需要回收的 +TimerManager.gc(); +``` \ No newline at end of file diff --git a/lib/DevApp/utils_readme/toast/DevToast.md b/lib/DevApp/utils_readme/toast/DevToast.md new file mode 100644 index 0000000000..10af7aafbd --- /dev/null +++ b/lib/DevApp/utils_readme/toast/DevToast.md @@ -0,0 +1,257 @@ +# Toast 工具类 + +#### 使用演示类 [DevToastUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/toast/DevToastUse.java) 介绍了配置参数及使用 + +> 1. 支持子线程弹出 Toast, 可通过开关配置 +> 2. 内部解决 Android 7.1.1 崩溃问题 +> 3. 已处理 部分 ROM 如魅族、小米、三星等关闭应用通知, 无法显示 Toast 问题 + +#### 项目类结构 - [包目录](https://github.com/afkT/DevUtils/tree/master/lib/DevApp/src/main/java/dev/utils/app/toast/toaster) + +* Toast 工具类([DevToast](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/DevToast.java)):Toast 工具类(对外公开直接调用), 直接调用 IToastImpl 类方法 + +* Toast 接口([IToast](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/IToast.java)):主要编写 Operate 操作接口、Style 样式接口、Filter 过滤接口 + +* Toast 接口实现方法([IToastImpl](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/IToastImpl.java)):实现 Toast.Operate 接口, 并且对对应的方法, 进行处理 + +* Toast 工厂模式([ToastFactory](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/ToastFactory.java)):用于生成适配不同 Android 版本对应的 Toast, 以及解决无通知权限显示 Toast + +* Toast 内部工具类([Utils](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/Utils.java)):内部快捷操作工具类 + +* Toast 默认样式([DefaultToastStyle](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/toast/toaster/DefaultToastStyle.java)):该类实现 IToast.Style 用于配置自定义 Toast + + +#### 框架亮点 + +* 支持无通知权限 Toast 弹出 + +* 支持不分主次线程都可以弹出 Toast, 并可通过开关控制 + +* 支持自动区分资源 stringId, 支持可变参数传参, 自动格式化 Toast 内容 + +* 使用单例 Toast, 避免频繁弹出造成不良的用户体验 + +* 支持自动适配, 根据不同 Android 版本、是否有通知权限, 生成不同的 Toast 对象 + +* 支持自定义 View 扩展, 通过 setView, 重新自定义 Toast 布局 + +* 支持自定义样式:Toast(Gravity、背景、圆角、边距等)、TextView(样式、颜色、大小、Ellipsize等) + +* 支持全局配置样式:可通过配置全局通用样式, 或单独 Toast 特殊样式, 实现整个应用统一替换/设置 + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| reset | 重置默认参数 | +| setIsHandler | 设置是否使用 Handler 显示 Toast | +| setNullText | 设置 Text 为 null 的文本 | +| setTextLength | 设置 Toast 文案长度转换 显示时间 | +| init | Application 初始化调用 (内部已调用) | +| style | 使用单次 Toast 样式配置 | +| defaultStyle | 使用默认 Toast 样式 | +| getToastStyle | 获取 Toast 样式配置 | +| initStyle | 初始化 Toast 样式配置 | +| initToastFilter | 初始化 Toast 过滤器 | +| setView | 设置 Toast 显示的 View | +| show | 显示 Toast | +| cancel | 取消当前显示的 Toast | + + +#### 配置相关 + +```java +// 初始化 Toast - DevUtils 内部已经调用 +DevToast.init(application); // 必须调用 + +// 初始化 Toast 样式 - 全局通用 +// DevToast.initStyle(new IToast.Style() {}); // 可以实现 IToast.Style 接口, 参照 DefaultToastStyle + +// 当 Toast 内容为 null 时, 显示的内容 +DevToast.setNullText("text is null"); + +// 是否设置 Handler 显示 Toast - 默认 true, 支持子线程显示 Toast +DevToast.setIsHandler(true); + +// 设置文本长度限制, 超过设置的位数则 为 LENGTH_LONG +DevToast.setTextLength(15); + +// 支持自定义 View - 可不配置, 默认使用系统 Toast View +DevToast.setView(view); +DevToast.setView(viewId); + +// 配置 Toast 过滤, 判断是否显示 Toast、以及内容处理 +// DevToast.initToastFilter(new IToast.Filter() {}); + +// 恢复默认配置 +DevToast.reset(); +``` + + +#### 使用方法 +```java +// 显示 Toast +DevToast.show(view); +DevToast.show(R.string.app_name); +DevToast.show("Toast"); // initStyle - Toast + +// 使用特殊样式 - 默认统一全局样式, style 则为 这个 Toast 单独为这个样式 +DevToast.style(new TempStyle()).show("tempStyle - Toast"); + +// 获取 当前全局使用的样式 +DevToast.getToastStyle(); + +// 获取默认样式 +DevToast.defaultStyle(); + +// 关闭正在显示的 Toast +DevToast.cancel(); +``` + + +#### 自定义样式 +```java +/** + * 自定义实现样式 + */ +private static class TempStyle implements IToast.Style { + + /** + * Toast 的重心 + * @return + */ + @Override + public int getGravity() { + return 0; + } + + /** + * X轴偏移 + * @return + */ + @Override + public int getXOffset() { + return 0; + } + + /** + * Y轴偏移 + * @return + */ + @Override + public int getYOffset() { + return 0; + } + + /** + * 获取水平边距 + * @return + */ + @Override + public int getHorizontalMargin() { + return 0; + } + + /** + * 获取垂直边距 + * @return + */ + @Override + public int getVerticalMargin() { + return 0; + } + + /** + * Toast Z轴坐标阴影 + * @return + */ + @Override + public int getZ() { + return 0; + } + + /** + * 圆角大小 + * @return + */ + @Override + public float getCornerRadius() { + return 5f; + } + + /** + * 背景着色颜色 + * @return + */ + @Override + public int getBackgroundTintColor() { + return 0xB2000000; + } + + /** + * 背景图片 + * @return + */ + @Override + public Drawable getBackground() { + return null; + } + + // = TextView 相关 = + + /** + * 文本颜色 + * @return + */ + @Override + public int getTextColor() { + return Color.WHITE; + } + + /** + * 字体大小 + * @return + */ + @Override + public float getTextSize() { + return 16f; + } + + /** + * 最大行数 + * @return + */ + @Override + public int getMaxLines() { + return 0; + } + + /** + * Ellipsize 效果 + * @return + */ + @Override + public TextUtils.TruncateAt getEllipsize() { + return null; + } + + /** + * 字体样式 + * @return + */ + @Override + public Typeface getTypeface() { + // return Typeface.create("sans-serif-condensed", Typeface.NORMAL); + return null; + } + + /** + * TextView padding 边距 - new int[] { left, top, right, bottom } + * @return + */ + @Override + public int[] getPadding() { + return new int[] { 25, 10, 25, 10 }; + } +} +``` \ No newline at end of file diff --git a/lib/DevApp/utils_readme/toast/ToastTintUtils.md b/lib/DevApp/utils_readme/toast/ToastTintUtils.md new file mode 100644 index 0000000000..8bd7c02acd --- /dev/null +++ b/lib/DevApp/utils_readme/toast/ToastTintUtils.md @@ -0,0 +1,270 @@ +# Toast 美化工具类 + +#### 使用演示类 [ToastTintUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/toast/ToastTintUse.java) 介绍了配置参数及使用 + +> 1. 支持子线程弹出 Toast, 可通过开关配置 +> 2. 内部解决 Android 7.1.1 崩溃问题 +> 3. 但无处理 部分 ROM 如魅族、小米、三星等关闭应用通知, 无法显示 Toast 问题 + +#### 项目类结构 + +* Toast 工具类([ToastTintUtils](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/toast/ToastTintUtils.java)):Toast 美化工具类 + + +#### 框架亮点 + +* 支持不分主次线程都可以弹出 Toast, 并可通过开关控制 + +* 支持自动区分资源 stringId, 支持可变参数传参, 自动格式化 Toast 内容 + +* 支持自动适配, 根据不同 Android 版本, 生成不同的 Toast 对象 + +* 支持自定义样式:Toast(Gravity、背景、圆角、边距等)、TextView(样式、颜色、大小、Ellipsize等) + +* 默认几种样式 Toast (normal、info、warning、success、error), 并且支持统一变更设置样式、以及自定义样式 + +* 原始 Toast 基础上封装美化, 并且可通过 Style 控制 Toast 效果, 实现全局配置 Style 效果 + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| reset | 重置默认参数 | +| setToastFilter | 设置 Toast 过滤器 | +| setIsHandler | 设置是否使用 Handler 显示 Toast | +| setNullText | 设置 Text 为 null 的文本 | +| setUseConfig | 设置是否使用配置 | +| setGravity | 设置 Toast 显示在屏幕上的位置 | +| setMargin | 设置边距 | +| getDefaultStyle | 获取默认样式 | +| getNormalStyle | 获取 Normal 样式 | +| getInfoStyle | 获取 Info 样式 | +| getWarningStyle | 获取 Warning 样式 | +| getErrorStyle | 获取 Error 样式 | +| getSuccessStyle | 获取 Success 样式 | +| setNormalStyle | 设置 Normal 样式 | +| setInfoStyle | 设置 Info 样式 | +| setWarningStyle | 设置 Warning 样式 | +| setErrorStyle | 设置 Error 样式 | +| setSuccessStyle | 设置 Success 样式 | +| getInfoDrawable | 获取 Info 样式 icon | +| getWarningDrawable | 获取 Warning 样式 icon | +| getErrorDrawable | 获取 Error 样式 icon | +| getSuccessDrawable | 获取 Success 样式 icon | +| normal | normal 样式 Toast | +| info | info 样式 Toast | +| warning | warning 样式 Toast | +| error | error 样式 Toast | +| success | success 样式 Toast | +| custom | custom Toast | + + +#### 配置相关 + +```java +// 获取默认样式 +ToastTintUtils.getDefaultStyle(); + +// 获取 Normal 样式 +ToastTintUtils.getNormalStyle(); +// 设置 Normal 样式 +ToastTintUtils.setNormalStyle(style); + +// 获取 Error 样式 +ToastTintUtils.getErrorStyle(); +// 设置 Error 样式 +ToastTintUtils.setErrorStyle(style); +// 获取 Error 样式 小图标 +ToastTintUtils.getErrorDrawable(); + +// 获取 Warning 样式 +ToastTintUtils.getWarningStyle(); +// 设置 Warning 样式 +ToastTintUtils.setWarningStyle(style); +// 获取 Warning 样式 小图标 +ToastTintUtils.getWarningDrawable(); + +// 获取 Success 样式 +ToastTintUtils.getSuccessStyle(); +// 设置 Success 样式 +ToastTintUtils.setSuccessStyle(style); +// 获取 Success 样式 小图标 +ToastTintUtils.getSuccessDrawable(); + +// 获取 Info 样式 +ToastTintUtils.getInfoStyle(); +// 设置 Info 样式 +ToastTintUtils.setInfoStyle(style); +// 获取 Info 样式 小图标 +ToastTintUtils.getInfoDrawable(); + +// 是否使用配置 - 如 Gravity、HorizontalMargin、VerticalMargin +ToastTintUtils.setUseConfig(true); + +// 设置 Gravity +ToastTintUtils.setGravity(Gravity.BOTTOM, 0, 0); + +// 当 Toast 内容为 null 时, 显示的内容 +ToastTintUtils.setNullText("text is null"); + +// 是否设置 Handler 显示 Toast - 默认 true, 支持子线程显示 Toast +ToastTintUtils.setIsHandler(true); + +// 设置 HorizontalMargin、VerticalMargin 边距 +ToastTintUtils.setMargin(0f, 0f); + +// 配置 Toast 过滤, 判断是否显示 Toast、以及内容处理 +// ToastTintUtils.setToastFilter(new ToastTintUtils.Filter() {}); + +// 恢复默认配置 +ToastTintUtils.reset(); +``` + + +#### 使用方法 +```java +// 显示 Success 样式 Toast +ToastTintUtils.success("Success Style Toast"); + +// 显示 Error 样式 Toast +ToastTintUtils.error("Error Style Toast"); + +// 显示 Info 样式 Toast +ToastTintUtils.info("Info Style Toast"); + +// 显示 Normal 样式 Toast +ToastTintUtils.normal("Normal Style Toast"); + +// 显示 Warning 样式 Toast +ToastTintUtils.warning("Warning Style Toast"); + +// 显示 Custom 样式 Toast +ToastTintUtils.custom(style, "Custom Style Toast"); + +// 显示 Custom 样式 Toast, 自定义小图标 +ToastTintUtils.custom(new TempStyle(), "Custom Style Toast", iconDrawable); +``` + +# 预览 + +***Success 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/toast/toast_tint/success.png) + +***Error 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/toast/toast_tint/error.png) + +***Info 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/toast/toast_tint/info.png) + +***Normal 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/toast/toast_tint/normal.png) + +***Warning 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/toast/toast_tint/warning.png) + +***Custom 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/utils_readme/toast/toast_tint/custom.png) + + +#### 自定义样式 +```java +/** + * 自定义实现样式 + * {@link ToastTintUtils.SuccessStyle} + * {@link ToastTintUtils.ErrorStyle} + * {@link ToastTintUtils.InfoStyle} + * {@link ToastTintUtils.WarningStyle} + * {@link ToastTintUtils.NormalStyle} + * {@link ToastTintUtils.DefaultStyle} + */ +private static class TempStyle implements ToastTintUtils.Style { + + /** + * 文本颜色 + * @return + */ + @Override + public int getTextColor() { + return Color.WHITE; + } + + /** + * 字体大小 + * @return + */ + @Override + public float getTextSize() { + return 16f; + } + + /** + * 背景着色颜色 + * @return + */ + @Override + public int getBackgroundTintColor() { + return 0; + } + + /** + * 背景图片 + * @return + */ + @Override + public Drawable getBackground() { + return null; + } + + /** + * 最大行数 + * @return + */ + @Override + public int getMaxLines() { + return 0; + } + + /** + * Ellipsize 效果 + * @return + */ + @Override + public TextUtils.TruncateAt getEllipsize() { + return null; + } + + /** + * 字体样式 + * @return + */ + @Override + public Typeface getTypeface() { + // return Typeface.create("sans-serif-condensed", Typeface.NORMAL); + return null; + } + + /** + * 获取图标着色颜色 + * @return + */ + @Override + public int getTintIconColor() { + return Color.WHITE; + } + + /** + * 是否渲染图标 - getTintIconColor() 着色渲染 + * @return + */ + @Override + public boolean isTintIcon() { + return false; + } +} +``` \ No newline at end of file diff --git a/lib/DevApp/utils_readme/toast/toast_tint/custom.png b/lib/DevApp/utils_readme/toast/toast_tint/custom.png new file mode 100644 index 0000000000..b3e55948eb Binary files /dev/null and b/lib/DevApp/utils_readme/toast/toast_tint/custom.png differ diff --git a/lib/DevApp/utils_readme/toast/toast_tint/error.png b/lib/DevApp/utils_readme/toast/toast_tint/error.png new file mode 100644 index 0000000000..721686e0ce Binary files /dev/null and b/lib/DevApp/utils_readme/toast/toast_tint/error.png differ diff --git a/lib/DevApp/utils_readme/toast/toast_tint/info.png b/lib/DevApp/utils_readme/toast/toast_tint/info.png new file mode 100644 index 0000000000..bda47cf413 Binary files /dev/null and b/lib/DevApp/utils_readme/toast/toast_tint/info.png differ diff --git a/lib/DevApp/utils_readme/toast/toast_tint/normal.png b/lib/DevApp/utils_readme/toast/toast_tint/normal.png new file mode 100644 index 0000000000..a4240b2311 Binary files /dev/null and b/lib/DevApp/utils_readme/toast/toast_tint/normal.png differ diff --git a/lib/DevApp/utils_readme/toast/toast_tint/success.png b/lib/DevApp/utils_readme/toast/toast_tint/success.png new file mode 100644 index 0000000000..3e7594e64b Binary files /dev/null and b/lib/DevApp/utils_readme/toast/toast_tint/success.png differ diff --git a/lib/DevApp/utils_readme/toast/toast_tint/warning.png b/lib/DevApp/utils_readme/toast/toast_tint/warning.png new file mode 100644 index 0000000000..1c4f52b86c Binary files /dev/null and b/lib/DevApp/utils_readme/toast/toast_tint/warning.png differ diff --git a/lib/DevApp/utils_readme/wifi/WifiHotUtils.md b/lib/DevApp/utils_readme/wifi/WifiHotUtils.md new file mode 100644 index 0000000000..61a7f10a37 --- /dev/null +++ b/lib/DevApp/utils_readme/wifi/WifiHotUtils.md @@ -0,0 +1,85 @@ +# Wifi 热点工具类 + +#### 使用演示类 [WifiHotUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/wifi/WifiHotUse.java) 介绍了配置参数及使用 + +> 1. Android 8.0 开始, 热点操作方法, 已经变更 - https://blog.csdn.net/bukker/article/details/78649504 +> 2. Android 7.1 系统以上不支持自动开启热点, 需要手动开启热点 - https://www.jianshu.com/p/9dbb02c3e21f + +#### 项目类结构 + +* Wifi 热点工具类([WifiHotUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevApp/src/main/java/dev/utils/app/wifi/WifiHotUtils.java)):Wifi 热点工具类, 内部适配不同 Android 版本 api + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| createWifiConfigToAp | 创建 wifi 热点配置(支持 无密码/WPA2 PSK) | +| stratWifiAp | 开启 wifi 热点 | +| closeWifiAp | 关闭 wifi 热点 | +| getWifiApState | 获取 wifi 热点状态 | +| getWifiApConfiguration | 获取 wifi 热点配置信息 | +| setWifiApConfiguration | 设置 wifi 热点配置信息 | +| isOpenWifiAp | 判断是否打开 wifi 热点 | +| closeWifiApCheck | 关闭 wifi 热点(判断当前状态) | +| isConnectHot | 是否有设备连接热点 | +| getHotspotServiceIp | 获取热点主机 IP 地址 | +| getHotspotAllotIp | 获取连接上的子网关热点 IP(一个) | +| getConnectHotspotMsg | 获取连接的热点信息 | +| getHotspotSplitIpMask | 获取热点拼接后的 IP 网关掩码 | +| getApWifiSSID | 获取 wifi 热点名 | +| getApWifiPwd | 获取 wifi 热点密码 | +| setOnWifiAPListener | 设置 wifi 热点监听事件 | +| onStarted | 开启热点回调 | +| onStopped | 关闭热点回调 | +| onFailed | 失败回调 | + +#### 使用方法 +```java +// 所需权限 +// +// +// +// +// +// + +final WifiHotUtils wifiHotUtils = new WifiHotUtils(DevUtils.getContext()); + +// 有密码 +WifiConfiguration wifiConfiguration = WifiHotUtils.createWifiConfigToAp("WifiHot_AP", "123456789"); + +// 无密码 +wifiConfiguration = WifiHotUtils.createWifiConfigToAp("WifiHot_AP", null); + +// 开启热点(兼容8.0) 7.1 跳转到热点页面, 需手动开启(但是配置信息使用上面的 WifiConfig) +wifiHotUtils.stratWifiAp(wifiConfiguration); + +// 关闭热点 +wifiHotUtils.closeWifiAp(); + +// = 8.0 特殊处理 = + +// 8.0 以后热点是针对应用开启, 并且必须强制使用随机生成的 WifiConfig 信息, 无法替换 + +// 如果应用开启了热点, 然后后台清空内存, 对应的热点会关闭, 应用开启的热点是系统随机的, 不影响系统设置中的热点配置信息 + +if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + wifiHotUtils.setOnWifiAPListener(new WifiHotUtils.OnWifiAPListener() { + @Override + public void onStarted(WifiConfiguration wifiConfig) { + String ssid = wifiHotUtils.getApWifiSSID(); + String pwd = wifiHotUtils.getApWifiPwd(); + } + + @Override + public void onStopped() { + + } + + @Override + public void onFailed(int reason) { + + } + }); +} +``` \ No newline at end of file diff --git a/lib/DevAssist/.gitignore b/lib/DevAssist/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/lib/DevAssist/.gitignore @@ -0,0 +1 @@ +/build diff --git a/lib/DevAssist/CHANGELOG.md b/lib/DevAssist/CHANGELOG.md new file mode 100644 index 0000000000..a5121b1734 --- /dev/null +++ b/lib/DevAssist/CHANGELOG.md @@ -0,0 +1,21 @@ +Change Log +========== + + +Version 1.0.2 *(2019-09-19)* +---------------------------- + + * `[Update]` 修改 AbstractCallBack、AdapterDataAssist 部分方法返回值 (返回当前对象, 方便链式调用) + + +Version 1.0.1 *(2019-09-12)* +---------------------------- + + * `[Update]` DevJSONEngine#toJsonIndent 删除 param indent (缩进单位) + + +Version 1.0.0 *(2019-09-03)* +---------------------------- + + Initial release + diff --git a/lib/DevAssist/README.md b/lib/DevAssist/README.md new file mode 100644 index 0000000000..3d1da372e7 --- /dev/null +++ b/lib/DevAssist/README.md @@ -0,0 +1,636 @@ + +## Gradle + +```java +implementation 'com.afkt:DevAssist:1.0.2' +``` + +## 目录结构 + +``` +- dev | 根目录 + - assist | 快捷功能辅助类 + - adapter | Adapter 相关 + - multiselect | 多选模块 + - edit | 多选编辑方法 + - base | 基类相关 + - callback | 回调相关 + - click | 点击回调 + - common | 通用回调 + - result | 操作结果回调 + - engine | 兼容 Engine + - http | Http Engine + - image | Image Engine + - json | JSON Engine + - log | Log Engine +``` + + +## 事项 + +- 部分 API 更新不及时或有遗漏等,`具体以对应的工具类为准` + +- [检测代码规范、注释内容排版,API 文档生成](https://github.com/afkT/JavaDoc) + +## API + + +- dev | 根目录 + - [assist](#devassist) | 快捷功能辅助类 + - [adapter](#devassistadapter) | Adapter 相关 + - [multiselect](#devassistmultiselect) | 多选模块 + - [edit](#devassistmultiselectedit) | 多选编辑方法 + - [base](#devbase) | 基类相关 + - [callback](#devcallback) | 回调相关 + - [click](#devcallbackclick) | 点击回调 + - [common](#devcallbackcommon) | 通用回调 + - [result](#devcallbackresult) | 操作结果回调 + - [engine](#devengine) | 兼容 Engine + - [http](#devenginehttp) | Http Engine + - [image](#devengineimage) | Image Engine + - [json](#devenginejson) | JSON Engine + - [log](#devenginelog) | Log Engine + + + + +## **`dev`** + + +* **开发辅助类 ->** [DevAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/DevAssist.java) + +| 方法 | 注释 | +| :- | :- | +| getDevAssistUtilsVersion | 获取 DevAssist 工具类版本 | +| getDevAssistUtilsVersionCode | 获取 DevAssist 工具类版本号 | +| getDevJavaUtilsVersion | 获取 DevJava 工具类版本 | +| getDevJavaUtilsVersionCode | 获取 DevJava 工具类版本号 | + + +## **`dev.assist`** + + +* **解决 Adapter 多个 Item 存在 EditText 监听输入问题 ->** [EditTextWatcherAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/EditTextWatcherAssist.java) + +| 方法 | 注释 | +| :- | :- | +| bindListener | 绑定事件 | + + +* **数量控制辅助类 ->** [NumberControlAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/NumberControlAssist.java) + +| 方法 | 注释 | +| :- | :- | +| isMinNumber | 判断当前数量, 是否等于最小值 | +| isLessThanMinNumber | 判断数量, 是否小于最小值 | +| isGreaterThanMinNumber | 判断数量, 是否大于最小值 | +| isMaxNumber | 判断当前数量, 是否等于最大值 | +| isLessThanMaxNumber | 判断数量, 是否小于最大值 | +| isGreaterThanMaxNumber | 判断数量, 是否大于最大值 | +| getObject | 获取 Object | +| setObject | 设置 Object | +| getMinNumber | 获取最小值 | +| setMinNumber | 设置最小值 | +| getMaxNumber | 获取最大值 | +| setMaxNumber | 设置最大值 | +| setMinMaxNumber | 设置最小值、最大值 | +| getCurrentNumber | 获取当前数量 | +| setCurrentNumber | 设置当前数量 | +| getResetNumber | 获取重置数量 | +| setResetNumber | 设置重置数量 | +| isAllowNegative | 获取是否允许设置为负数 | +| setAllowNegative | 设置是否允许设置为负数 | +| numberChange | 数量改变通知 | +| addNumber | 添加数量 ( 默认累加 1) | +| subtractionNumber | 减少数量 ( 默认累减 1) | +| getNumberListener | 获取数量监听事件接口 | +| setNumberListener | 设置数量监听事件接口 | + + +* **Page 页数辅助类 ->** [PageAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/PageAssist.java) + +| 方法 | 注释 | +| :- | :- | +| initPageConfig | 初始化全局分页配置 | +| getPageNum | 获取当前请求页数 | +| setPageNum | 设置当前请求页数 | +| getPageNumReady | 获取当前 ( 正在 / 准备 ) 请求的页数 | +| setPageNumReady | 设置当前 ( 正在 / 准备 ) 请求的页数 | +| getPageSize | 获取每页请求条数 | +| setPageSize | 设置每页请求条数 | +| getTotalRow | 获取数据总条数 | +| setTotalRow | 设置数据总条数 | +| isLastPage | 判断是否最后一页 | +| setLastPage | 设置是否最后一页 | +| isFirstPage | 判断是否第一页 | +| isAllowNextPage | 判断是否允许请求下一页 | +| getNextPage | 获取下一页页数 | +| nextPage | 设置下一页页数 | +| getNextPageReady | 获取下一页页数 ( 预加载页数 ) | +| nextPageReady | 设置下一页页数 ( 预加载页数 ) | +| isLessThanPageSize | 判断是否小于每页请求条数 | +| reset | 重置操作 | + + +* **请求状态辅助类 ->** [RequestStatusAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/RequestStatusAssist.java) + +| 方法 | 注释 | +| :- | :- | +| getRequestType | 获取请求类型 | +| setRequestType | 设置请求类型 | +| equalsRequestType | 判断请求类型是否一致 | +| getRequestHashCode | 获取请求 HashCode | +| getRequestHashCodeRandom | 获取请求 HashCode - 随机生成并赋值 | +| equalsHashCode | 判断 HashCode 是否一致 | +| getRequestStatus | 获取请求状态 | +| setRequestStatus | 设置请求状态 | +| isRequestNormal | 判断是否默认状态 ( 暂未进行操作 ) | +| isRequestNever | 判断是否未请求过 | +| isRequestIng | 判断是否请求中 | +| isRequestSuccess | 判断是否请求成功 | +| isRequestFail | 判断是否请求失败 | +| isRequestError | 判断是否请求异常 | +| setRequestNormal | 设置状态为默认状态 ( 暂未进行操作 ) | +| setRequestNever | 设置状态为未请求过 | +| setRequestIng | 设置状态为请求中 | +| setRequestSuccess | 设置状态为请求成功 | +| setRequestFail | 设置状态为请求失败 | +| setRequestError | 设置状态为请求异常 | + + +* **变量辅助类 ->** [VariableAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/VariableAssist.java) + +| 方法 | 注释 | +| :- | :- | +| getBooleanVariable | 获取 Boolean 变量存储对象 | +| getObjectVariable | 获取 Object 变量存储对象 | + + +## **`dev.assist.adapter`** + + +* **Adapter 数据辅助类 ->** [AdapterDataAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/adapter/AdapterDataAssist.java) + +| 方法 | 注释 | +| :- | :- | +| setAdapterNotify | 设置 Adapter Notify | +| getDataList | 获取 List Data | +| getDataArrayList | 获取 ArrayList Data | +| getDataCount | 获取 List Count | +| getDataItem | 获取 List Position Item | +| getDataItemPosition | 获取 Value Position | +| getDataFirstItem | 获取 First Item Data | +| getDataLastItem | 获取 Last Item Data | +| isFirstItem | 判断是否 First Item Data | +| isLastItem | 判断是否 Last Item Data | +| clearDataList | 清空全部数据 | +| addData | 添加数据 | +| addAllData | 添加数据 | +| removeData | 移除数据 | +| setDataList | 设置 List Data | +| adapterNotifyDataSetChanged | 通知 Adapter 数据改变 | + + +* **Adapter 数据操作接口 ->** [IAdapterData.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/adapter/IAdapterData.java) + +| 方法 | 注释 | +| :- | :- | +| getDataList | 获取 List Data | +| getDataArrayList | 获取 ArrayList Data | +| getDataCount | 获取 List Count | +| getDataItem | 获取 List Position Item | +| getDataItemPosition | 获取 Value Position | +| getDataFirstItem | 获取 First Item Data | +| getDataLastItem | 获取 Last Item Data | +| isFirstItem | 判断是否 First Item Data | +| isLastItem | 判断是否 Last Item Data | +| clearDataList | 清空全部数据 | +| addData | 添加数据 | +| addAllData | 添加数据 | +| removeData | 移除数据 | +| setDataList | 设置 List Data | + + +* **Adapter 通知接口 ->** [IAdapterNotify.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/adapter/IAdapterNotify.java) + +| 方法 | 注释 | +| :- | :- | +| adapterNotifyDataSetChanged | 通知 Adapter 数据改变 | + + +## **`dev.assist.multiselect`** + + +* **多选操作接口 - 基类 ->** [IBaseMultiSelect.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/multiselect/IBaseMultiSelect.java) + +| 方法 | 注释 | +| :- | :- | +| clearSelects | 清空全部选中数据 | +| getSelectSize | 获取选中的数据条数 | +| getSelects | 获取选中的数据集合 | +| putSelects | 通过集合添加选中数据 | +| isSelect | 判断是否存在选中的数据 | +| isSelectValue | 判断是否选中 - 通过 value 判断 | +| unselectValue | 设置非选中 | +| unselectValueAll | 设置非选中 - 符合条件的全部 value | + + +* **多选操作接口 - List ->** [IMultiSelectToList.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/multiselect/IMultiSelectToList.java) + +| 方法 | 注释 | +| :- | :- | +| isSelect | 判断是否选中 - 通过 value 判断 | +| select | 设置选中操作 | +| unselect | 设置非选中操作 | +| toggle | 切换选中状态 | +| getSelectValues | 获取选中的数据集合 | +| getSelectValuesToReverse | 获取选中的数据集合 ( 倒序 ) | +| getSelectValue | 获取选中的数据 | +| getSelectValueToPosition | 获取选中的数据所在的索引 | + + +* **多选操作接口 - Map ->** [IMultiSelectToMap.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/multiselect/IMultiSelectToMap.java) + +| 方法 | 注释 | +| :- | :- | +| isSelect | 判断是否选中 - 如果未选中, 则设置为选中 | +| isSelectKey | 判断是否选中 - 通过 key 判断 | +| select | 设置选中操作 | +| unselect | 设置非选中操作 | +| toggle | 切换选中状态 | +| getSelectValue | 通过 key 获取 value | +| getSelectValues | 获取选中的数据集合 | +| getSelectValuesToReverse | 获取选中的数据集合 ( 倒序 ) | +| getSelectKey | 通过 value 获取 key | +| getSelectKeys | 获取选中的数据集合 | +| getSelectKeysToReverse | 获取选中的数据集合 ( 倒序 ) | + + +* **List 多选辅助类 ->** [MultiSelectListAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/multiselect/MultiSelectListAssist.java) + +| 方法 | 注释 | +| :- | :- | +| clearSelects | 清空全部选中数据 | +| getSelectSize | 获取选中的数据条数 | +| getSelects | 获取选中的数据集合 | +| putSelects | 通过集合添加选中数据 | +| isSelect | 判断是否存在选中的数据 | +| isSelectValue | 判断是否选中 - 通过 value 判断 | +| unselectValue | 设置非选中 | +| unselectValueAll | 设置非选中 - 符合条件的全部 value | +| select | 设置选中操作 | +| unselect | 设置非选中操作 | +| toggle | 切换选中状态 | +| getSelectValues | 获取选中的数据集合 | +| getSelectValuesToReverse | 获取选中的数据集合 ( 倒序 ) | +| getSelectValue | 获取选中的数据 | +| getSelectValueToPosition | 获取选中的数据所在的索引 | + + +* **Map 多选辅助类 ->** [MultiSelectMapAssist.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/multiselect/MultiSelectMapAssist.java) + +| 方法 | 注释 | +| :- | :- | +| clearSelects | 清空全部选中数据 | +| getSelectSize | 获取选中的数据条数 | +| getSelects | 获取选中的数据集合 | +| putSelects | 通过集合添加选中数据 | +| isSelect | 判断是否存在选中的数据 | +| isSelectValue | 判断是否选中 - 通过 value 判断 | +| unselectValue | 设置非选中 | +| unselectValueAll | 设置非选中 - 符合条件的全部 value | +| isSelectKey | 判断是否选中 - 通过 key 判断 | +| select | 设置选中操作 | +| unselect | 设置非选中操作 | +| toggle | 切换选中状态 | +| getSelectValue | 通过 key 获取 value | +| getSelectValues | 获取选中的数据集合 | +| getSelectValuesToReverse | 获取选中的数据集合 ( 倒序 ) | +| getSelectKey | 通过 value 获取 key | +| getSelectKeys | 获取选中的数据集合 | +| getSelectKeysToReverse | 获取选中的数据集合 ( 倒序 ) | + + +## **`dev.assist.multiselect.edit`** + + +* **多选编辑接口 ->** [IMultiSelectEdit.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/assist/multiselect/edit/IMultiSelectEdit.java) + +| 方法 | 注释 | +| :- | :- | +| isEditStatus | 是否编辑状态 | +| setEditStatus | 设置编辑状态 | +| toggleEditStatus | 切换编辑状态 | +| selectAll | 全选 | +| clearSelectAll | 清空全选 ( 非全选 ) | +| inverseSelect | 反选 | +| isSelectAll | 判断是否全选 | +| isSelect | 判断是否存在选中的数据 | +| isNotSelect | 判断是否不存在选中的数据 | + + +## **`dev.base`** + + +* **Key-Value Entry ->** [DevBaseEntry.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/base/DevBaseEntry.java) + +| 方法 | 注释 | +| :- | :- | +| getEntryKey | 获取 Entry Key | +| setEntryKey | 设置 Entry Key | +| getEntryValue | 获取 Entry Value | +| setEntryValue | 设置 Entry Value | +| isCorrect | 校验数据正确性 | + + +* **Event 基类 ->** [DevBaseEvent.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/base/DevBaseEvent.java) + +| 方法 | 注释 | +| :- | :- | +| getCode | 获取 Code | +| setCode | 设置 Code | +| getCodeStr | 获取 Code String | +| setCodeStr | 设置 Code String | +| getValue | 获取 Value | +| setValue | 设置 Value | +| getObject | 获取 Object | +| setObject | 设置 Object | +| equalsCode | 判断 Code 是否一致 | +| equalsValue | 判断 Value 是否一致 | +| equalsObject | 判断 Object 是否一致 | + + +* **Model 基类 ->** [DevBaseModel.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/base/DevBaseModel.java) + +| 方法 | 注释 | +| :- | :- | +| getUuid | 获取 UUID | +| getModelId | 获取 Model id | +| setModelId | 设置 Model id | +| isCorrect | 校验数据正确性 | + + +* **变量基类 ( 方便判断处理 ) ->** [DevBaseVariable.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/base/DevBaseVariable.java) + +| 方法 | 注释 | +| :- | :- | +| getVariables | 获取全部变量数据 | +| clearVariables | 清空全部变量数据 | +| putVariables | 保存变量数据集合 | +| getVariablesSize | 获取变量总数 | +| isVariables | 判断是否存在变量数据 | +| isVariableValue | 判断是否存在变量 - 通过 value 判断 | +| removeVariableValue | 删除指定变量数据 | +| removeVariableValueAll | 删除指定变量数据 - 符合条件的全部 value | +| isVariable | 判断是否存在变量 - 通过 key 判断 | +| putVariable | 保存变量数据 | +| removeVariable | 移除指定变量数据 - 通过 key 判断 | +| toggle | 切换变量数据存储状态 | +| getVariableValue | 通过 key 获取 value | +| getVariableValueConvert | 通过 key 获取 value | +| getVariableValues | 获取变量数据 value list | +| getVariableValuesToReverse | 获取变量数据 value list( 倒序 ) | +| getVariableKey | 通过 value 获取 key | +| getVariableKeys | 获取变量数据 key list | +| getVariableKeysToReverse | 获取变量数据 key list( 倒序 ) | + + +## **`dev.callback`** + + +* **抽象回调 - 基类 ->** [AbstractCallBack.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/callback/AbstractCallBack.java) + +| 方法 | 注释 | +| :- | :- | +| getUUID | 获取 UUID | +| getTag | 获取标记 Tag | +| setTag | 设置标记 Tag | +| getValue | 获取 Value | +| setValue | 设置 Value | +| getObject | 获取 Object | +| setObject | 设置 Object | + + +* **通用抽象回调 - 基类 ->** [AbstractCommonCallBack.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/callback/AbstractCommonCallBack.java) + +| 方法 | 注释 | +| :- | :- | +| onResult | 结果回调通知 | +| onError | 异常回调通知 | +| onFailure | 失败回调通知 | + + +## **`dev.callback.click`** + + +* **通用 Click 回调 ->** [DevClickCallBack.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/callback/click/DevClickCallBack.java) + +| 方法 | 注释 | +| :- | :- | +| onClick | 点击回调 | +| onLongClick | 长按回调 | + + +* **通用 Dialog 回调 ->** [DevDialogCallBack.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/callback/click/DevDialogCallBack.java) + +| 方法 | 注释 | +| :- | :- | +| onDialogNotify | 特殊通知 | +| onDialogShow | show 通知 | +| onDialogDismiss | dismiss 通知 | +| onDialogStart | start 通知 | +| onDialogResume | resume 通知 | +| onDialogPause | pause 通知 | +| onDialogStop | stop 通知 | +| onDialogDestroy | destroy 通知 | + + +* **通用 Item Click 回调 ->** [DevItemClickCallBack.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/callback/click/DevItemClickCallBack.java) + +| 方法 | 注释 | +| :- | :- | +| onItemClick | 点击 Item 回调 | +| onItemLongClick | 长按 Item 回调 | + + +## **`dev.callback.common`** + + +* **Dev 回调基类 ->** [DevCallBack.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/callback/common/DevCallBack.java) + +| 方法 | 注释 | +| :- | :- | +| callback | 回调方法 | + + +* **过滤处理回调类 ->** [DevFilterCallback.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/callback/common/DevFilterCallback.java) + +| 方法 | 注释 | +| :- | :- | +| filter | 过滤处理 | +| isFilter | 判断是否过滤 | +| compare | 对比过滤处理 | + + +## **`dev.callback.result`** + + +* **通用结果回调类 ( 针对 DevResultCallback 进行扩展 ) ->** [DevExResultCallback.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/callback/result/DevExResultCallback.java) + +| 方法 | 注释 | +| :- | :- | +| onResult | 结果回调通知 | +| onError | 异常回调通知 | +| onFailure | 失败回调通知 | +| getExpandResult | 获取实体类 | +| setExpandResult | 设置实体类 | + + +* **通用结果回调类 ->** [DevResultCallback.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/callback/result/DevResultCallback.java) + +| 方法 | 注释 | +| :- | :- | +| onResult | 结果回调通知 | + + +## **`dev.engine`** + + +## **`dev.engine.http`** + + +* **Http Engine ->** [DevHttpEngine.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/engine/http/DevHttpEngine.java) + +| 方法 | 注释 | +| :- | :- | +| initEngine | 初始化 Engine | +| newCall | 获取 Request Call Object | +| cancelAll | 取消请求 ( 全部 ) | +| cancelCall | 取消请求 | +| cancelUrl | 取消请求 | +| cancelTag | 取消请求 | + + +* **Http Engine 接口 ->** [IHttpEngine.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/engine/http/IHttpEngine.java) + +| 方法 | 注释 | +| :- | :- | +| newCall | 获取 Request Call Object | +| cancelAll | 取消请求 ( 全部 ) | +| cancelCall | 取消请求 | +| cancelUrl | 取消请求 | +| cancelTag | 取消请求 | +| getRequest | 获取 Request Object | +| getSentRequestAtMillis | 获取发送请求时间 | +| getReceivedResponseAtMillis | 获取请求响应时间 | +| isCanceled | 是否取消请求 | +| isExecuted | 是否执行过请求 | +| isEnd | 是否请求结束 | +| cancel | 取消请求 | +| start | 开始请求方法 ( 同步 ) | +| startAync | 开始请求方法 ( 异步 ) | +| onStart | 开始请求 | +| onCancel | 请求取消 | +| onResponse | 请求响应 | +| onFailure | 请求失败 | + + +## **`dev.engine.image`** + + +* **Image Engine ->** [DevImageEngine.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/engine/image/DevImageEngine.java) + +| 方法 | 注释 | +| :- | :- | +| initEngine | 初始化 Engine | +| displayImage | 图片显示 | +| loadImage | 图片加载 | +| clearDiskCache | 清除磁盘缓存 | +| clearMemoryCache | 清除内存缓存 | + + +* **Image Engine 接口 ->** [IImageEngine.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/engine/image/IImageEngine.java) + +| 方法 | 注释 | +| :- | :- | +| displayImage | 图片显示 | +| loadImage | 图片加载 | +| clearDiskCache | 清除磁盘缓存 | +| clearMemoryCache | 清除内存缓存 | +| getTranscodeType | 获取转码类型 | +| onResponse | 响应回调 | +| onFailure | 失败回调 | + + +## **`dev.engine.json`** + + +* **JSON Engine ->** [DevJSONEngine.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/engine/json/DevJSONEngine.java) + +| 方法 | 注释 | +| :- | :- | +| initEngine | 初始化 Engine | +| toJson | 将对象转换为 JSON String | +| fromJson | 将 JSON String 映射为指定类型对象 | +| isJSON | 判断字符串是否 JSON 格式 | +| toJsonIndent | JSON String 缩进处理 | + + +* **JSON Engine 接口 ->** [IJSONEngine.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/engine/json/IJSONEngine.java) + +| 方法 | 注释 | +| :- | :- | +| toJson | 将对象转换为 JSON String | +| fromJson | 将 JSON String 映射为指定类型对象 | +| isJSON | 判断字符串是否 JSON 格式 | +| toJsonIndent | JSON String 缩进处理 | + + +## **`dev.engine.log`** + + +* **Log Engine ->** [DevLogEngine.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/engine/log/DevLogEngine.java) + +| 方法 | 注释 | +| :- | :- | +| initEngine | 初始化 Engine | +| isPrintLog | 判断是否打印日志 | +| d | 打印 Log.DEBUG | +| e | 打印 Log.ERROR | +| w | 打印 Log.WARN | +| i | 打印 Log.INFO | +| v | 打印 Log.VERBOSE | +| wtf | 打印 Log.ASSERT | +| json | 格式化 JSON 格式数据, 并打印 | +| xml | 格式化 XML 格式数据, 并打印 | +| dTag | 打印 Log.DEBUG | +| eTag | 打印 Log.ERROR | +| wTag | 打印 Log.WARN | +| iTag | 打印 Log.INFO | +| vTag | 打印 Log.VERBOSE | +| wtfTag | 打印 Log.ASSERT | +| jsonTag | 格式化 JSON 格式数据, 并打印 | +| xmlTag | 格式化 XML 格式数据, 并打印 | + + +* **Log Engine 接口 ->** [ILogEngine.java](https://github.com/afkT/DevUtils/blob/master/lib/DevAssist/src/main/java/dev/engine/log/ILogEngine.java) + +| 方法 | 注释 | +| :- | :- | +| isPrintLog | 判断是否打印日志 | +| d | 打印 Log.DEBUG | +| e | 打印 Log.ERROR | +| w | 打印 Log.WARN | +| i | 打印 Log.INFO | +| v | 打印 Log.VERBOSE | +| wtf | 打印 Log.ASSERT | +| json | 格式化 JSON 格式数据, 并打印 | +| xml | 格式化 XML 格式数据, 并打印 | +| dTag | 打印 Log.DEBUG | +| eTag | 打印 Log.ERROR | +| wTag | 打印 Log.WARN | +| iTag | 打印 Log.INFO | +| vTag | 打印 Log.VERBOSE | +| wtfTag | 打印 Log.ASSERT | +| jsonTag | 格式化 JSON 格式数据, 并打印 | +| xmlTag | 格式化 XML 格式数据, 并打印 | \ No newline at end of file diff --git a/lib/DevAssist/build.gradle b/lib/DevAssist/build.gradle new file mode 100644 index 0000000000..f7a75f17ce --- /dev/null +++ b/lib/DevAssist/build.gradle @@ -0,0 +1,20 @@ +apply { + from rootProject.file(files.app_lib_gradle) +} + +android.defaultConfig { + versionCode versions.dev_assist_versionCode + versionName versions.dev_assist_versionName + // DevJava Module Version + buildConfigField "String", "DevJava_Version", "\"${versions.dev_java_version}\"" + buildConfigField "int", "DevJava_VersionCode", "${versions.dev_java_versionCode}" +} + +dependencies { + compileOnly project(path: ':DevJava') +} + +// gradlew clean +// gradlew install +// gradlew bintrayUpload +//apply from : rootProject.file(files.app_bintray_upload) \ No newline at end of file diff --git a/lib/DevAssist/proguard-rules.pro b/lib/DevAssist/proguard-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/DevAssist/project.properties b/lib/DevAssist/project.properties new file mode 100644 index 0000000000..b63f25e8ba --- /dev/null +++ b/lib/DevAssist/project.properties @@ -0,0 +1,10 @@ +#project +project.name=DevAssist +project.groupId=com.afkt +project.artifactId=DevAssist +project.packaging=aar +project.siteUrl=https://github.com/afkT/DevUtils +project.gitUrl=https://github.com/afkT/DevUtils.git + +#javadoc +javadoc.name=DevAssist \ No newline at end of file diff --git a/lib/DevAssist/src/main/AndroidManifest.xml b/lib/DevAssist/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..537a89d614 --- /dev/null +++ b/lib/DevAssist/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/lib/DevAssist/src/main/java/dev/DevAssist.java b/lib/DevAssist/src/main/java/dev/DevAssist.java new file mode 100644 index 0000000000..ea3f4c93c0 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/DevAssist.java @@ -0,0 +1,62 @@ +package dev; + +import dev.assist.BuildConfig; + +/** + * detail: 开发辅助类 + * @author Ttt + *
+ *     GitHub
+ *     @see 
+ *     DevApp Api
+ *     @see 
+ *     DevJava Api
+ *     @see 
+ *     DevAssist Api
+ *     @see 
+ * 
+ */ +public final class DevAssist { + + private DevAssist() { + } + + // 日志 TAG + private static final String TAG = DevAssist.class.getSimpleName(); + + // ============== + // = 工具类版本 = + // ============== + + /** + * 获取 DevAssist 工具类版本 + * @return DevAssist versionName + */ + public static String getDevAssistUtilsVersion() { + return BuildConfig.VERSION_NAME; + } + + /** + * 获取 DevAssist 工具类版本号 + * @return DevAssist versionCode + */ + public static int getDevAssistUtilsVersionCode() { + return BuildConfig.VERSION_CODE; + } + + /** + * 获取 DevJava 工具类版本 + * @return DevJava version + */ + public static String getDevJavaUtilsVersion() { + return BuildConfig.DevJava_Version; + } + + /** + * 获取 DevJava 工具类版本号 + * @return DevJava version + */ + public static int getDevJavaUtilsVersionCode() { + return BuildConfig.DevJava_VersionCode; + } +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/assist/EditTextWatcherAssist.java b/lib/DevAssist/src/main/java/dev/assist/EditTextWatcherAssist.java new file mode 100644 index 0000000000..5fdd126710 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/EditTextWatcherAssist.java @@ -0,0 +1,166 @@ +package dev.assist; + +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.View; +import android.widget.EditText; + +/** + * detail: 解决 Adapter 多个 Item 存在 EditText 监听输入问题 + * @author Ttt + */ +public class EditTextWatcherAssist { + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 绑定事件 + * @param text 待设置文本 + * @param position 索引 + * @param editText EditText + * @param listener 输入监听回调事件 + */ + public void bindListener(final CharSequence text, final int position, + final EditText editText, final InputListener listener) { + bindListener(text, position, editText, null, listener); + } + + /** + * 绑定事件 + * @param text 待设置文本 + * @param position 索引 + * @param editText EditText + * @param object Object + * @param listener 输入监听回调事件 + */ + public void bindListener(final CharSequence text, final int position, + final EditText editText, final T object, final InputListener listener) { + if (editText != null) { + // 设置内容 + editText.setText(TextUtils.isEmpty(text) ? "" : text); + // 清空焦点 + editText.clearFocus(); + // 设置获取焦点事件 + editText.setOnFocusChangeListener(new FocusListener(position, editText, object, listener)); + } + } + + // ================ + // = 内部判断方法 = + // ================ + + /** + * detail: 输入监听回调事件 + * @param 泛型 + * @author Ttt + */ + public interface InputListener { + + /** + * 文本改变监听 + * @param charSequence 改变文本 + * @param editText EditText + * @param position 索引 + * @param object Object + */ + void onTextChanged(CharSequence charSequence, EditText editText, int position, T object); + } + + // ================================ + // = 处理 Adapter Item - EditText = + // ================================ + + // Text 改变事件 + private TextWatcher mTextWatcher; + // 获得焦点的 EditText + private EditText mFocusEdit; + // 获得焦点的索引 + private int mFocusPos; + + /** + * 焦点改变 + * @param editText EditText + * @param position 索引 + */ + private void focusChange(final EditText editText, final int position) { + if (mTextWatcher != null) { + if (mFocusEdit != null) { + mFocusEdit.removeTextChangedListener(mTextWatcher); + } + mTextWatcher = null; + } + // 保存获得焦点的 EditText + mFocusEdit = editText; + // 保存索引 + mFocusPos = position; + } + + // = + + /** + * detail: 焦点事件监听 + * @author Ttt + */ + private class FocusListener implements View.OnFocusChangeListener { + + // 当前索引 + private int position; + // EditText + private EditText editText; + // Object + private T object; + // 输入监听事件 + private InputListener listener; + + /** + * 构造函数 + * @param position 索引 + * @param editText EditText + * @param object Object + * @param listener 输入监听回调事件 + */ + public FocusListener(int position, EditText editText, T object, InputListener listener) { + this.position = position; + this.editText = editText; + this.object = object; + this.listener = listener; + } + + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { + // 获得焦点设置 View 操作 + focusChange(editText, position); + // 判断是否为 null + if (mTextWatcher == null) { + mTextWatcher = new TextWatcher() { + @Override + public void onTextChanged(CharSequence charSequence, int start, int before, int count) { + if (mFocusPos == position) { + if (listener != null) { // 触发回调 + listener.onTextChanged(charSequence, editText, position, object); + } + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void afterTextChanged(Editable s) { + } + }; + } + if (mFocusEdit != null) { // 增加监听 + mFocusEdit.addTextChangedListener(mTextWatcher); + } + } else { // 失去焦点, 清空操作 + focusChange(null, -1); + } + } + } +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/assist/NumberControlAssist.java b/lib/DevAssist/src/main/java/dev/assist/NumberControlAssist.java new file mode 100644 index 0000000000..fd61711a41 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/NumberControlAssist.java @@ -0,0 +1,621 @@ +package dev.assist; + +/** + * detail: 数量控制辅助类 + * @author Ttt + *
+ *      主要用于数量加减限制
+ *      如: 购物车数量加减
+ * 
+ */ +public class NumberControlAssist { + + // Object + private Object mObject = null; + // 最小值 + private int mMinNumber = 1; + // 最大值 + private int mMaxNumber = Integer.MAX_VALUE; + // 当前数量 + private int mCurrentNumber = 1; + // 重置数量 - 出现异常情况, 则使用该变量赋值 + private int mResetNumber = 1; + // 是否允许设置为负数 + private boolean mIsAllowNegative = false; + + public NumberControlAssist() { + } + + public NumberControlAssist(final int minNumber) { + this.mMinNumber = minNumber; + } + + public NumberControlAssist(final int minNumber, final int maxNumber) { + this.mMinNumber = minNumber; + this.mMaxNumber = maxNumber; + } + + // ================ + // = 对外提供方法 = + // ================ + + /** + * 判断当前数量, 是否等于最小值 + * @return {@code true} yes, {@code false} no + */ + public boolean isMinNumber() { + return isMinNumber(mCurrentNumber); + } + + /** + * 判断数量, 是否等于最小值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + public boolean isMinNumber(final int number) { + return number == mMinNumber; + } + + /** + * 判断数量, 是否小于最小值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + public boolean isLessThanMinNumber(final int number) { + return number < mMinNumber; + } + + /** + * 判断数量, 是否大于最小值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + public boolean isGreaterThanMinNumber(final int number) { + return number > mMinNumber; + } + + // = + + /** + * 判断当前数量, 是否等于最大值 + * @return {@code true} yes, {@code false} no + */ + public boolean isMaxNumber() { + return isMaxNumber(mCurrentNumber); + } + + /** + * 判断数量, 是否等于最大值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + public boolean isMaxNumber(final int number) { + return number == mMaxNumber; + } + + /** + * 判断数量, 是否小于最大值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + public boolean isLessThanMaxNumber(final int number) { + return number < mMaxNumber; + } + + /** + * 判断数量, 是否大于最大值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + public boolean isGreaterThanMaxNumber(final int number) { + return number > mMaxNumber; + } + + // =========== + // = get/set = + // =========== + + /** + * 获取 Object + * @param 泛型 + * @return Object convert T object + */ + public T getObject() { + try { + return (T) mObject; + } catch (Exception e) { + } + return null; + } + + /** + * 设置 Object + * @param object Object + * @return {@link NumberControlAssist} + */ + public NumberControlAssist setObject(final Object object) { + this.mObject = object; + return this; + } + + // = + + /** + * 获取最小值 + * @return 最小值 + */ + public int getMinNumber() { + return mMinNumber; + } + + /** + * 设置最小值 + *
+     *      内部判断了, 是否大于 mMaxNumber, 属于的话则自动赋值 mMaxNumber
+     * 
+ * @param minNumber 最小值 + * @return {@link NumberControlAssist} + */ + public NumberControlAssist setMinNumber(final int minNumber) { + int number = minNumber; + // 如果不允许为负数, 并且设置最小值为负数, 则设置为重置数量 + if (!mIsAllowNegative && number < 0) { + number = mResetNumber; + } + if (number > mMaxNumber) { + number = mMaxNumber; + } + this.mMinNumber = number; + return this; + } + + // = + + /** + * 获取最大值 + * @return 最大值 + */ + public int getMaxNumber() { + return mMaxNumber; + } + + /** + * 设置最大值 + *
+     *      内部判断了, 是否小于 mMinNumber, 属于的话则自动赋值 mMinNumber
+     *      特殊情况 ( 修改为负数 ), 最好先调用 setMinNumber, 在调用 setMaxNumber
+     * 
+ * @param maxNumber 最大值 + * @return {@link NumberControlAssist} + */ + public NumberControlAssist setMaxNumber(final int maxNumber) { + this.mMaxNumber = (maxNumber < mMinNumber) ? mMinNumber : maxNumber; + return this; + } + + /** + * 设置最小值、最大值 + * @param minNumber 最小值 + * @param maxNumber 最大值 + * @return {@link NumberControlAssist} + */ + public NumberControlAssist setMinMaxNumber(final int minNumber, final int maxNumber) { + return setMinNumber(minNumber).setMaxNumber(maxNumber); + } + + // = + + /** + * 获取当前数量 + * @return 当前数量 + */ + public int getCurrentNumber() { + return mCurrentNumber; + } + + /** + * 设置当前数量 + * @param currentNumber 当前数量 + * @return {@link NumberControlAssist} + */ + public NumberControlAssist setCurrentNumber(final int currentNumber) { + return setCurrentNumber(currentNumber, true); + } + + /** + * 设置当前数量 + * @param currentNumber 当前数量 + * @param isTriggerListener 是否触发事件 + * @return {@link NumberControlAssist} + */ + public NumberControlAssist setCurrentNumber(final int currentNumber, final boolean isTriggerListener) { + int number = currentNumber; + if (number < mMinNumber) { + number = mMinNumber; + } else if (number > mMaxNumber) { + number = mMaxNumber; + } + // 判断是否添加 + boolean isAdd = (number > mCurrentNumber); + // 重新赋值 + this.mCurrentNumber = number; + // 判断是否触发事件 + if (isTriggerListener) { + if (mINumberListener != null) { + mINumberListener.onNumberChanged(isAdd, number); + } + } + return this; + } + + // = + + /** + * 获取重置数量 + * @return 重置数量 + */ + public int getResetNumber() { + return mResetNumber; + } + + /** + * 设置重置数量 + * @param resetNumber 重置数量 + * @return {@link NumberControlAssist} + */ + public NumberControlAssist setResetNumber(final int resetNumber) { + // 防止出现负数 + this.mResetNumber = (!mIsAllowNegative && mResetNumber < 0) ? 1 : resetNumber; + return this; + } + + // = + + /** + * 获取是否允许设置为负数 + * @return {@code true} yes, {@code false} no + */ + public boolean isAllowNegative() { + return mIsAllowNegative; + } + + /** + * 设置是否允许设置为负数 + * @param allowNegative {@code true} yes, {@code false} no + * @return {@link NumberControlAssist} + */ + public NumberControlAssist setAllowNegative(final boolean allowNegative) { + this.mIsAllowNegative = allowNegative; + // 进行检查更新 + checkUpdate(); + return this; + } + + // ================ + // = 数量变化方法 = + // ================ + + /** + * 数量改变通知 + * @param number 变化基数数量 + * @return {@link NumberControlAssist} + */ + public NumberControlAssist numberChange(final int number) { + if (mINumberListener != null) { + // 计算之后的数量 + int afterNumber = (mCurrentNumber + number); + // 判断是否添加, 大于等于当前数量, 表示添加 + boolean isAdd = afterNumber >= mCurrentNumber; + // 进行判断 + if (mINumberListener.onPrepareChanged(isAdd, mCurrentNumber, afterNumber)) { + // 进行设置当前数量 + setCurrentNumber(afterNumber, true); + } + } + return this; + } + + /** + * 添加数量 ( 默认累加 1) + * @return {@link NumberControlAssist} + */ + public NumberControlAssist addNumber() { + return numberChange(1); + } + + /** + * 减少数量 ( 默认累减 1) + * @return {@link NumberControlAssist} + */ + public NumberControlAssist subtractionNumber() { + return numberChange(-1); + } + + // ================ + // = 内部判断方法 = + // ================ + + /** + * 检查更新处理 + */ + private void checkUpdate() { + if (!mIsAllowNegative && mResetNumber < 0) { + mResetNumber = 1; + } + if (!mIsAllowNegative && mMinNumber < 0) { + mMinNumber = mResetNumber; + } + if (!mIsAllowNegative && mMaxNumber < 0) { + mMaxNumber = mResetNumber; + } + // 重置最大值 + setMaxNumber(mMaxNumber); + } + + // ============ + // = 接口相关 = + // ============ + + /** + * detail: 数量操作接口 + * @param 泛型 + * @author Ttt + *
+     *     每个接口方法直接通过调用 NumberControlAssist 已实现同名方法即可
+     * 
+ */ + public interface INumberOperate { + + /** + * 判断当前数量, 是否等于最小值 + * @return {@code true} yes, {@code false} no + */ + boolean isMinNumber(); + + /** + * 判断数量, 是否等于最小值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + boolean isMinNumber(int number); + + /** + * 判断数量, 是否小于最小值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + boolean isLessThanMinNumber(int number); + + /** + * 判断数量, 是否大于最小值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + boolean isGreaterThanMinNumber(int number); + + // = + + /** + * 判断当前数量, 是否等于最大值 + * @return {@code true} yes, {@code false} no + */ + boolean isMaxNumber(); + + /** + * 判断数量, 是否等于最大值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + boolean isMaxNumber(int number); + + /** + * 判断数量, 是否小于最大值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + boolean isLessThanMaxNumber(int number); + + /** + * 判断数量, 是否大于最大值 + * @param number Number + * @return {@code true} yes, {@code false} no + */ + boolean isGreaterThanMaxNumber(int number); + + // =========== + // = get/set = + // =========== + + /** + * 获取 Object + * @param 泛型 + * @return Object convert T object + */ + T getObject(); + + /** + * 设置 Object + * @param object Object + * @return R 泛型返回对象 + */ + R setObject(Object object); + + // = + + /** + * 获取最小值 + * @return 最小值 + */ + int getMinNumber(); + + /** + * 设置最小值 + *
+         *      内部判断了, 是否大于 mMaxNumber, 属于的话则自动赋值 mMaxNumber
+         * 
+ * @param minNumber 最小值 + * @return R 泛型返回对象 + */ + R setMinNumber(int minNumber); + + // = + + /** + * 获取最大值 + * @return 最大值 + */ + int getMaxNumber(); + + /** + * 设置最大值 + *
+         *      内部判断了, 是否小于 mMinNumber, 属于的话则自动赋值 mMinNumber
+         *      特殊情况 ( 修改为负数 ), 最好先调用 setMinNumber, 在调用 setMaxNumber
+         * 
+ * @param maxNumber 最大值 + * @return R 泛型返回对象 + */ + R setMaxNumber(int maxNumber); + + /** + * 设置最小值、最大值 + * @param minNumber 最小值 + * @param maxNumber 最大值 + * @return R 泛型返回对象 + */ + R setMinMaxNumber(int minNumber, int maxNumber); + + // = + + /** + * 获取当前数量 + * @return 当前数量 + */ + int getCurrentNumber(); + + /** + * 设置当前数量 + * @param currentNumber 当前数量 + * @return R 泛型返回对象 + */ + R setCurrentNumber(int currentNumber); + + /** + * 设置当前数量 + * @param currentNumber 当前数量 + * @param isTriggerListener 是否触发事件 + * @return R 泛型返回对象 + */ + R setCurrentNumber(int currentNumber, boolean isTriggerListener); + + // = + + /** + * 获取重置数量 + * @return 重置数量 + */ + int getResetNumber(); + + /** + * 设置重置数量 + * @param resetNumber 重置数量 + * @return R 泛型返回对象 + */ + R setResetNumber(int resetNumber); + + // = + + /** + * 获取是否允许设置为负数 + * @return {@code true} yes, {@code false} no + */ + boolean isAllowNegative(); + + /** + * 设置是否允许设置为负数 + * @param allowNegative {@code true} yes, {@code false} no + * @return R 泛型返回对象 + */ + R setAllowNegative(boolean allowNegative); + + // = + + /** + * 数量改变通知 + * @param number 变化基数数量 + * @return R 泛型返回对象 + */ + R numberChange(int number); + + /** + * 添加数量 ( 默认累加 1) + * @return R 泛型返回对象 + */ + R addNumber(); + + /** + * 减少数量 ( 默认累减 1) + * @return R 泛型返回对象 + */ + R subtractionNumber(); + + // = + + /** + * 获取数量监听事件接口 + * @return {@link INumberListener} + */ + INumberListener getNumberListener(); + + /** + * 设置数量监听事件接口 + * @param iNumberListener {@link INumberListener} + * @return R 泛型返回对象 + */ + R setNumberListener(INumberListener iNumberListener); + } + + /** + * detail: 数量监听事件接口 + * @author Ttt + */ + public interface INumberListener { + + /** + * 数量准备变化通知 + * @param isAdd 是否增加 + * @param curNumber 当前数量 + * @param afterNumber 处理之后的数量 + * @return {@code true} allow, {@code false} prohibit + */ + boolean onPrepareChanged(boolean isAdd, int curNumber, int afterNumber); + + /** + * 数量变化通知 + * @param isAdd 是否增加 + * @param curNumber 当前数量 + */ + void onNumberChanged(boolean isAdd, int curNumber); + } + + // 数量监听事件接口 + private INumberListener mINumberListener; + + /** + * 获取数量监听事件接口 + * @return {@link INumberListener} + */ + public INumberListener getNumberListener() { + return mINumberListener; + } + + /** + * 设置数量监听事件接口 + * @param iNumberListener {@link INumberListener} + * @return {@link NumberControlAssist} + */ + public NumberControlAssist setNumberListener(final INumberListener iNumberListener) { + this.mINumberListener = iNumberListener; + return this; + } +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/assist/PageAssist.java b/lib/DevAssist/src/main/java/dev/assist/PageAssist.java new file mode 100644 index 0000000000..356196e557 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/PageAssist.java @@ -0,0 +1,294 @@ +package dev.assist; + +/** + * detail: Page 页数辅助类 + * @author Ttt + */ +public class PageAssist extends RequestStatusAssist { + + // 全局默认初始化页数配置 + public static int DF_PAGE_NUM = 1; + // 全局默认配置每页请求条数 + public static int DF_PAGE_SIZE = 20; + // 默认页数配置 + private int config_page_num = DF_PAGE_NUM; + // 默认每页请求条数配置 + private int config_page_size = DF_PAGE_SIZE; + + // 当前请求页数 ( 已成功 ) + private int mPageNum = config_page_num; + // 当前 ( 正在 / 准备 ) 请求的页数, 如果成功则赋值给 pageNum + private int mPageNumReady = config_page_num; + // 每页请求条数 + private int mPageSize = config_page_size; + // 数据总条数 ( 全部 ), 非当前页数有多少条 + private int mTotalRow; + // 判断是否最后一页 + private boolean mLastPage; + + public PageAssist() { + } + + public PageAssist(int config_page_num) { + this.config_page_num = config_page_num; + } + + public PageAssist(int config_page_num, int config_page_size) { + this.config_page_num = config_page_num; + this.config_page_size = config_page_size; + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 初始化全局分页配置 + * @param pageNum 初始化页数 + * @param pageSize 初始化条数 + */ + public static void initPageConfig(final int pageNum, final int pageSize) { + PageAssist.DF_PAGE_NUM = pageNum; + PageAssist.DF_PAGE_SIZE = pageSize; + } + + // = + + /** + * 获取当前请求页数 + * @return 当前请求页数 + */ + public int getPageNum() { + return mPageNum; + } + + /** + * 获取当前请求页数 + * @param pageNum 当前请求页数 + * @return 当前请求页数 + */ + public int getPageNum(final int pageNum) { + return (this.mPageNum = pageNum); + } + + /** + * 设置当前请求页数 + * @param pageNum 当前请求页数 + * @return {@link PageAssist} + */ + public PageAssist setPageNum(final int pageNum) { + this.mPageNum = pageNum; + return this; + } + + // = + + /** + * 获取当前 ( 正在 / 准备 ) 请求的页数 + * @return 当前 ( 正在 / 准备 ) 请求的页数 + */ + public int getPageNumReady() { + return mPageNumReady; + } + + /** + * 获取当前 ( 正在 / 准备 ) 请求的页数 + * @param pageNumReady 请求的页数 + * @return 当前 ( 正在 / 准备 ) 请求的页数 + */ + public int getPageNumReady(final int pageNumReady) { + return (this.mPageNumReady = pageNumReady); + } + + /** + * 设置当前 ( 正在 / 准备 ) 请求的页数 + * @param pageNumReady 请求的页数 + * @return {@link PageAssist} + */ + public PageAssist setPageNumReady(final int pageNumReady) { + this.mPageNumReady = pageNumReady; + return this; + } + + // = + + /** + * 获取每页请求条数 + * @return 每页请求条数 + */ + public int getPageSize() { + return mPageSize; + } + + /** + * 获取每页请求条数 + * @param pageSize 每页请求条数 + * @return 每页请求条数 + */ + public int getPageSize(final int pageSize) { + return (this.mPageSize = pageSize); + } + + /** + * 设置每页请求条数 + * @param pageSize 每页请求条数 + * @return {@link PageAssist} + */ + public PageAssist setPageSize(final int pageSize) { + this.mPageSize = pageSize; + return this; + } + + // = + + /** + * 获取数据总条数 + * @return 数据总条数 + */ + public int getTotalRow() { + return mTotalRow; + } + + /** + * 获取数据总条数 + * @param totalRow 数据总条数 + * @return 数据总条数 + */ + public int getTotalRow(final int totalRow) { + return (this.mTotalRow = totalRow); + } + + /** + * 设置数据总条数 + * @param totalRow 数据总条数 + * @return {@link PageAssist} + */ + public PageAssist setTotalRow(final int totalRow) { + this.mTotalRow = totalRow; + return this; + } + + // = + + /** + * 判断是否最后一页 + * @return {@code true} yes, {@code false} no + */ + public boolean isLastPage() { + return mLastPage; + } + + /** + * 判断是否最后一页 + * @param lastPage {@code true} yes, {@code false} no + * @return {@code true} yes, {@code false} no + */ + public boolean isLastPage(final boolean lastPage) { + return (this.mLastPage = lastPage); + } + + /** + * 设置是否最后一页 + * @param lastPage {@code true} yes, {@code false} no + * @return {@link PageAssist} + */ + public PageAssist setLastPage(final boolean lastPage) { + this.mLastPage = lastPage; + return this; + } + + // ============ + // = 快捷方法 = + // ============ + + /** + * 判断是否第一页 + * @return {@code true} yes, {@code false} no + */ + public boolean isFirstPage() { + return isFirstPage(config_page_num); + } + + /** + * 判断是否第一页 + * @param firstPage 第一页页数 + * @return {@code true} yes, {@code false} no + */ + public boolean isFirstPage(final int firstPage) { + return mPageNum == firstPage; + } + + /** + * 判断是否允许请求下一页 + * @return {@code true} yes, {@code false} no + */ + public boolean isAllowNextPage() { + return !mLastPage; // 非最后一页则可以请求 + } + + // = + + /** + * 获取下一页页数 + * @return 下一页页数 + */ + public int getNextPage() { + return mPageNum + 1; + } + + /** + * 设置下一页页数 + * @return {@link PageAssist} + */ + public PageAssist nextPage() { + mPageNum += 1; + return this; + } + + // = + + /** + * 获取下一页页数 ( 预加载页数 ) + * @return 下一页页数 ( 预加载页数 ) + */ + public int getNextPageReady() { + return mPageNumReady + 1; + } + + /** + * 设置下一页页数 ( 预加载页数 ) + * @return {@link PageAssist} + */ + public PageAssist nextPageReady() { + mPageNumReady += 1; + return this; + } + + // ============ + // = 其他逻辑 = + // ============ + + /** + * 判断是否小于每页请求条数 + *
+     *     如果小于每页请求条数, 也表明已经没有下一页
+     * 
+ * @param size 当前请求返回条数 + * @return {@code true} yes, {@code false} no + */ + public boolean isLessThanPageSize(final int size) { + return size < mPageSize; + } + + /** + * 重置操作 + *
+     *     设置 请求页数、每页请求条数、非最后一页、请求默认状态
+     * 
+ * @return {@link PageAssist} + */ + public PageAssist reset() { + setPageNum(config_page_num).setPageNumReady(config_page_num).setLastPage(false).setRequestNormal(); + return this; + } +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/assist/RequestStatusAssist.java b/lib/DevAssist/src/main/java/dev/assist/RequestStatusAssist.java new file mode 100644 index 0000000000..f7dd7231e6 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/RequestStatusAssist.java @@ -0,0 +1,239 @@ +package dev.assist; + +import java.util.UUID; + +import dev.utils.common.ObjectUtils; + +/** + * detail: 请求状态辅助类 + * @author Ttt + */ +public class RequestStatusAssist { + + private static final int BASE = 102030; + // 默认状态 ( 暂未进行操作 ) + public static final int REQUEST_NORMAL = BASE + 1; + // 未请求过 + public static final int REQUEST_NEVER = BASE + 2; + // 请求中 + public static final int REQUEST_ING = BASE + 3; + // 请求成功 + public static final int REQUEST_SUC = BASE + 4; + // 请求失败 + public static final int REQUEST_FAIL = BASE + 5; + // 请求异常 + public static final int REQUEST_ERROR = BASE + 6; + + // 请求类型 + private T mRequestType; + // 请求 HashCode + private long mRequestHashCode = UUID.randomUUID().hashCode(); + // 请求状态 - 默认状态 ( 暂未进行操作 ) + private int mRequestStatus = REQUEST_NORMAL; + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 获取请求类型 + * @return 请求类型 + */ + public T getRequestType() { + return mRequestType; + } + + /** + * 设置请求类型 + * @param requestType 请求类型 + * @return 请求类型 + */ + public T getRequestType(final T requestType) { + return (this.mRequestType = requestType); + } + + /** + * 设置请求类型 + * @param requestType 请求类型 + * @return {@link RequestStatusAssist} + */ + public RequestStatusAssist setRequestType(final T requestType) { + this.mRequestType = requestType; + return this; + } + + /** + * 判断请求类型是否一致 + * @param requestType 待校验请求类型 + * @return {@code true} yes, {@code false} no + */ + public boolean equalsRequestType(final T requestType) { + return this.mRequestType != null && requestType != null && ObjectUtils.equals(this.mRequestType, requestType); + } + + // = + + /** + * 获取请求 HashCode + * @return 请求 HashCode + */ + public long getRequestHashCode() { + return mRequestHashCode; + } + + /** + * 获取请求 HashCode - 随机生成并赋值 + * @return 请求 HashCode + */ + public long getRequestHashCodeRandom() { + return (this.mRequestHashCode = UUID.randomUUID().hashCode()); + } + + /** + * 判断 HashCode 是否一致 + * @param hashCode 待校验请求 HashCode + * @return {@code true} yes, {@code false} no + */ + public boolean equalsHashCode(final long hashCode) { + return this.mRequestHashCode == hashCode; + } + + // = + + /** + * 获取请求状态 + * @return 请求状态 + */ + public int getRequestStatus() { + return mRequestStatus; + } + + /** + * 获取请求状态 + * @param requestStatus 请求状态 + * @return 请求状态 + */ + public int getRequestStatus(final int requestStatus) { + return (this.mRequestStatus = requestStatus); + } + + /** + * 设置请求状态 + * @param requestStatus 请求状态 + * @return {@link RequestStatusAssist} + */ + public RequestStatusAssist setRequestStatus(final int requestStatus) { + this.mRequestStatus = requestStatus; + return this; + } + + // ============ + // = 快捷方法 = + // ============ + + /** + * 判断是否默认状态 ( 暂未进行操作 ) + * @return {@code true} yes, {@code false} no + */ + public boolean isRequestNormal() { + return this.mRequestStatus == REQUEST_NORMAL; + } + + /** + * 判断是否未请求过 + * @return {@code true} yes, {@code false} no + */ + public boolean isRequestNever() { + return this.mRequestStatus == REQUEST_NEVER; + } + + /** + * 判断是否请求中 + * @return {@code true} yes, {@code false} no + */ + public boolean isRequestIng() { + return this.mRequestStatus == REQUEST_ING; + } + + /** + * 判断是否请求成功 + * @return {@code true} yes, {@code false} no + */ + public boolean isRequestSuccess() { + return this.mRequestStatus == REQUEST_SUC; + } + + /** + * 判断是否请求失败 + * @return {@code true} yes, {@code false} no + */ + public boolean isRequestFail() { + return this.mRequestStatus == REQUEST_FAIL; + } + + /** + * 判断是否请求异常 + * @return {@code true} yes, {@code false} no + */ + public boolean isRequestError() { + return this.mRequestStatus == REQUEST_ERROR; + } + + // ======= + // = set = + // ======= + + /** + * 设置状态为默认状态 ( 暂未进行操作 ) + * @return {@link RequestStatusAssist} + */ + public RequestStatusAssist setRequestNormal() { + this.mRequestStatus = REQUEST_NORMAL; + return this; + } + + /** + * 设置状态为未请求过 + * @return {@link RequestStatusAssist} + */ + public RequestStatusAssist setRequestNever() { + this.mRequestStatus = REQUEST_NEVER; + return this; + } + + /** + * 设置状态为请求中 + * @return {@link RequestStatusAssist} + */ + public RequestStatusAssist setRequestIng() { + this.mRequestStatus = REQUEST_ING; + return this; + } + + /** + * 设置状态为请求成功 + * @return {@link RequestStatusAssist} + */ + public RequestStatusAssist setRequestSuccess() { + this.mRequestStatus = REQUEST_SUC; + return this; + } + + /** + * 设置状态为请求失败 + * @return {@link RequestStatusAssist} + */ + public RequestStatusAssist setRequestFail() { + this.mRequestStatus = REQUEST_FAIL; + return this; + } + + /** + * 设置状态为请求异常 + * @return {@link RequestStatusAssist} + */ + public RequestStatusAssist setRequestError() { + this.mRequestStatus = REQUEST_ERROR; + return this; + } +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/assist/VariableAssist.java b/lib/DevAssist/src/main/java/dev/assist/VariableAssist.java new file mode 100644 index 0000000000..5158380944 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/VariableAssist.java @@ -0,0 +1,31 @@ +package dev.assist; + +import dev.base.DevBaseVariable; + +/** + * detail: 变量辅助类 + * @author Ttt + */ +public class VariableAssist { + + // Boolean 变量存储对象 + private DevBaseVariable mBooleanVariable = new DevBaseVariable<>(); + // Object 变量存储对象 + private DevBaseVariable mObjectVariable = new DevBaseVariable<>(); + + /** + * 获取 Boolean 变量存储对象 + * @return {@link DevBaseVariable} + */ + public DevBaseVariable getBooleanVariable() { + return mBooleanVariable; + } + + /** + * 获取 Object 变量存储对象 + * @return {@link DevBaseVariable} + */ + public DevBaseVariable getObjectVariable() { + return mObjectVariable; + } +} diff --git a/lib/DevAssist/src/main/java/dev/assist/adapter/AdapterDataAssist.java b/lib/DevAssist/src/main/java/dev/assist/adapter/AdapterDataAssist.java new file mode 100644 index 0000000000..7517dba537 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/adapter/AdapterDataAssist.java @@ -0,0 +1,340 @@ +package dev.assist.adapter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: Adapter 数据辅助类 + * @author Ttt + *
+ *     使用: 只需要实现 IAdapterData, 每个接口方法直接通过调用 AdapterDataAssist 已实现同名方法即可
+ * 
+ */ +public class AdapterDataAssist implements IAdapterData, IAdapterNotify { + + // 日志 TAG + private static final String TAG = AdapterDataAssist.class.getSimpleName(); + // List Data + private List mList = new ArrayList<>(); + // IAdapterNotify + private IAdapterNotify mAdapterNotify; + + /** + * 构造函数 + */ + public AdapterDataAssist() { + } + + /** + * 构造函数 + * @param adapterNotify {@link IAdapterNotify} + */ + public AdapterDataAssist(final IAdapterNotify adapterNotify) { + this.mAdapterNotify = adapterNotify; + } + + // ======= + // = set = + // ======= + + /** + * 设置 Adapter Notify + * @param adapterNotify {@link IAdapterNotify} + * @return {@link AdapterDataAssist} + */ + public AdapterDataAssist setAdapterNotify(final IAdapterNotify adapterNotify) { + this.mAdapterNotify = adapterNotify; + return this; + } + + // ================ + // = IAdapterData = + // ================ + + // ============ + // = 获取相关 = + // ============ + + /** + * 获取 List Data + * @return {@link List} + */ + @Override + public List getDataList() { + return mList; + } + + /** + * 获取 ArrayList Data + * @return {@link ArrayList} + */ + @Override + public ArrayList getDataArrayList() { + return new ArrayList<>(mList); + } + + // = + + /** + * 获取 List Count + * @return List.size() + */ + @Override + public int getDataCount() { + return mList == null ? 0 : mList.size(); + } + + /** + * 获取 List Position Item + * @param position 索引 + * @return V Object + */ + @Override + public V getDataItem(final int position) { + try { + return mList.get(position); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDataItem"); + } + return null; + } + + /** + * 获取 Value Position + * @param value V Object + * @return position + */ + @Override + public int getDataItemPosition(final V value) { + try { + return mList.indexOf(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDataItemPosition"); + } + return -1; + } + + /** + * 获取 First Item Data + * @return V Object + */ + @Override + public V getDataFirstItem() { + return getDataItem(0); + } + + /** + * 获取 Last Item Data + * @return V Object + */ + @Override + public V getDataLastItem() { + return getDataItem(getDataCount() - 1); + } + + // ============ + // = 其他方法 = + // ============ + + /** + * 判断是否 First Item Data + * @param position 索引 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isFirstItem(final int position) { + return position == 0; + } + + /** + * 判断是否 Last Item Data + * @param position 索引 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isLastItem(final int position) { + return isLastItem(position, getDataCount()); + } + + /** + * 判断是否 Last Item Data + * @param position 索引 + * @param count 总数 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isLastItem(final int position, final int count) { + return count - position == 1; + } + + // = + + /** + * 清空全部数据 + */ + @Override + public void clearDataList() { + clearDataList(true); + } + + /** + * 清空全部数据 + * @param notify 是否通知适配器 + */ + @Override + public void clearDataList(final boolean notify) { + if (mList != null) mList.clear(); + if (notify) adapterNotifyDataSetChanged(); + } + + // ================ + // = 数据处理方法 = + // ================ + + /** + * 添加数据 + * @param value Value + */ + @Override + public void addData(final V value) { + try { + mList.add(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addData"); + } + } + + /** + * 添加数据 + * @param position 索引 + * @param value Value + */ + @Override + public void addData(final int position, final V value) { + try { + mList.add(position, value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addData"); + } + } + + /** + * 添加数据 + * @param collection {@link Collection} + */ + @Override + public void addAllData(final Collection collection) { + if (collection != null) { + try { + mList.addAll(collection); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addAllData"); + } + } + } + + /** + * 添加数据 + * @param position 索引 + * @param collection {@link Collection} + */ + @Override + public void addAllData(final int position, final Collection collection) { + if (collection != null) { + try { + mList.addAll(position, collection); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addAllData"); + } + } + } + // = + + /** + * 移除数据 + * @param value Value + * @return {@code true} success, {@code false} fail + */ + @Override + public boolean removeData(final V value) { + try { + return mList.remove(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeData"); + } + return false; + } + + /** + * 移除数据 + * @param position 索引 + * @return remove position value + */ + @Override + public V removeData(final int position) { + try { + return mList.remove(position); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeData"); + } + return null; + } + + /** + * 移除数据 + * @param collection {@link Collection} + */ + @Override + public void removeData(final Collection collection) { + if (collection != null) { + try { + mList.removeAll(collection); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeData"); + } + } + } + + // = + + /** + * 设置 List Data + * @param lists {@link List} + */ + @Override + public void setDataList(final List lists) { + setDataList(lists, true); + } + + /** + * 设置 List Data + * @param lists {@link List} + * @param notify 是否通知适配器 + */ + @Override + public void setDataList(final List lists, final boolean notify) { + if (mList != null) mList.clear(); + // 添加数据 + addAllData(lists); + // 通知适配器 + if (notify) adapterNotifyDataSetChanged(); + } + + // ================== + // = IAdapterNotify = + // ================== + + /** + * 通知 Adapter 数据改变 + */ + @Override + public void adapterNotifyDataSetChanged() { + // 通知适配器 + if (mAdapterNotify != null) { + mAdapterNotify.adapterNotifyDataSetChanged(); + } + } +} diff --git a/lib/DevAssist/src/main/java/dev/assist/adapter/IAdapterData.java b/lib/DevAssist/src/main/java/dev/assist/adapter/IAdapterData.java new file mode 100644 index 0000000000..f1cb11d997 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/adapter/IAdapterData.java @@ -0,0 +1,168 @@ +package dev.assist.adapter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * detail: Adapter 数据操作接口 + * @author Ttt + */ +public interface IAdapterData { + + // ============ + // = 获取相关 = + // ============ + + /** + * 获取 List Data + * @return {@link List} + */ + List getDataList(); + + /** + * 获取 ArrayList Data + * @return {@link ArrayList} + */ + ArrayList getDataArrayList(); + + // = + + /** + * 获取 List Count + * @return List.size() + */ + int getDataCount(); + + /** + * 获取 List Position Item + * @param position 索引 + * @return V Object + */ + V getDataItem(int position); + + /** + * 获取 Value Position + * @param value V Object + * @return position + */ + int getDataItemPosition(V value); + + /** + * 获取 First Item Data + * @return V Object + */ + V getDataFirstItem(); + + /** + * 获取 Last Item Data + * @return V Object + */ + V getDataLastItem(); + + // ============ + // = 其他方法 = + // ============ + + /** + * 判断是否 First Item Data + * @param position 索引 + * @return {@code true} yes, {@code false} no + */ + boolean isFirstItem(int position); + + /** + * 判断是否 Last Item Data + * @param position 索引 + * @return {@code true} yes, {@code false} no + */ + boolean isLastItem(int position); + + /** + * 判断是否 Last Item Data + * @param position 索引 + * @param count 总数 + * @return {@code true} yes, {@code false} no + */ + boolean isLastItem(int position, int count); + + // = + + /** + * 清空全部数据 + */ + void clearDataList(); + + /** + * 清空全部数据 + * @param notify 是否通知适配器 + */ + void clearDataList(boolean notify); + + // ================ + // = 数据处理方法 = + // ================ + + /** + * 添加数据 + * @param value Value + */ + void addData(V value); + + /** + * 添加数据 + * @param position 索引 + * @param value Value + */ + void addData(int position, V value); + + /** + * 添加数据 + * @param collection {@link Collection} + */ + void addAllData(Collection collection); + + /** + * 添加数据 + * @param position 索引 + * @param collection {@link Collection} + */ + void addAllData(int position, Collection collection); + + // = + + /** + * 移除数据 + * @param value Value + * @return {@code true} success, {@code false} fail + */ + boolean removeData(V value); + + /** + * 移除数据 + * @param position 索引 + * @return remove position value + */ + V removeData(int position); + + /** + * 移除数据 + * @param collection {@link Collection} + */ + void removeData(Collection collection); + + // = + + /** + * 设置 List Data + * @param lists {@link List} + */ + void setDataList(List lists); + + /** + * 设置 List Data + * @param lists {@link List} + * @param notify 是否通知适配器 + */ + void setDataList(List lists, boolean notify); +} diff --git a/lib/DevAssist/src/main/java/dev/assist/adapter/IAdapterNotify.java b/lib/DevAssist/src/main/java/dev/assist/adapter/IAdapterNotify.java new file mode 100644 index 0000000000..bdaf506a2c --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/adapter/IAdapterNotify.java @@ -0,0 +1,13 @@ +package dev.assist.adapter; + +/** + * detail: Adapter 通知接口 + * @author Ttt + */ +public interface IAdapterNotify { + + /** + * 通知 Adapter 数据改变 + */ + void adapterNotifyDataSetChanged(); +} diff --git a/lib/DevAssist/src/main/java/dev/assist/multiselect/IBaseMultiSelect.java b/lib/DevAssist/src/main/java/dev/assist/multiselect/IBaseMultiSelect.java new file mode 100644 index 0000000000..0d6e484833 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/multiselect/IBaseMultiSelect.java @@ -0,0 +1,66 @@ +package dev.assist.multiselect; + +import dev.base.DevBaseEntry; + +/** + * detail: 多选操作接口 - 基类 + * @param 集合泛型 + * @param Value + * @author Ttt + */ +public interface IBaseMultiSelect { + + /** + * 清空全部选中数据 + */ + void clearSelects(); + + /** + * 获取选中的数据条数 + * @return 选中的数据条数 + */ + int getSelectSize(); + + /** + * 获取选中的数据集合 + * @return 选中的数据集合 + */ + CollectionG getSelects(); + + /** + * 通过集合添加选中数据 + * @param collection 集合泛型 + */ + void putSelects(CollectionG collection); + + /** + * 通过集合添加选中数据 + * @param collections 集合 + */ + void putSelects(java.util.Collection> collections); + + /** + * 判断是否存在选中的数据 + * @return {@code true} yes, {@code false} no + */ + boolean isSelect(); + + /** + * 判断是否选中 - 通过 value 判断 + * @param value Value + * @return {@code true} yes, {@code false} no + */ + boolean isSelectValue(V value); + + /** + * 设置非选中 + * @param value Value + */ + void unselectValue(V value); + + /** + * 设置非选中 - 符合条件的全部 value + * @param value Value + */ + void unselectValueAll(V value); +} diff --git a/lib/DevAssist/src/main/java/dev/assist/multiselect/IMultiSelectToList.java b/lib/DevAssist/src/main/java/dev/assist/multiselect/IMultiSelectToList.java new file mode 100644 index 0000000000..cb88d7fce1 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/multiselect/IMultiSelectToList.java @@ -0,0 +1,99 @@ +package dev.assist.multiselect; + +import java.util.List; + +/** + * detail: 多选操作接口 - List + * @param 集合泛型 + * @param Value + * @author Ttt + */ +public interface IMultiSelectToList extends IBaseMultiSelect { + + /** + * 判断是否选中 - 通过 value 判断 + * @param value Value + * @return {@code true} yes, {@code false} no + */ + boolean isSelect(V value); + + // = + + /** + * 设置选中操作 + * @param value Value + */ + void select(V value); + + /** + * 设置选中操作 + * @param isSelect 是否选中 + * @param value Value + */ + void select(boolean isSelect, V value); + + /** + * 设置选中操作 ( 保存到指定索引 ) + * @param value Value + * @param position 索引 + */ + void select(V value, int position); + + // = + + /** + * 设置非选中操作 + * @param position 索引 + * @return Value + */ + V unselect(int position); + + /** + * 设置非选中操作 + * @param value Value + */ + void unselect(V value); + + // = + + /** + * 切换选中状态 + *
+     *     如果选中, 则设为非选中, 反之设为选中
+     * 
+ * @param value Value + */ + void toggle(V value); + + // ================== + // = 获取选中的数据 = + // ================== + + /** + * 获取选中的数据集合 + * @return {@link List} + */ + List getSelectValues(); + + /** + * 获取选中的数据集合 ( 倒序 ) + * @return {@link List} + */ + List getSelectValuesToReverse(); + + // = + + /** + * 获取选中的数据 + * @param position 索引 + * @return Value + */ + V getSelectValue(int position); + + /** + * 获取选中的数据所在的索引 + * @param value Value + * @return value position + */ + int getSelectValueToPosition(V value); +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/assist/multiselect/IMultiSelectToMap.java b/lib/DevAssist/src/main/java/dev/assist/multiselect/IMultiSelectToMap.java new file mode 100644 index 0000000000..5ed764d2f6 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/multiselect/IMultiSelectToMap.java @@ -0,0 +1,116 @@ +package dev.assist.multiselect; + +import java.util.List; +import java.util.Map; + +/** + * detail: 多选操作接口 - Map + * @param 集合泛型 + * @param Key + * @param Value + * @author Ttt + */ +public interface IMultiSelectToMap extends IBaseMultiSelect { + + /** + * 判断是否选中 - 如果未选中, 则设置为选中 + * @param key Key + * @param value Value + * @return {@code true} yes, {@code false} no + */ + boolean isSelect(K key, V value); + + /** + * 判断是否选中 - 通过 key 判断 + * @param key Key + * @return {@code true} yes, {@code false} no + */ + boolean isSelectKey(K key); + + // = + + /** + * 设置选中操作 + * @param key Key + * @param value Value + */ + void select(K key, V value); + + /** + * 设置选中操作 + * @param isSelect 是否选中 + * @param key Key + * @param value Value + */ + void select(boolean isSelect, K key, V value); + + // = + + /** + * 设置非选中操作 + * @param key Key + */ + void unselect(K key); + + // = + + /** + * 切换选中状态 + *
+     *     如果选中, 则设为非选中, 反之设为选中
+     * 
+ * @param key Key + * @param value Value + */ + void toggle(K key, V value); + + // ================== + // = 获取选中的数据 = + // ================== + + // ========= + // = Value = + // ========= + + /** + * 通过 key 获取 value + * @param key Key + * @return Value + */ + V getSelectValue(K key); + + /** + * 获取选中的数据集合 + * @return {@link List} + */ + List getSelectValues(); + + /** + * 获取选中的数据集合 ( 倒序 ) + * @return {@link List} + */ + List getSelectValuesToReverse(); + + // ======= + // = Key = + // ======= + + /** + * 通过 value 获取 key + * @param value Value + * @return Key + */ + K getSelectKey(V value); + + /** + * 获取选中的数据集合 + * @return {@link List} + */ + List getSelectKeys(); + + /** + * 获取选中的数据集合 ( 倒序 ) + * @return {@link List} + */ + List getSelectKeysToReverse(); +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/assist/multiselect/MultiSelectListAssist.java b/lib/DevAssist/src/main/java/dev/assist/multiselect/MultiSelectListAssist.java new file mode 100644 index 0000000000..42fd386368 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/multiselect/MultiSelectListAssist.java @@ -0,0 +1,260 @@ +package dev.assist.multiselect; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import dev.base.DevBaseEntry; + +/** + * detail: List 多选辅助类 + * @param Value + * @author Ttt + *
+ *     使用: 只需要实现 IMultiSelectToList, 每个接口方法直接通过调用 MultiSelectListAssist 已实现同名方法即可
+ * 
+ */ +public class MultiSelectListAssist implements IMultiSelectToList, V> { + + // 选中数据集 + private final List mListSelects = new ArrayList<>(); + + // ==================== + // = IBaseMultiSelect = + // ==================== + + /** + * 清空全部选中数据 + */ + @Override + public void clearSelects() { + mListSelects.clear(); + } + + /** + * 获取选中的数据条数 + * @return 选中的数据条数 + */ + @Override + public int getSelectSize() { + return mListSelects.size(); + } + + /** + * 获取选中的数据集合 + * @return 选中的数据集合 + */ + @Override + public List getSelects() { + return mListSelects; + } + + /** + * 通过集合添加选中数据 + * @param collection 集合泛型 + */ + @Override + public void putSelects(final List collection) { + if (collection != null) { + mListSelects.addAll(collection); + } + } + + /** + * 通过集合添加选中数据 + * @param collections 集合 + */ + @Override + public void putSelects(final Collection> collections) { + if (collections != null) { + for (DevBaseEntry entry : collections) { + if (entry != null) { + mListSelects.add(entry.getEntryValue()); + } + } + } + } + + /** + * 判断是否存在选中的数据 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isSelect() { + return getSelectSize() != 0; + } + + /** + * 判断是否选中 - 通过 value 判断 + * @param value Value + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isSelectValue(final V value) { + return mListSelects.contains(value); + } + + /** + * 设置非选中 + * @param value Value + */ + @Override + public void unselectValue(final V value) { + mListSelects.remove(value); + } + + /** + * 设置非选中 - 符合条件的全部 value + * @param value Value + */ + @Override + public void unselectValueAll(final V value) { + Iterator iterator = mListSelects.iterator(); + while (iterator.hasNext()) { + V v = iterator.next(); + if (v == value) { + iterator.remove(); + } + } + } + + // ====================== + // = IMultiSelectToList = + // ====================== + + /** + * 判断是否选中 - 通过 value 判断 + * @param value Value + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isSelect(final V value) { + return isSelectValue(value); + } + + /** + * 设置选中操作 + * @param value Value + */ + @Override + public void select(final V value) { + mListSelects.add(value); + } + + /** + * 设置选中操作 + * @param isSelect 是否选中 + * @param value Value + */ + @Override + public void select(final boolean isSelect, final V value) { + if (isSelect) { + select(value); + } else { + unselect(value); + } + } + + /** + * 设置选中操作 ( 保存到指定索引 ) + * @param value Value + * @param position 索引 + */ + @Override + public void select(final V value, final int position) { + if (position >= 0) mListSelects.add(position, value); + } + + /** + * 设置非选中操作 + * @param position 索引 + * @return Value + */ + @Override + public V unselect(final int position) { + if (position >= 0) { + try { + return mListSelects.remove(position); + } catch (Exception e) { + } + } + return null; + } + + /** + * 设置非选中操作 + * @param value Value + */ + @Override + public void unselect(final V value) { + mListSelects.remove(value); + } + + /** + * 切换选中状态 + *
+     *     如果选中, 则设为非选中, 反之设为选中
+     * 
+ * @param value Value + */ + @Override + public void toggle(final V value) { + if (mListSelects.contains(value)) { + mListSelects.remove(value); + } else { + mListSelects.add(value); + } + } + + // ================== + // = 获取选中的数据 = + // ================== + + /** + * 获取选中的数据集合 + * @return {@link List} + */ + @Override + public List getSelectValues() { + return new ArrayList<>(mListSelects); + } + + /** + * 获取选中的数据集合 ( 倒序 ) + * @return {@link List} + */ + @Override + public List getSelectValuesToReverse() { + List lists = getSelectValues(); + Collections.reverse(lists); + return lists; + } + + /** + * 获取选中的数据 + * @param position 索引 + * @return Value + */ + @Override + public V getSelectValue(final int position) { + if (position >= 0) { + try { + return mListSelects.get(position); + } catch (Exception e) { + } + } + return null; + } + + /** + * 获取选中的数据所在的索引 + * @param value Value + * @return value position + */ + @Override + public int getSelectValueToPosition(final V value) { + return mListSelects.indexOf(value); + } +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/assist/multiselect/MultiSelectMapAssist.java b/lib/DevAssist/src/main/java/dev/assist/multiselect/MultiSelectMapAssist.java new file mode 100644 index 0000000000..5b0a4f5a3c --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/multiselect/MultiSelectMapAssist.java @@ -0,0 +1,298 @@ +package dev.assist.multiselect; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import dev.base.DevBaseEntry; + +/** + * detail: Map 多选辅助类 + * @param Key + * @param Value + * @author Ttt + *
+ *     使用: 只需要实现 IMultiSelectToMap, 每个接口方法直接通过调用 MultiSelectMapAssist 已实现同名方法即可
+ * 
+ */ +public class MultiSelectMapAssist implements IMultiSelectToMap, K, V> { + + // 选中数据集 + private final LinkedHashMap mMapSelects = new LinkedHashMap<>(); + + // ==================== + // = IBaseMultiSelect = + // ==================== + + /** + * 清空全部选中数据 + */ + @Override + public void clearSelects() { + mMapSelects.clear(); + } + + /** + * 获取选中的数据条数 + * @return 选中的数据条数 + */ + @Override + public int getSelectSize() { + return mMapSelects.size(); + } + + /** + * 获取选中的数据集合 + * @return 选中的数据集合 + */ + @Override + public LinkedHashMap getSelects() { + return mMapSelects; + } + + /** + * 通过集合添加选中数据 + * @param collection 集合泛型 + */ + @Override + public void putSelects(final LinkedHashMap collection) { + if (collection != null) { + mMapSelects.putAll(collection); + } + } + + /** + * 通过集合添加选中数据 + * @param collections 集合 + */ + @Override + public void putSelects(final Collection> collections) { + if (collections != null) { + for (DevBaseEntry entry : collections) { + if (entry != null) { + mMapSelects.put((K) entry.getEntryKey(), entry.getEntryValue()); + } + } + } + } + + /** + * 判断是否存在选中的数据 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isSelect() { + return getSelectSize() != 0; + } + + /** + * 判断是否选中 - 通过 value 判断 + * @param value Value + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isSelectValue(final V value) { + return mMapSelects.containsValue(value); + } + + /** + * 设置非选中 + * @param value Value + */ + @Override + public void unselectValue(final V value) { + Iterator iterator = mMapSelects.values().iterator(); + while (iterator.hasNext()) { + V v = iterator.next(); + if (v == value) { + iterator.remove(); + break; + } + } + } + + /** + * 设置非选中 - 符合条件的全部 value + * @param value Value + */ + @Override + public void unselectValueAll(final V value) { + Iterator iterator = mMapSelects.values().iterator(); + while (iterator.hasNext()) { + V v = iterator.next(); + if (v == value) { + iterator.remove(); + } + } + } + + // ===================== + // = IMultiSelectToMap = + // ===================== + + /** + * 判断是否选中 - 如果未选中, 则设置为选中 + * @param key Key + * @param value Value + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isSelect(final K key, final V value) { + if (!isSelectKey(key)) { + mMapSelects.put(key, value); + return false; + } + return true; + } + + /** + * 判断是否选中 - 通过 key 判断 + * @param key Key + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean isSelectKey(final K key) { + return mMapSelects.containsKey(key); + } + + /** + * 设置选中操作 + * @param key Key + * @param value Value + */ + @Override + public void select(final K key, final V value) { + mMapSelects.put(key, value); + } + + /** + * 设置选中操作 + * @param isSelect 是否选中 + * @param key Key + * @param value Value + */ + @Override + public void select(final boolean isSelect, final K key, final V value) { + if (isSelect) { + select(key, value); + } else { + unselect(key); + } + } + + /** + * 设置非选中操作 + * @param key Key + */ + @Override + public void unselect(final K key) { + mMapSelects.remove(key); + } + + /** + * 切换选中状态 + *
+     *     如果选中, 则设为非选中, 反之设为选中
+     * 
+ * @param key Key + * @param value Value + */ + @Override + public void toggle(final K key, final V value) { + if (isSelectKey(key)) { + mMapSelects.remove(key); + } else { + mMapSelects.put(key, value); + } + } + + // ================== + // = 获取选中的数据 = + // ================== + + // ========= + // = Value = + // ========= + + /** + * 通过 key 获取 value + * @param key Key + * @return Value + */ + @Override + public V getSelectValue(final K key) { + return mMapSelects.get(key); + } + + /** + * 获取选中的数据集合 + * @return {@link List} + */ + @Override + public List getSelectValues() { + List lists = new ArrayList<>(); + lists.addAll(mMapSelects.values()); + return lists; + } + + /** + * 获取选中的数据集合 ( 倒序 ) + * @return {@link List} + */ + @Override + public List getSelectValuesToReverse() { + List lists = getSelectValues(); + Collections.reverse(lists); + return lists; + } + + // ======= + // = Key = + // ======= + + /** + * 通过 value 获取 key + * @param value Value + * @return Key + */ + @Override + public K getSelectKey(final V value) { + // 进行循环遍历获取 + Iterator> iterator = mMapSelects.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + V v = entry.getValue(); + // 判断是否符合对应的 value + if (v == value) { + return entry.getKey(); + } + } + return null; + } + + /** + * 获取选中的数据集合 + * @return {@link List} + */ + @Override + public List getSelectKeys() { + List lists = new ArrayList<>(); + lists.addAll(mMapSelects.keySet()); + return lists; + } + + /** + * 获取选中的数据集合 ( 倒序 ) + * @return {@link List} + */ + @Override + public List getSelectKeysToReverse() { + List lists = getSelectKeys(); + Collections.reverse(lists); + return lists; + } +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/assist/multiselect/edit/IMultiSelectEdit.java b/lib/DevAssist/src/main/java/dev/assist/multiselect/edit/IMultiSelectEdit.java new file mode 100644 index 0000000000..321d4b4516 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/assist/multiselect/edit/IMultiSelectEdit.java @@ -0,0 +1,74 @@ +package dev.assist.multiselect.edit; + +/** + * detail: 多选编辑接口 + * @author Ttt + *
+ *     主要用于 Adapter 实现该接口, 对外支持快捷操作方法
+ *     内部通过 MultiSelectListAssist、MultiSelectMapAssist 实现多选操作功能
+ * 
+ */ +public interface IMultiSelectEdit { + + // ============ + // = 编辑状态 = + // ============ + + /** + * 是否编辑状态 + * @return {@code true} yes, {@code false} no + */ + boolean isEditStatus(); + + /** + * 设置编辑状态 + * @param isEdit {@code true} yes, {@code false} no + */ + void setEditStatus(boolean isEdit); + + /** + * 切换编辑状态 + */ + void toggleEditStatus(); + + // ============ + // = 选择操作 = + // ============ + + /** + * 全选 + */ + void selectAll(); + + /** + * 清空全选 ( 非全选 ) + */ + void clearSelectAll(); + + /** + * 反选 + */ + void inverseSelect(); + + // ============ + // = 判断处理 = + // ============ + + /** + * 判断是否全选 + * @return {@code true} yes, {@code false} no + */ + boolean isSelectAll(); + + /** + * 判断是否存在选中的数据 + * @return {@code true} yes, {@code false} no + */ + boolean isSelect(); + + /** + * 判断是否不存在选中的数据 + * @return {@code true} yes, {@code false} no + */ + boolean isNotSelect(); +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/base/DevBaseEntry.java b/lib/DevAssist/src/main/java/dev/base/DevBaseEntry.java new file mode 100644 index 0000000000..5aecc8007c --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/base/DevBaseEntry.java @@ -0,0 +1,78 @@ +package dev.base; + +/** + * detail: Key-Value Entry + * @author Ttt + */ +public class DevBaseEntry extends DevBaseModel { + + // key + public K mEntryKey; + // value + public V mEntryValue; + + public DevBaseEntry() { + } + + public DevBaseEntry(K entryKey) { + this.mEntryKey = entryKey; + } + + public DevBaseEntry(K entryKey, V entryValue) { + this.mEntryKey = entryKey; + this.mEntryValue = entryValue; + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 获取 Entry Key + * @return Entry Key + */ + public K getEntryKey() { + return mEntryKey; + } + + /** + * 设置 Entry Key + * @param entryKey Entry Key + * @return {@link DevBaseEntry} + */ + public DevBaseEntry setEntryKey(final K entryKey) { + this.mEntryKey = entryKey; + return this; + } + + // = + + /** + * 获取 Entry Value + * @return Entry Value + */ + public V getEntryValue() { + return mEntryValue; + } + + /** + * 设置 Entry Value + * @param entryValue Entry Value + * @return {@link DevBaseEntry} + */ + public DevBaseEntry setEntryValue(final V entryValue) { + this.mEntryValue = entryValue; + return this; + } + + // = + + /** + * 校验数据正确性 + * @return {@code true} correct, {@code false} error + */ + @Override + public boolean isCorrect() { + return mEntryKey != null; + } +} diff --git a/lib/DevAssist/src/main/java/dev/base/DevBaseEvent.java b/lib/DevAssist/src/main/java/dev/base/DevBaseEvent.java new file mode 100644 index 0000000000..2d5c705f7b --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/base/DevBaseEvent.java @@ -0,0 +1,141 @@ +package dev.base; + +import dev.utils.common.ObjectUtils; + +/** + * detail: Event 基类 + * @author Ttt + */ +public class DevBaseEvent { + + // code + private int mCode; + // code String + private String mCodeStr; + // value + private T mValue; + // object + private Object mObject; + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 获取 Code + * @return Code + */ + public int getCode() { + return mCode; + } + + /** + * 设置 Code + * @param code Code + * @return {@link DevBaseEvent} + */ + public DevBaseEvent setCode(final int code) { + this.mCode = code; + return this; + } + + // = + + /** + * 获取 Code String + * @return Code String + */ + public String getCodeStr() { + return mCodeStr; + } + + /** + * 设置 Code String + * @param codeStr Code String + * @return {@link DevBaseEvent} + */ + public DevBaseEvent setCodeStr(final String codeStr) { + this.mCodeStr = codeStr; + return this; + } + + // = + + /** + * 获取 Value + * @return Value + */ + public T getValue() { + return mValue; + } + + /** + * 设置 Value + * @param value Value + * @return {@link DevBaseEvent} + */ + public DevBaseEvent setValue(final T value) { + this.mValue = value; + return this; + } + + // = + + /** + * 获取 Object + * @return Object + */ + public Object getObject() { + return mObject; + } + + /** + * 设置 Object + * @param object Object + * @return {@link DevBaseEvent} + */ + public DevBaseEvent setObject(final Object object) { + this.mObject = object; + return this; + } + + // ============ + // = 判断方法 = + // ============ + + /** + * 判断 Code 是否一致 + * @param code 待校验 Code + * @return {@code true} yes, {@code false} no + */ + public boolean equalsCode(final int code) { + return this.mCode == code; + } + + /** + * 判断 Code String 是否一致 + * @param codeStr 待校验 Code String + * @return {@code true} yes, {@code false} no + */ + public boolean equalsCode(final String codeStr) { + return this.mCodeStr != null && codeStr != null && this.mCodeStr.equals(codeStr); + } + + /** + * 判断 Value 是否一致 + * @param value 待校验 Value + * @return {@code true} yes, {@code false} no + */ + public boolean equalsValue(final T value) { + return this.mValue != null && value != null && ObjectUtils.equals(this.mValue, value); + } + + /** + * 判断 Object 是否一致 + * @param object 待校验 Object + * @return {@code true} yes, {@code false} no + */ + public boolean equalsObject(final Object object) { + return this.mObject != null && object != null && ObjectUtils.equals(this.mObject, object); + } +} diff --git a/lib/DevAssist/src/main/java/dev/base/DevBaseModel.java b/lib/DevAssist/src/main/java/dev/base/DevBaseModel.java new file mode 100644 index 0000000000..19f2dbca4b --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/base/DevBaseModel.java @@ -0,0 +1,68 @@ +package dev.base; + +import java.io.Serializable; +import java.util.UUID; + +/** + * detail: Model 基类 + * @author Ttt + *
+ *     isCorrect 可以用于 BaseModel 子类的数据校验
+ * 
+ */ +public abstract class DevBaseModel implements Serializable { + + private static final long serialVersionUID = 2514563577168445802L; + + // uuid + private final int mUUID = UUID.randomUUID().hashCode(); + // model id + private int mModelId; + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 获取 UUID + * @return random UUID HashCode + */ + public int getUuid() { + return mUUID; + } + + /** + * 获取 Model id + * @return model id + */ + public int getModelId() { + return mModelId; + } + + /** + * 设置 Model id + * @param modelId model id + * @return {@link DevBaseModel} + */ + public DevBaseModel setModelId(final int modelId) { + this.mModelId = modelId; + return this; + } + + // = + + /** + * 校验数据正确性 + * @param data {@link DevBaseModel} + * @return {@code true} correct, {@code false} error + */ + public final static boolean isCorrect(final DevBaseModel data) { + return data != null && data.isCorrect(); + } + + /** + * 校验数据正确性 + * @return {@code true} correct, {@code false} error + */ + protected abstract boolean isCorrect(); +} diff --git a/lib/DevAssist/src/main/java/dev/base/DevBaseVariable.java b/lib/DevAssist/src/main/java/dev/base/DevBaseVariable.java new file mode 100644 index 0000000000..66b41ffc32 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/base/DevBaseVariable.java @@ -0,0 +1,274 @@ +package dev.base; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * detail: 变量基类 ( 方便判断处理 ) + * @author Ttt + */ +public class DevBaseVariable { + + // 存储数据 Map + private LinkedHashMap mLinkedHashMap = new LinkedHashMap<>(); + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 获取全部变量数据 + * @return {@link LinkedHashMap} + */ + public LinkedHashMap getVariables() { + return mLinkedHashMap; + } + + /** + * 清空全部变量数据 + * @return {@link DevBaseVariable} + */ + public DevBaseVariable clearVariables() { + mLinkedHashMap.clear(); + return this; + } + + /** + * 保存变量数据集合 + * @param collection {@link LinkedHashMap} + * @return {@link DevBaseVariable} + */ + public DevBaseVariable putVariables(final LinkedHashMap collection) { + if (collection != null) mLinkedHashMap.putAll(collection); + return this; + } + + /** + * 获取变量总数 + * @return 变量总数 + */ + public int getVariablesSize() { + return mLinkedHashMap.size(); + } + + /** + * 判断是否存在变量数据 + * @return {@code true} yes, {@code false} no + */ + public boolean isVariables() { + return getVariablesSize() != 0; + } + + // = + + /** + * 判断是否存在变量 - 通过 value 判断 + * @param value Value + * @return {@code true} yes, {@code false} no + */ + public boolean isVariableValue(final V value) { + return mLinkedHashMap.containsValue(value); + } + + /** + * 删除指定变量数据 + * @param value Value + * @return {@link DevBaseVariable} + */ + public DevBaseVariable removeVariableValue(final V value) { + Iterator iterator = mLinkedHashMap.values().iterator(); + while (iterator.hasNext()) { + V v = iterator.next(); + if (v == value) { + iterator.remove(); + break; + } + } + return this; + } + + /** + * 删除指定变量数据 - 符合条件的全部 value + * @param value Value + * @return {@link DevBaseVariable} + */ + public DevBaseVariable removeVariableValueAll(final V value) { + Iterator iterator = mLinkedHashMap.values().iterator(); + while (iterator.hasNext()) { + V v = iterator.next(); + if (v == value) { + iterator.remove(); + } + } + return this; + } + + // = + + /** + * 判断是否存在变量 - 通过 key 判断 + * @param key Key + * @return {@code true} yes, {@code false} no + */ + public boolean isVariable(final K key) { + return mLinkedHashMap.containsKey(key); + } + + /** + * 判断是否存在变量 - 如果不存在, 则保存 + * @param key Key + * @param value Value + * @return {@code true} yes, {@code false} no + */ + public boolean isVariable(final K key, final V value) { + if (!isVariable(key)) { + mLinkedHashMap.put(key, value); + return false; + } + return true; + } + + // = + + /** + * 保存变量数据 + * @param key Key + * @param value Value + * @return {@link DevBaseVariable} + */ + public DevBaseVariable putVariable(final K key, final V value) { + mLinkedHashMap.put(key, value); + return this; + } + + /** + * 保存变量数据 + * @param put {@code true} put, {@code false} remove + * @param key Key + * @param value Value + * @return {@link DevBaseVariable} + */ + public DevBaseVariable putVariable(final boolean put, final K key, final V value) { + return put ? putVariable(key, value) : removeVariable(key); + } + + /** + * 移除指定变量数据 - 通过 key 判断 + * @param key Key + * @return {@link DevBaseVariable} + */ + public DevBaseVariable removeVariable(final K key) { + mLinkedHashMap.remove(key); + return this; + } + + /** + * 切换变量数据存储状态 + *
+     *     如果存在则删除、反之则保存
+     * 
+ * @param key Key + * @param value Value + * @return {@link DevBaseVariable} + */ + public DevBaseVariable toggle(final K key, final V value) { + if (isVariable(key)) { // 移除存在的数据 + mLinkedHashMap.remove(key); + } else { // 保存变量数据 + mLinkedHashMap.put(key, value); + } + return this; + } + + // = + + /** + * 通过 key 获取 value + * @param key Key + * @return Value + */ + public V getVariableValue(final K key) { + return mLinkedHashMap.get(key); + } + + /** + * 通过 key 获取 value + * @param key Key + * @param 泛型 + * @return Value convert T value + */ + public T getVariableValueConvert(final K key) { + try { + return (T) mLinkedHashMap.get(key); + } catch (Exception e) { + } + return null; + } + + // = + + /** + * 获取变量数据 value list + * @return {@link List} + */ + public List getVariableValues() { + List lists = new ArrayList<>(); + lists.addAll(mLinkedHashMap.values()); + return lists; + } + + /** + * 获取变量数据 value list( 倒序 ) + * @return {@link List} + */ + public List getVariableValuesToReverse() { + List lists = getVariableValues(); + Collections.reverse(lists); + return lists; + } + + // = + + /** + * 通过 value 获取 key + * @param value Value + * @return Key + */ + public K getVariableKey(final V value) { + // 进行循环遍历获取 + Iterator> iterator = mLinkedHashMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + V v = entry.getValue(); + // 判断是否符合对应的 value + if (v == value) { + return entry.getKey(); + } + } + return null; + } + + /** + * 获取变量数据 key list + * @return {@link List} + */ + public List getVariableKeys() { + List lists = new ArrayList<>(); + lists.addAll(mLinkedHashMap.keySet()); + return lists; + } + + /** + * 获取变量数据 key list( 倒序 ) + * @return {@link List} + */ + public List getVariableKeysToReverse() { + List lists = getVariableKeys(); + Collections.reverse(lists); + return lists; + } +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/callback/AbstractCallBack.java b/lib/DevAssist/src/main/java/dev/callback/AbstractCallBack.java new file mode 100644 index 0000000000..9ea4cb27cb --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/callback/AbstractCallBack.java @@ -0,0 +1,103 @@ +package dev.callback; + +import java.util.UUID; + +/** + * detail: 抽象回调 - 基类 + * @author Ttt + */ +public abstract class AbstractCallBack { + + // uuid 一定程度上唯一 + private final int mUUID = UUID.randomUUID().hashCode(); + // 标记 tag + private String mTag; + // Value + private T mValue; + // Object + private Object mObject; + + public AbstractCallBack() { + } + + public AbstractCallBack(T value) { + this.mValue = value; + } + + public AbstractCallBack(T value, Object object) { + this.mValue = value; + this.mObject = object; + } + + public AbstractCallBack(T value, Object object, String tag) { + this.mValue = value; + this.mObject = object; + this.mTag = tag; + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 获取 UUID + * @return random UUID HashCode + */ + public final int getUUID() { + return mUUID; + } + + /** + * 获取标记 Tag + * @return 标记 Tag + */ + public String getTag() { + return mTag; + } + + /** + * 设置标记 Tag + * @param tag Tag + * @return {@link AbstractCallBack} + */ + public AbstractCallBack setTag(String tag) { + this.mTag = tag; + return this; + } + + /** + * 获取 Value + * @return Value + */ + public T getValue() { + return mValue; + } + + /** + * 设置 Value + * @param value T Class Object + * @return {@link AbstractCallBack} + */ + public AbstractCallBack setValue(T value) { + this.mValue = value; + return this; + } + + /** + * 获取 Object + * @return Object + */ + public Object getObject() { + return mObject; + } + + /** + * 设置 Object + * @param object Object + * @return {@link AbstractCallBack} + */ + public AbstractCallBack setObject(Object object) { + this.mObject = object; + return this; + } +} diff --git a/lib/DevAssist/src/main/java/dev/callback/AbstractCommonCallBack.java b/lib/DevAssist/src/main/java/dev/callback/AbstractCommonCallBack.java new file mode 100644 index 0000000000..ae0885d48d --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/callback/AbstractCommonCallBack.java @@ -0,0 +1,52 @@ +package dev.callback; + +/** + * detail: 通用抽象回调 - 基类 + * @author Ttt + */ +public abstract class AbstractCommonCallBack extends AbstractCallBack { + + public AbstractCommonCallBack() { + } + + public AbstractCommonCallBack(T value) { + super(value); + } + + public AbstractCommonCallBack(T value, Object object) { + super(value, object); + } + + public AbstractCommonCallBack(T value, Object object, String tag) { + super(value, object, tag); + } + + // ============ + // = 回调方法 = + // ============ + + /** + * 结果回调通知 + * @param str 返回数据 + * @param msg 返回信息 + * @param value 返回值 + */ + public void onResult(String str, String msg, T value) { + } + + /** + * 异常回调通知 + * @param e 异常信息 + * @param 泛型 + */ + public void onError(E e) { + } + + /** + * 失败回调通知 + * @param fail 失败信息 + * @param errorCode 错误 code + */ + public void onFailure(String fail, String errorCode) { + } +} diff --git a/lib/DevAssist/src/main/java/dev/callback/click/DevClickCallBack.java b/lib/DevAssist/src/main/java/dev/callback/click/DevClickCallBack.java new file mode 100644 index 0000000000..77b339ca6f --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/callback/click/DevClickCallBack.java @@ -0,0 +1,95 @@ +package dev.callback.click; + +import dev.callback.AbstractCommonCallBack; + +/** + * detail: 通用 Click 回调 + * @author Ttt + */ +public class DevClickCallBack extends AbstractCommonCallBack { + + public DevClickCallBack() { + } + + public DevClickCallBack(T value) { + super(value); + } + + public DevClickCallBack(T value, Object object) { + super(value, object); + } + + public DevClickCallBack(T value, Object object, String tag) { + super(value, object, tag); + } + + // ============ + // = 通用方法 = + // ============ + + /** + * 点击回调 + */ + public void onClick() { + } + + /** + * 长按回调 + */ + public void onLongClick() { + } + + // = + + /** + * 点击回调 + * @param param 回调参数 + */ + public void onClick(int param) { + } + + /** + * 长按回调 + * @param param 回调参数 + */ + public void onLongClick(int param) { + } + + // = + + /** + * 点击回调 + * @param param 回调参数 + * @param value 回调值 + */ + public void onClick(int param, T value) { + } + + /** + * 长按回调 + * @param param 回调参数 + * @param value 回调值 + */ + public void onLongClick(int param, T value) { + } + + // = + + /** + * 点击回调 + * @param param 回调参数 + * @param value 回调值 + * @param objects 回调对象数组 + */ + public void onClick(int param, T value, Object[] objects) { + } + + /** + * 长按回调 + * @param param 回调参数 + * @param value 回调值 + * @param objects 回调对象数组 + */ + public void onLongClick(int param, T value, Object[] objects) { + } +} diff --git a/lib/DevAssist/src/main/java/dev/callback/click/DevDialogCallBack.java b/lib/DevAssist/src/main/java/dev/callback/click/DevDialogCallBack.java new file mode 100644 index 0000000000..9553169d51 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/callback/click/DevDialogCallBack.java @@ -0,0 +1,77 @@ +package dev.callback.click; + +/** + * detail: 通用 Dialog 回调 + * @author Ttt + */ +public class DevDialogCallBack extends DevClickCallBack { + + public DevDialogCallBack() { + } + + public DevDialogCallBack(T value) { + super(value); + } + + public DevDialogCallBack(T value, Object object) { + super(value, object); + } + + public DevDialogCallBack(T value, Object object, String tag) { + super(value, object, tag); + } + + // ============ + // = 通知方法 = + // ============ + + /** + * 特殊通知 + */ + public void onDialogNotify() { + } + + /** + * show 通知 + */ + public void onDialogShow() { + } + + /** + * dismiss 通知 + */ + public void onDialogDismiss() { + } + + // = + + /** + * start 通知 + */ + public void onDialogStart() { + } + + /** + * resume 通知 + */ + public void onDialogResume() { + } + + /** + * pause 通知 + */ + public void onDialogPause() { + } + + /** + * stop 通知 + */ + public void onDialogStop() { + } + + /** + * destroy 通知 + */ + public void onDialogDestroy() { + } +} diff --git a/lib/DevAssist/src/main/java/dev/callback/click/DevItemClickCallBack.java b/lib/DevAssist/src/main/java/dev/callback/click/DevItemClickCallBack.java new file mode 100644 index 0000000000..5752f29998 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/callback/click/DevItemClickCallBack.java @@ -0,0 +1,95 @@ +package dev.callback.click; + +import dev.callback.AbstractCommonCallBack; + +/** + * detail: 通用 Item Click 回调 + * @author Ttt + */ +public class DevItemClickCallBack extends AbstractCommonCallBack { + + public DevItemClickCallBack() { + } + + public DevItemClickCallBack(T value) { + super(value); + } + + public DevItemClickCallBack(T value, Object object) { + super(value, object); + } + + public DevItemClickCallBack(T value, Object object, String tag) { + super(value, object, tag); + } + + // ============ + // = 通用方法 = + // ============ + + /** + * 点击 Item 回调 + */ + public void onItemClick() { + } + + /** + * 长按 Item 回调 + */ + public void onItemLongClick() { + } + + // = + + /** + * 点击 Item 回调 + * @param param 回调参数 + */ + public void onItemClick(int param) { + } + + /** + * 长按 Item 回调 + * @param param 回调参数 + */ + public void onItemLongClick(int param) { + } + + // = + + /** + * 点击 Item 回调 + * @param param 回调参数 + * @param value 回调值 + */ + public void onItemClick(int param, T value) { + } + + /** + * 长按 Item 回调 + * @param param 回调参数 + * @param value 回调值 + */ + public void onItemLongClick(int param, T value) { + } + + // = + + /** + * 点击 Item 回调 + * @param param 回调参数 + * @param value 回调值 + * @param objects 回调对象数组 + */ + public void onItemClick(int param, T value, Object[] objects) { + } + + /** + * 长按 Item 回调 + * @param param 回调参数 + * @param value 回调值 + * @param objects 回调对象数组 + */ + public void onItemLongClick(int param, T value, Object[] objects) { + } +} diff --git a/lib/DevAssist/src/main/java/dev/callback/common/DevCallBack.java b/lib/DevAssist/src/main/java/dev/callback/common/DevCallBack.java new file mode 100644 index 0000000000..e5560380c8 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/callback/common/DevCallBack.java @@ -0,0 +1,59 @@ +package dev.callback.common; + +import dev.callback.AbstractCallBack; + +/** + * detail: Dev 回调基类 + * @author Ttt + */ +public class DevCallBack extends AbstractCallBack { + + public DevCallBack() { + } + + public DevCallBack(T value) { + super(value); + } + + public DevCallBack(T value, Object object) { + super(value, object); + } + + public DevCallBack(T value, Object object, String tag) { + super(value, object, tag); + } + + // ============ + // = 通用方法 = + // ============ + + /** + * 回调方法 + */ + public void callback() { + } + + /** + * 回调方法 + * @param param 回调参数 + */ + public void callback(int param) { + } + + /** + * 回调方法 + * @param param 回调参数 + * @param value 回调值 + */ + public void callback(int param, T value) { + } + + /** + * 回调方法 + * @param param 回调参数 + * @param value 回调值 + * @param objects 回调对象数组 + */ + public void callback(int param, T value, Object[] objects) { + } +} diff --git a/lib/DevAssist/src/main/java/dev/callback/common/DevFilterCallback.java b/lib/DevAssist/src/main/java/dev/callback/common/DevFilterCallback.java new file mode 100644 index 0000000000..82ffd47ee4 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/callback/common/DevFilterCallback.java @@ -0,0 +1,89 @@ +package dev.callback.common; + +import dev.callback.AbstractCallBack; + +/** + * detail: 过滤处理回调类 + * @author Ttt + */ +public class DevFilterCallback extends AbstractCallBack { + + public DevFilterCallback() { + } + + public DevFilterCallback(T value) { + super(value); + } + + public DevFilterCallback(T value, Object object) { + super(value, object); + } + + public DevFilterCallback(T value, Object object, String tag) { + super(value, object, tag); + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 过滤处理 + * @param value 待处理值 + * @return 过滤处理的值 + */ + public T filter(T value) { + return value; + } + + /** + * 过滤处理 + * @param values 待处理数组值 + * @return 过滤处理的数组值 + */ + public T[] filter(T[] values) { + return values; + } + + // = + + /** + * 判断是否过滤 + * @param value 待判断值 + * @return {@code true} yes, {@code false} no + */ + public boolean isFilter(T value) { + return false; + } + + /** + * 判断是否过滤 + * @param values 待判断值 + * @return {@code true} yes, {@code false} no + */ + public boolean isFilter(T[] values) { + return false; + } + + // = + + /** + * 对比过滤处理 + * @param value 待对比值 + * @param value1 待对比值 + * @return {@code true} yes, {@code false} no + */ + public boolean compare(T value, T value1) { + return false; + } + + /** + * 对比过滤处理 + * @param values 待对比值 + * @param values1 待对比值 + * @return {@code true} yes, {@code false} no + */ + public boolean compare(T[] values, T[] values1) { + return false; + } +} diff --git a/lib/DevAssist/src/main/java/dev/callback/result/DevExResultCallback.java b/lib/DevAssist/src/main/java/dev/callback/result/DevExResultCallback.java new file mode 100644 index 0000000000..92cc1e2712 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/callback/result/DevExResultCallback.java @@ -0,0 +1,110 @@ +package dev.callback.result; + +/** + * detail: 通用结果回调类 ( 针对 DevResultCallback 进行扩展 ) + * @author Ttt + *
+ *     针对 DevResultCallback 进行扩展, 实现多个地方共用一个回调, 并且区分各个结果回调等
+ * 
+ */ +public abstract class DevExResultCallback extends DevResultCallback { + + public DevExResultCallback() { + super(); + } + + public DevExResultCallback(T value) { + super(value); + } + + public DevExResultCallback(T value, Object object) { + super(value, object); + } + + public DevExResultCallback(T value, Object object, String tag) { + super(value, object, tag); + } + + // ===================== + // = DevResultCallback = + // ===================== + + /** + * 结果回调通知 + * @param str 返回数据 + * @param msg 返回信息 + * @param value 返回值 + */ + @Override + public void onResult(String str, String msg, T value) { + } + + // ============ + // = 扩展回调 = + // ============ + + /** + * 结果回调通知 + * @param type 类型 + * @param str 返回数据 + * @param msg 返回信息 + * @param value 返回值 + */ + public void onResult(int type, String str, String msg, T value) { + } + + /** + * 异常回调通知 + * @param type 类型 + * @param e 异常信息 + * @param 泛型 + */ + public void onError(int type, E e) { + } + + /** + * 失败回调通知 + * @param type 类型 + * @param fail 失败信息 + * @param errorCode 错误 code + */ + public void onFailure(int type, String fail, String errorCode) { + } + + // ============ + // = 扩展处理 = + // ============ + + /** + * detail: 数据业务抽象类 + * @author Ttt + *
+     *     处理数据业务类, 主要处理结果中参数的传递处理保存等
+     * 
+ */ + public static abstract class ExpandResult { + } + + // = + + // 数据业务对象 + private ExpandResult mExpandResult; + + /** + * 获取实体类 + * @return {@link ExpandResult} + */ + public final ExpandResult getExpandResult() { + return mExpandResult; + } + + /** + * 设置实体类 + * @param expandResult 数据业务抽象类 + * @return {@link DevExResultCallback} + */ + public final DevExResultCallback setExpandResult(final ExpandResult expandResult) { + this.mExpandResult = expandResult; + return this; + } +} diff --git a/lib/DevAssist/src/main/java/dev/callback/result/DevResultCallback.java b/lib/DevAssist/src/main/java/dev/callback/result/DevResultCallback.java new file mode 100644 index 0000000000..022c668c3a --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/callback/result/DevResultCallback.java @@ -0,0 +1,38 @@ +package dev.callback.result; + +import dev.callback.AbstractCommonCallBack; + +/** + * detail: 通用结果回调类 + * @author Ttt + */ +public abstract class DevResultCallback extends AbstractCommonCallBack { + + public DevResultCallback() { + } + + public DevResultCallback(T value) { + super(value); + } + + public DevResultCallback(T value, Object object) { + super(value, object); + } + + public DevResultCallback(T value, Object object, String tag) { + super(value, object, tag); + } + + // ============ + // = 回调方法 = + // ============ + + /** + * 结果回调通知 + * @param str 返回数据 + * @param msg 返回信息 + * @param value 返回值 + */ + @Override + public abstract void onResult(String str, String msg, T value); +} diff --git a/lib/DevAssist/src/main/java/dev/engine/http/DevHttpEngine.java b/lib/DevAssist/src/main/java/dev/engine/http/DevHttpEngine.java new file mode 100644 index 0000000000..96b1669795 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/engine/http/DevHttpEngine.java @@ -0,0 +1,90 @@ +package dev.engine.http; + +/** + * detail: Http Engine + * @author Ttt + *
+ *     Application: IHttpEngine.initEngine()
+ *     use: IHttpEngine.xxx
+ * 
+ */ +public final class DevHttpEngine { + + private DevHttpEngine() { + } + + // IHttpEngine + private static IHttpEngine sHttpEngine; + + /** + * 初始化 Engine + * @param httpEngine {@link IHttpEngine} + */ + public static void initEngine(final IHttpEngine httpEngine) { + DevHttpEngine.sHttpEngine = httpEngine; + } + + // =============== + // = IHttpEngine = + // =============== + + // ============= + // = 获取 Call = + // ============= + + /** + * 获取 Request Call Object + * @param request {@link IHttpEngine.Request} + * @param callBack {@link IHttpEngine.RequestCallBack} + * @return {@link IHttpEngine.Call} + */ + public static IHttpEngine.Call newCall(final IHttpEngine.Request request, final IHttpEngine.RequestCallBack callBack) { + if (sHttpEngine != null) { + return sHttpEngine.newCall(request, callBack); + } + return null; + } + + // ============ + // = 操作方法 = + // ============ + + /** + * 取消请求 ( 全部 ) + */ + public static void cancelAll() { + if (sHttpEngine != null) { + sHttpEngine.cancelAll(); + } + } + + /** + * 取消请求 + * @param call {@link IHttpEngine.Call} + */ + public static void cancelCall(final IHttpEngine.Call call) { + if (sHttpEngine != null) { + sHttpEngine.cancelCall(call); + } + } + + /** + * 取消请求 + * @param url Request Url + */ + public static void cancelUrl(final String url) { + if (sHttpEngine != null) { + sHttpEngine.cancelUrl(url); + } + } + + /** + * 取消请求 + * @param object Object Tag + */ + public static void cancelTag(final Object object) { + if (sHttpEngine != null) { + sHttpEngine.cancelTag(object); + } + } +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/engine/http/IHttpEngine.java b/lib/DevAssist/src/main/java/dev/engine/http/IHttpEngine.java new file mode 100644 index 0000000000..cab9a68744 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/engine/http/IHttpEngine.java @@ -0,0 +1,189 @@ +package dev.engine.http; + +/** + * detail: Http Engine 接口 + * @author Ttt + */ +public interface IHttpEngine { + + /** + * detail: Request Object + * @author Ttt + */ + class Request { + } + + /** + * detail: Request Response + * Created by Ttt + */ + class Response { + + // Request + protected Request request; + // 发送请求时间 + protected long sentRequestAtMillis; + // 请求响应时间 + protected long receivedResponseAtMillis; + + /** + * 获取 Request Object + * @param 泛型 + * @return Request Object + */ + public final T getRequest() { + try { + return (T) request; + } catch (Exception e) { + } + return null; + } + + /** + * 获取发送请求时间 + * @return 发送请求时间 + */ + public final long getSentRequestAtMillis() { + return sentRequestAtMillis; + } + + /** + * 获取请求响应时间 + * @return 请求响应时间 + */ + public final long getReceivedResponseAtMillis() { + return receivedResponseAtMillis; + } + } + + /** + * detail: Request Call Operate + * @author Ttt + */ + interface Call { + + /** + * 获取 Request Object + * @param 泛型 + * @return Request Object + */ + T getRequest(); + + // ======== + // = 状态 = + // ======== + + /** + * 是否取消请求 + * @return {@code true} yes, {@code false} no + */ + boolean isCanceled(); + + /** + * 是否执行过请求 + * @return {@code true} yes, {@code false} no + */ + boolean isExecuted(); + + /** + * 是否请求结束 + * @return {@code true} yes, {@code false} no + */ + boolean isEnd(); + + // ============ + // = 操作方法 = + // ============ + + /** + * 取消请求 + */ + void cancel(); + + /** + * 开始请求方法 ( 同步 ) + * @return {@code true} operation successfully, {@code false} operation failed + */ + boolean start(); + + /** + * 开始请求方法 ( 异步 ) + * @return {@code true} operation successfully, {@code false} operation failed + */ + boolean startAync(); + } + + /** + * detail: Request CallBack + * Created by Ttt + */ + abstract class RequestCallBack { + + /** + * 开始请求 + * @param call {@link Call} + */ + public void onStart(Call call) { + } + + /** + * 请求取消 + * @param call {@link Call} + */ + public void onCancel(Call call) { + } + + /** + * 请求响应 + * @param call {@link Call} + * @param response {@link Response} + */ + public abstract void onResponse(Call call, T response); + + /** + * 请求失败 + * @param call {@link Call} + * @param throwable {@link Throwable} + */ + public abstract void onFailure(Call call, Throwable throwable); + } + + // ============= + // = 获取 Call = + // ============= + + /** + * 获取 Request Call Object + * @param request {@link Request} + * @param callBack {@link RequestCallBack} + * @return {@link Call} + */ + Call newCall(Request request, RequestCallBack callBack); + + // ============ + // = 操作方法 = + // ============ + + /** + * 取消请求 ( 全部 ) + */ + void cancelAll(); + + /** + * 取消请求 + * @param call {@link Call} + */ + void cancelCall(Call call); + + /** + * 取消请求 + * @param url Request Url + */ + void cancelUrl(String url); + + /** + * 取消请求 + * @param object Object Tag + */ + void cancelTag(Object object); +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/engine/image/DevImageEngine.java b/lib/DevAssist/src/main/java/dev/engine/image/DevImageEngine.java new file mode 100644 index 0000000000..54380eb3fa --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/engine/image/DevImageEngine.java @@ -0,0 +1,133 @@ +package dev.engine.image; + +import android.widget.ImageView; + +/** + * detail: Image Engine + * @author Ttt + *
+ *     Application: DevImageEngine.initEngine()
+ *     use: DevImageEngine.xxx
+ *     test if: getTranscodeType() == Bitmap.class
+ * 
+ */ +public final class DevImageEngine { + + private DevImageEngine() { + } + + // IImageEngine + private static IImageEngine sImageEngine; + + /** + * 初始化 Engine + * @param imageEngine {@link IImageEngine} + */ + public static void initEngine(final IImageEngine imageEngine) { + DevImageEngine.sImageEngine = imageEngine; + } + + // ================ + // = IImageEngine = + // ================ + + // ============ + // = 图片显示 = + // ============ + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + */ + public static void displayImage(final String uri, final ImageView imageView) { + if (sImageEngine != null) { + sImageEngine.displayImage(uri, imageView); + } + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param defaultImage Default resource image + */ + public static void displayImage(final String uri, final ImageView imageView, final int defaultImage) { + if (sImageEngine != null) { + sImageEngine.displayImage(uri, imageView, defaultImage); + } + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param isDefault 是否使用默认配置 + */ + public static void displayImage(final String uri, final ImageView imageView, final boolean isDefault) { + if (sImageEngine != null) { + sImageEngine.displayImage(uri, imageView, isDefault); + } + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param config {@link IImageEngine.ImageConfig} 加载配置 + */ + public static void displayImage(final String uri, final ImageView imageView, final IImageEngine.ImageConfig config) { + if (sImageEngine != null) { + sImageEngine.displayImage(uri, imageView, config); + } + } + + // ============ + // = 图片加载 = + // ============ + + /** + * 图片加载 + * @param uri Image Uri + * @param imageCallback 图片加载回调 + */ + public static void loadImage(final String uri, final IImageEngine.ImageCallBack imageCallback) { + if (sImageEngine != null) { + sImageEngine.loadImage(uri, imageCallback); + } + } + + /** + * 图片加载 + * @param uri Image Uri + * @param imageCallback 图片加载回调 + * @param config {@link IImageEngine.ImageConfig} 加载配置 + */ + public static void loadImage(final String uri, final IImageEngine.ImageCallBack imageCallback, final IImageEngine.ImageConfig config) { + if (sImageEngine != null) { + sImageEngine.loadImage(uri, imageCallback, config); + } + } + + // ============ + // = 其他方法 = + // ============ + + /** + * 清除磁盘缓存 + */ + public static void clearDiskCache() { + if (sImageEngine != null) { + sImageEngine.clearDiskCache(); + } + } + + /** + * 清除内存缓存 + */ + public static void clearMemoryCache() { + if (sImageEngine != null) { + sImageEngine.clearMemoryCache(); + } + } +} diff --git a/lib/DevAssist/src/main/java/dev/engine/image/IImageEngine.java b/lib/DevAssist/src/main/java/dev/engine/image/IImageEngine.java new file mode 100644 index 0000000000..f7d11020e0 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/engine/image/IImageEngine.java @@ -0,0 +1,151 @@ +package dev.engine.image; + +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.widget.ImageView; + +/** + * detail: Image Engine 接口 + * @author Ttt + */ +public interface IImageEngine { + + /** + * detail: Image Config + * @author Ttt + */ + class ImageConfig { + } + + /** + * detail: 图片加载回调 + * @param 泛型 + * @author Ttt + */ + interface ImageCallBack { + + /** + * 获取转码类型 + * @return TranscodeType Class + */ + Class getTranscodeType(); + + /** + * 响应回调 + * @param uri Image Uri + * @param value Value + */ + void onResponse(String uri, TranscodeType value); + + /** + * 失败回调 + * @param uri Image Uri + * @param throwable Throwable + */ + void onFailure(String uri, Throwable throwable); + } + + /** + * detail: Bitmap CallBack + * @author Ttt + */ + abstract class BitmapCallBack implements ImageCallBack { + + @Override + public final Class getTranscodeType() { + return Bitmap.class; + } + + @Override + public abstract void onResponse(String uri, Bitmap value); + + @Override + public abstract void onFailure(String uri, Throwable throwable); + } + + /** + * detail: Drawable CallBack + * @author Ttt + */ + abstract class DrawableCallBack implements ImageCallBack { + + @Override + public final Class getTranscodeType() { + return Drawable.class; + } + + @Override + public abstract void onResponse(String uri, Drawable value); + + @Override + public abstract void onFailure(String uri, Throwable throwable); + } + + // ============ + // = 图片显示 = + // ============ + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + */ + void displayImage(String uri, ImageView imageView); + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param defaultImage Default resource image + */ + void displayImage(String uri, ImageView imageView, int defaultImage); + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param isDefault 是否使用默认配置 + */ + void displayImage(String uri, ImageView imageView, boolean isDefault); + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param config {@link ImageConfig} 加载配置 + */ + void displayImage(String uri, ImageView imageView, ImageConfig config); + + // ============ + // = 图片加载 = + // ============ + + /** + * 图片加载 + * @param uri Image Uri + * @param imageCallback 图片加载回调 + */ + void loadImage(String uri, ImageCallBack imageCallback); + + /** + * 图片加载 + * @param uri Image Uri + * @param imageCallback 图片加载回调 + * @param config {@link ImageConfig} 加载配置 + */ + void loadImage(String uri, ImageCallBack imageCallback, ImageConfig config); + + // ============ + // = 其他方法 = + // ============ + + /** + * 清除磁盘缓存 + */ + void clearDiskCache(); + + /** + * 清除内存缓存 + */ + void clearMemoryCache(); +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/engine/json/DevJSONEngine.java b/lib/DevAssist/src/main/java/dev/engine/json/DevJSONEngine.java new file mode 100644 index 0000000000..b6817284bd --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/engine/json/DevJSONEngine.java @@ -0,0 +1,191 @@ +package dev.engine.json; + +import java.lang.reflect.Type; + +/** + * detail: JSON Engine + * @author Ttt + *
+ *     Application: DevJSONEngine.initEngine()
+ *     use: DevJSONEngine.xxx
+ * 
+ */ +public final class DevJSONEngine { + + private DevJSONEngine() { + } + + // IJSONEngine + private static IJSONEngine sJSONEngine; + + /** + * 初始化 Engine + * @param jsonEngine {@link IJSONEngine} + */ + public static void initEngine(final IJSONEngine jsonEngine) { + DevJSONEngine.sJSONEngine = jsonEngine; + } + + // =============== + // = IJSONEngine = + // =============== + + // ============ + // = 转换方法 = + // ============ + + /** + * 将对象转换为 JSON String + * @param object {@link Object} + * @return JSON String + */ + public static String toJson(final Object object) { + if (sJSONEngine != null) { + return sJSONEngine.toJson(object); + } + return null; + } + + /** + * 将对象转换为 JSON String + * @param object {@link Object} + * @param jsonConfig {@link IJSONEngine.JSONConfig} + * @return JSON String + */ + public static String toJson(final Object object, final IJSONEngine.JSONConfig jsonConfig) { + if (sJSONEngine != null) { + return sJSONEngine.toJson(object, jsonConfig); + } + return null; + } + + // = + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param classOfT {@link Class} T + * @param 泛型 + * @return instance of type + */ + public static T fromJson(final String json, final Class classOfT) { + if (sJSONEngine != null) { + return sJSONEngine.fromJson(json, classOfT); + } + return null; + } + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param classOfT {@link Class} T + * @param jsonConfig {@link IJSONEngine.JSONConfig} + * @param 泛型 + * @return instance of type + */ + public static T fromJson(final String json, final Class classOfT, final IJSONEngine.JSONConfig jsonConfig) { + if (sJSONEngine != null) { + return sJSONEngine.fromJson(json, classOfT, jsonConfig); + } + return null; + } + + // = + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param typeOfT {@link Type} T + * @param 泛型 + * @return instance of type + */ + public static T fromJson(final String json, final Type typeOfT) { + if (sJSONEngine != null) { + return sJSONEngine.fromJson(json, typeOfT); + } + return null; + } + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param typeOfT {@link Type} T + * @param jsonConfig {@link IJSONEngine.JSONConfig} + * @param 泛型 + * @return instance of type + */ + public static T fromJson(final String json, final Type typeOfT, final IJSONEngine.JSONConfig jsonConfig) { + if (sJSONEngine != null) { + return sJSONEngine.fromJson(json, typeOfT, jsonConfig); + } + return null; + } + + // ============ + // = 其他方法 = + // ============ + + /** + * 判断字符串是否 JSON 格式 + * @param json 待校验 JSON String + * @return {@code true} yes, {@code false} no + */ + public static boolean isJSON(final String json) { + if (sJSONEngine != null) { + return sJSONEngine.isJSON(json); + } + return false; + } + + /** + * JSON String 缩进处理 + * @param json JSON String + * @return JSON String + */ + public static String toJsonIndent(final String json) { + if (sJSONEngine != null) { + return sJSONEngine.toJsonIndent(json); + } + return null; + } + + /** + * JSON String 缩进处理 + * @param json JSON String + * @param jsonConfig {@link IJSONEngine.JSONConfig} + * @return JSON String + */ + public static String toJsonIndent(final String json, final IJSONEngine.JSONConfig jsonConfig) { + if (sJSONEngine != null) { + return sJSONEngine.toJsonIndent(json, jsonConfig); + } + return null; + } + + // = + + /** + * Object 转 JSON String 并进行缩进处理 + * @param object {@link Object} + * @return JSON String + */ + public static String toJsonIndent(final Object object) { + if (sJSONEngine != null) { + return sJSONEngine.toJsonIndent(object); + } + return null; + } + + /** + * Object 转 JSON String 并进行缩进处理 + * @param object {@link Object} + * @param jsonConfig {@link IJSONEngine.JSONConfig} + * @return JSON String + */ + public static String toJsonIndent(final Object object, final IJSONEngine.JSONConfig jsonConfig) { + if (sJSONEngine != null) { + return sJSONEngine.toJsonIndent(object, jsonConfig); + } + return null; + } +} diff --git a/lib/DevAssist/src/main/java/dev/engine/json/IJSONEngine.java b/lib/DevAssist/src/main/java/dev/engine/json/IJSONEngine.java new file mode 100644 index 0000000000..7a3f16f15f --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/engine/json/IJSONEngine.java @@ -0,0 +1,121 @@ +package dev.engine.json; + +import java.lang.reflect.Type; + +/** + * detail: JSON Engine 接口 + * @author Ttt + */ +public interface IJSONEngine { + + /** + * detail: JSON Config + * @author Ttt + */ + class JSONConfig { + } + + // ============ + // = 转换方法 = + // ============ + + /** + * 将对象转换为 JSON String + * @param object {@link Object} + * @return JSON String + */ + String toJson(Object object); + + /** + * 将对象转换为 JSON String + * @param object {@link Object} + * @param jsonConfig {@link JSONConfig} + * @return JSON String + */ + String toJson(Object object, JSONConfig jsonConfig); + + // = + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param classOfT {@link Class} T + * @param 泛型 + * @return instance of type + */ + T fromJson(String json, Class classOfT); + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param classOfT {@link Class} T + * @param jsonConfig {@link JSONConfig} + * @param 泛型 + * @return instance of type + */ + T fromJson(String json, Class classOfT, JSONConfig jsonConfig); + + // = + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param typeOfT {@link Type} T + * @param 泛型 + * @return instance of type + */ + T fromJson(String json, Type typeOfT); + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param typeOfT {@link Type} T + * @param jsonConfig {@link JSONConfig} + * @param 泛型 + * @return instance of type + */ + T fromJson(String json, Type typeOfT, JSONConfig jsonConfig); + + // ============ + // = 其他方法 = + // ============ + + /** + * 判断字符串是否 JSON 格式 + * @param json 待校验 JSON String + * @return {@code true} yes, {@code false} no + */ + boolean isJSON(String json); + + /** + * JSON String 缩进处理 + * @param json JSON String + * @return JSON String + */ + String toJsonIndent(String json); + + /** + * JSON String 缩进处理 + * @param json JSON String + * @param jsonConfig {@link JSONConfig} + * @return JSON String + */ + String toJsonIndent(String json, JSONConfig jsonConfig); + + // = + + /** + * Object 转 JSON String 并进行缩进处理 + * @param object {@link Object} + * @return JSON String + */ + String toJsonIndent(Object object); + + /** + * Object 转 JSON String 并进行缩进处理 + * @param object {@link Object} + * @param jsonConfig {@link JSONConfig} + * @return JSON String + */ + String toJsonIndent(Object object, JSONConfig jsonConfig); +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/java/dev/engine/log/DevLogEngine.java b/lib/DevAssist/src/main/java/dev/engine/log/DevLogEngine.java new file mode 100644 index 0000000000..3afb0b51b2 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/engine/log/DevLogEngine.java @@ -0,0 +1,283 @@ +package dev.engine.log; + +/** + * detail: Log Engine + * @author Ttt + *
+ *     Application: DevLogEngine.initEngine()
+ *     use: DevLogEngine.xxx
+ * 
+ */ +public final class DevLogEngine { + + private DevLogEngine() { + } + + // ILogEngine + private static ILogEngine sLogEngine; + + /** + * 初始化 Engine + * @param logEngine {@link ILogEngine} + */ + public static void initEngine(final ILogEngine logEngine) { + DevLogEngine.sLogEngine = logEngine; + } + + // ============== + // = ILogEngine = + // ============== + + // ============ + // = 配置方法 = + // ============ + + /** + * 判断是否打印日志 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPrintLog() { + if (sLogEngine != null) { + return sLogEngine.isPrintLog(); + } + return false; + } + + // =============================== + // = 使用默认 TAG - 日志打印方法 = + // =============================== + + /** + * 打印 Log.DEBUG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void d(final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.d(message, args); + } + } + + /** + * 打印 Log.ERROR + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void e(final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.e(message, args); + } + } + + /** + * 打印 Log.ERROR + * @param throwable 异常 + */ + public static void e(final Throwable throwable) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.e(throwable); + } + } + + /** + * 打印 Log.ERROR + * @param throwable 异常 + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void e(final Throwable throwable, final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.e(throwable, message, args); + } + } + + /** + * 打印 Log.WARN + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void w(final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.w(message, args); + } + } + + /** + * 打印 Log.INFO + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void i(final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.i(message, args); + } + } + + /** + * 打印 Log.VERBOSE + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void v(final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.v(message, args); + } + } + + /** + * 打印 Log.ASSERT + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void wtf(final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.wtf(message, args); + } + } + + // = + + /** + * 格式化 JSON 格式数据, 并打印 + * @param json JSON 格式字符串 + */ + public static void json(final String json) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.json(json); + } + } + + /** + * 格式化 XML 格式数据, 并打印 + * @param xml XML 格式字符串 + */ + public static void xml(final String xml) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.xml(xml); + } + } + + // ================================= + // = 使用自定义 TAG - 日志打印方法 = + // ================================= + + /** + * 打印 Log.DEBUG + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void dTag(final String tag, final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.dTag(tag, message, args); + } + } + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void eTag(final String tag, final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.eTag(tag, message, args); + } + } + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param throwable 异常 + */ + public static void eTag(final String tag, final Throwable throwable) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.eTag(tag, throwable); + } + } + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param throwable 异常 + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void eTag(final String tag, final Throwable throwable, final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.eTag(tag, throwable, message, args); + } + } + + /** + * 打印 Log.WARN + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void wTag(final String tag, final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.wTag(tag, message, args); + } + } + + /** + * 打印 Log.INFO + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void iTag(final String tag, final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.iTag(tag, message, args); + } + } + + /** + * 打印 Log.VERBOSE + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void vTag(final String tag, final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.vTag(tag, message, args); + } + } + + /** + * 打印 Log.ASSERT + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + public static void wtfTag(final String tag, final String message, final Object... args) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.wtfTag(tag, message, args); + } + } + + // = + + /** + * 格式化 JSON 格式数据, 并打印 + * @param tag 日志 TAG + * @param json JSON 格式字符串 + */ + public static void jsonTag(final String tag, final String json) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.jsonTag(tag, json); + } + } + + /** + * 格式化 XML 格式数据, 并打印 + * @param tag 日志 TAG + * @param xml XML 格式字符串 + */ + public static void xmlTag(final String tag, final String xml) { + if (isPrintLog() && sLogEngine != null) { + sLogEngine.xmlTag(tag, xml); + } + } +} diff --git a/lib/DevAssist/src/main/java/dev/engine/log/ILogEngine.java b/lib/DevAssist/src/main/java/dev/engine/log/ILogEngine.java new file mode 100644 index 0000000000..4122b9fc89 --- /dev/null +++ b/lib/DevAssist/src/main/java/dev/engine/log/ILogEngine.java @@ -0,0 +1,176 @@ +package dev.engine.log; + +/** + * detail: Log Engine 接口 + * @author Ttt + */ +public interface ILogEngine { + + // ============ + // = 配置方法 = + // ============ + + /** + * 判断是否打印日志 + * @return {@code true} yes, {@code false} no + */ + boolean isPrintLog(); + + // =============================== + // = 使用默认 TAG - 日志打印方法 = + // =============================== + + /** + * 打印 Log.DEBUG + * @param message 日志信息 + * @param args 格式化参数 + */ + void d(String message, Object... args); + + /** + * 打印 Log.ERROR + * @param message 日志信息 + * @param args 格式化参数 + */ + void e(String message, Object... args); + + /** + * 打印 Log.ERROR + * @param throwable 异常 + */ + void e(Throwable throwable); + + /** + * 打印 Log.ERROR + * @param throwable 异常 + * @param message 日志信息 + * @param args 格式化参数 + */ + void e(Throwable throwable, String message, Object... args); + + /** + * 打印 Log.WARN + * @param message 日志信息 + * @param args 格式化参数 + */ + void w(String message, Object... args); + + /** + * 打印 Log.INFO + * @param message 日志信息 + * @param args 格式化参数 + */ + void i(String message, Object... args); + + /** + * 打印 Log.VERBOSE + * @param message 日志信息 + * @param args 格式化参数 + */ + void v(String message, Object... args); + + /** + * 打印 Log.ASSERT + * @param message 日志信息 + * @param args 格式化参数 + */ + void wtf(String message, Object... args); + + // = + + /** + * 格式化 JSON 格式数据, 并打印 + * @param json JSON 格式字符串 + */ + void json(String json); + + /** + * 格式化 XML 格式数据, 并打印 + * @param xml XML 格式字符串 + */ + void xml(String xml); + + // ================================= + // = 使用自定义 TAG - 日志打印方法 = + // ================================= + + /** + * 打印 Log.DEBUG + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void dTag(String tag, String message, Object... args); + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void eTag(String tag, String message, Object... args); + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param throwable 异常 + */ + void eTag(String tag, Throwable throwable); + + /** + * 打印 Log.ERROR + * @param tag 日志 TAG + * @param throwable 异常 + * @param message 日志信息 + * @param args 格式化参数 + */ + void eTag(String tag, Throwable throwable, String message, Object... args); + + /** + * 打印 Log.WARN + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void wTag(String tag, String message, Object... args); + + /** + * 打印 Log.INFO + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void iTag(String tag, String message, Object... args); + + /** + * 打印 Log.VERBOSE + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void vTag(String tag, String message, Object... args); + + /** + * 打印 Log.ASSERT + * @param tag 日志 TAG + * @param message 日志信息 + * @param args 格式化参数 + */ + void wtfTag(String tag, String message, Object... args); + + // = + + /** + * 格式化 JSON 格式数据, 并打印 + * @param tag 日志 TAG + * @param json JSON 格式字符串 + */ + void jsonTag(String tag, String json); + + /** + * 格式化 XML 格式数据, 并打印 + * @param tag 日志 TAG + * @param xml XML 格式字符串 + */ + void xmlTag(String tag, String xml); +} \ No newline at end of file diff --git a/lib/DevAssist/src/main/res/values/strings.xml b/lib/DevAssist/src/main/res/values/strings.xml new file mode 100644 index 0000000000..8542005550 --- /dev/null +++ b/lib/DevAssist/src/main/res/values/strings.xml @@ -0,0 +1,2 @@ + + diff --git a/lib/DevJava/.gitignore b/lib/DevJava/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/lib/DevJava/.gitignore @@ -0,0 +1 @@ +/build diff --git a/lib/DevJava/CHANGELOG.md b/lib/DevJava/CHANGELOG.md new file mode 100644 index 0000000000..5851b664ce --- /dev/null +++ b/lib/DevJava/CHANGELOG.md @@ -0,0 +1,26 @@ +Change Log +========== + +Version 1.0.3 *(2019-10-09)* +---------------------------- + + * `[Add]` NumberUtils#getMultiple、getMultipleI、getMultipleD、getMultipleL、getMultipleF + + +Version 1.0.2 *(2019-09-19)* +---------------------------- + + * `[Update]` 修改 FileBreadthFirstSearchUtils 部分方法返回值 (返回当前对象, 方便链式调用) + + +Version 1.0.1 *(2019-09-12)* +---------------------------- + + * `[Add]` ConvertUtils#convert + + +Version 1.0.0 *(2019-08-25)* +---------------------------- + + Initial release + diff --git a/lib/DevJava/README.md b/lib/DevJava/README.md new file mode 100644 index 0000000000..afcd650c0a --- /dev/null +++ b/lib/DevJava/README.md @@ -0,0 +1,1097 @@ + +## Gradle + +```java +implementation 'com.afkt:DevJava:1.0.3' +``` + +## 目录结构 + +``` +- dev.utils | 根目录 + - common | Java 工具类, 不依赖 android api + - assist | 各种快捷辅助类 + - search | 搜索相关 ( 文件搜索等 ) + - cipher | 编 / 解码工具类 + - encrypt | 加密工具类 + - random | 随机概率算法工具类 + - thread | 线程相关 + - validator | 数据校验工具类 +``` + + +## 事项 + +- 该工具类不依赖 android api,属于 Java 工具类库 + +- 开启日志 +```java +// 打开 lib 内部日志 - 线上 (release) 环境,不调用方法就行 +JCLogUtils.setPrintLog(true); +// 控制台打印日志 +JCLogUtils.setControlPrintLog(true); +// 设置 Java 模块日志信息监听 +JCLogUtils.setPrint(new JCLogUtils.Print() {}); +``` + +- 部分 API 更新不及时或有遗漏等,`具体以对应的工具类为准` + +- [检测代码规范、注释内容排版,API 文档生成](https://github.com/afkT/JavaDoc) + +## API + + +- dev.utils | 根目录 + - [common](#devutilscommon) | Java 工具类, 不依赖 android api + - [assist](#devutilscommonassist) | 各种快捷辅助类 + - [search](#devutilscommonassistsearch) | 搜索相关 ( 文件搜索等 ) + - [cipher](#devutilscommoncipher) | 编 / 解码工具类 + - [encrypt](#devutilscommonencrypt) | 加密工具类 + - [random](#devutilscommonrandom) | 随机概率算法工具类 + - [thread](#devutilscommonthread) | 线程相关 + - [validator](#devutilscommonvalidator) | 数据校验工具类 + + + + +## **`dev.utils.common`** + + +* **Array 数组工具类 ->** [ArrayUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/ArrayUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断数组是否为 null | +| isNotEmpty | 判断数组是否不为 null | +| length | 获取数组长度 | +| isLength | 判断数组长度是否等于期望长度 | +| get | 获取数组对应索引数据 | +| getFirst | 获取数组第一条数据 | +| getLast | 获取数组最后一条数据 | +| getPosition | 根据指定值获取 value 所在位置 + 偏移量的索引 | +| getNotNull | 根据指定 value 获取 value 所在位置 + 偏移量的值, 不允许值为 null | +| getPositionNotNull | 根据指定 value 获取索引, 不允许值为 null | +| intsToIntegers | int[] 转换 Integer[] | +| bytesToBytes | byte[] 转换 Byte[] | +| charsToCharacters | char[] 转换 Character[] | +| shortsToShorts | short[] 转换 Short[] | +| longsToLongs | long[] 转换 Long[] | +| floatsToFloats | float[] 转换 Float[] | +| doublesToDoubles | double[] 转换 Double[] | +| booleansToBooleans | boolean[] 转换 Boolean[] | +| integersToInts | Integer[] 转换 int[] | +| charactersToChars | Character[] 转换 char[] | +| asList | 转换数组为集合 | +| equals | 判断两个值是否一样 | +| arraycopy | 拼接数组 | +| newarray | 创建指定长度数组 | +| subarray | 从数组上截取一段 | +| appendToString | 追加数组内容字符串 | + + +* **资金运算工具类 ->** [BigDecimalUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/BigDecimalUtils.java) + +| 方法 | 注释 | +| :- | :- | +| add | 提供精确的加法运算 | +| substract | 提供精确的减法运算 | +| multiply | 提供精确的乘法运算 | +| divide | 提供 ( 相对 ) 精确的除法运算 | +| round | 提供精确的小数位四舍五入处理 | +| remainder | 取余数 | +| compare | 比较大小 | +| formatMoney | 金额分割, 四舍五入金额 | +| adjustDouble | 获取自己想要的数据格式 | + + +* **类 (Class) 工具类 ->** [ClassUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/ClassUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isBaseDataType | 判断类是否是基础数据类型 - 目前支持 11 种 | +| newInstance | 根据类获取对象, 不再必须一个无参构造 | +| getDefaultPrimiticeValue | 判断 Class 是否为原始类型 (boolean、char、byte、short、int、long、float、double) | +| isCollection | 判断是否集合类型 | +| isArray | 判断是否数组类型 | +| getGenericSuperclass | 获取父类泛型类型 | +| getGenericInterfaces | 获取接口泛型类型 | + + +* **克隆工具类 ->** [CloneUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/CloneUtils.java) + +| 方法 | 注释 | +| :- | :- | +| deepClone | 进行克隆 | +| serializableToBytes | 通过序列化实体类, 获取对应的 byte[] 数据 | + + +* **关闭 (IO 流 ) 工具类 ->** [CloseUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/CloseUtils.java) + +| 方法 | 注释 | +| :- | :- | +| closeIO | 关闭 IO | +| closeIOQuietly | 安静关闭 IO | + + +* **集合工具类 (Collection - List、Set、Queue) 等 ->** [CollectionUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/CollectionUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断 Collection 是否为 null | +| isNotEmpty | 判断 Collection 是否不为 null | +| length | 获取 Collection 长度 | +| isLength | 获取长度 to Collection 是否等于期望长度 | +| greaterThan | 判断 Collection 长度是否大于指定长度 | +| greaterThanOrEqual | 判断 Collection 长度是否大于等于指定长度 | +| lessThan | 判断 Collection 长度是否小于指定长度 | +| lessThanOrEqual | 判断 Collection 长度是否小于等于指定长度 | +| get | 获取数据 | +| getFirst | 获取第一条数据 | +| getLast | 获取最后一条数据 | +| getPosition | 根据指定 value 获取 value 所在位置 + 偏移量的索引 | +| getPositionNotNull | 根据指定 value 获取索引, 不允许值为 null | +| getNext | 根据指定 value 获取 value 所在位置的下一个值 | +| getNextNotNull | 根据指定 value 获取 value 所在位置的下一个值, 不允许值为 null | +| getPrevious | 根据指定 value 获取 value 所在位置的上一个值 | +| getPreviousNotNull | 根据指定 value 获取 value 所在位置的上一个值, 不允许值为 null | +| add | 添加一条数据 | +| addNotNull | 添加一条数据 - value 不允许为 null | +| addAll | 添加集合数据 | +| addAllNotNull | 添加集合数据 - values 内的值不允许为 null | +| remove | 移除一条数据 | +| removeAll | 移除集合数据 | +| clear | 清空集合中符合指定 value 的全部数据 | +| clearNotBelong | 保留集合中符合指定 value 的全部数据 | +| clearAll | 清空集合全部数据 | +| clearNull | 清空集合中为 null 的值 | +| isEqualCollection | 判断两个集合是否相同 | +| isEqualCollections | 判断多个集合是否相同 | +| union | 两个集合并集处理 | +| unions | 多个集合并集处理 | +| intersection | 两个集合交集处理 | +| disjunction | 两个集合交集的补集处理 | +| subtract | 两个集合差集 ( 扣除 ) 处理 | +| equals | 判断两个值是否一样 | +| toArray | 转换数组 to Object | +| reverse | 集合翻转处理 | + + +* **颜色工具类 ( 包括常用的色值 ) ->** [ColorUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/ColorUtils.java) + +| 方法 | 注释 | +| :- | :- | +| toHexAlpha | 获取十六进制透明度字符串 | +| alpha | 返回一个颜色中的透明度值 ( 返回十进制 ) | +| alphaPercent | 返回一个颜色中的透明度百分比值 | +| red | 返回一个颜色中红色的色值 ( 返回十进制 ) | +| redPercent | 返回一个颜色中红色的百分比值 | +| green | 返回一个颜色中绿色的色值 ( 返回十进制 ) | +| greenPercent | 返回一个颜色中绿色的百分比值 | +| blue | 返回一个颜色中蓝色的色值 ( 返回十进制 ) | +| bluePercent | 返回一个颜色中蓝色的百分比值 | +| rgb | 根据对应的 red、green、blue 生成一个颜色值 | +| argb | 根据对应的 alpha、red、green、blue 生成一个颜色值 ( 含透明度 ) | +| isRGB | 判断颜色 RGB 是否有效 | +| isARGB | 判断颜色 ARGB 是否有效 | +| setAlpha | 设置透明度 | +| setRed | 改变颜色值中的红色色值 | +| setGreen | 改变颜色值中的绿色色值 | +| setBlue | 改变颜色值中的蓝色色值 | +| parseColor | 解析颜色字符串, 返回对应的颜色值 | +| intToRgbString | 颜色值 转换 RGB 颜色字符串 | +| intToArgbString | 颜色值 转换 ARGB 颜色字符串 | +| getRandomColor | 获取随机颜色值 | +| judgeColorString | 判断是否为 ARGB 格式的十六进制颜色, 例如: FF990587 | +| setDark | 颜色加深 ( 单独修改 RGB 值, 不变动透明度 ) | +| setLight | 颜色变浅, 变亮 ( 单独修改 RGB 值, 不变动透明度 ) | +| setAlphaDark | 设置透明度加深 | +| setAlphaLight | 设置透明度变浅 | + + +* **转换工具类 (Byte、Hex 等 ) ->** [ConvertUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/ConvertUtils.java) + +| 方法 | 注释 | +| :- | :- | +| convert | Object 转换所需类型对象 | +| toString | char[] 转 String | +| toInt | 字符串 转 int | +| toBoolean | 字符串 转 boolean | +| toFloat | 字符串 转 float | +| toDouble | 字符串 转 double | +| toLong | 字符串 转 long | +| toShort | 基本类型对象 转 short | +| toChar | 基本类型对象 转 char | +| toByte | 基本类型对象 转 byte | +| toCharInt | char 转换 unicode 编码 | +| toChars | 字符串 获取 char[] | +| toBytes | 字符串 获取 byte[] | +| parseInt | 字符串转换对应的进制 | +| parseLong | 字符串转换对应的进制 | +| bytesToObject | byte[] 转为 Object | +| objectToBytes | Object 转为 byte[] | +| bytesToChars | byte[] 转换 char[], 并且进行补码 | +| charsToBytes | char[] 转换 byte[] | +| intsToStrings | int[] 转换 string[] | +| doublesToStrings | double[] 转换 string[] | +| longsToStrings | long[] 转换 string[] | +| floatsToStrings | float[] 转换 string[] | +| intsToDoubles | int[] 转换 double[] | +| intsToLongs | int[] 转换 long[] | +| intsToFloats | int[] 转换 float[] | +| stringsToInts | string[] 转换 int[] | +| stringsToDoubles | string[] 转换 double[] | +| stringsToLongs | string[] 转换 long[] | +| stringsToFloats | string[] 转换 float[] | +| doublesToInts | double[] 转换 int[] | +| longsToInts | long[] 转换 int[] | +| floatsToInts | float[] 转换 int[] | +| toBinaryString | 将 字节转换 为 二进制字符串 | +| decodeBinary | 二进制字符串 转换 byte[] 解码 | +| isHex | 判断是否十六进制数据 | +| decodeHex | 将十六进制字节数组解码 | +| hexToInt | 十六进制 char 转换 int | +| toHexString | int 转换十六进制 | +| toHexChars | 将 string 转换为 十六进制 char[] | +| bytesBitwiseAND | 按位求补 byte[] 位移编解码 ( 共用同一个方法 ) | + + +* **坐标 (GPS 纠偏 ) 相关工具类 ->** [CoordinateUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/CoordinateUtils.java) + +| 方法 | 注释 | +| :- | :- | +| bd09ToGcj02 | BD09 坐标转 GCJ02 坐标 | +| gcj02ToBd09 | GCJ02 坐标转 BD09 坐标 | +| gcj02ToWGS84 | GCJ02 坐标转 WGS84 坐标 | +| wgs84ToGcj02 | WGS84 坐标转 GCJ02 坐标 | +| bd09ToWGS84 | BD09 坐标转 WGS84 坐标 | +| wgs84ToBd09 | WGS84 坐标转 BD09 坐标 | +| outOfChina | 判断是否中国境外 | + + +* **日期工具类 ->** [DateUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/DateUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getDateNow | 获取当前日期的字符串 - yyyy-MM-dd HH:mm:ss | +| formatTime | 将时间戳转换日期字符串 | +| formatDate | 将 Date 转换日期字符串 | +| parseDate | 将时间戳转换成 Date | +| parseLong | 解析时间字符串转换为 long 毫秒 - 默认表示 time 属于 yyyy-MM-dd HH:mm:ss 格式 | +| parseToString | 转换时间为指定字符串 | +| getTimeDiffMinute | 获取时间差 - 分钟 | +| getTimeDiffHour | 获取时间差 - 小时 | +| getTimeDiffDay | 获取时间差 - 天 | +| getTimeDiff | 获取时间差 - ( 传入时间 - 当前时间 ) | +| getYear | 获取年 | +| getMonth | 获取月 (0 - 11) + 1 | +| getDay | 获取日 | +| getWeek | 获取日期是星期几 | +| get24Hour | 获取时 - 24 | +| get12Hour | 获取时 - 12 | +| getMinute | 获取分 | +| getSecond | 获取秒 | +| convertTime | 转换时间处理, 小于 10, 则自动补充 0x | +| isLeapYear | 判断是否闰年 | +| getMonthDayNumberAll | 根据年份、月份, 获取对应的天数 ( 完整天数, 无判断是否属于未来日期 ) | +| getYearMonthNumber | 根据年份, 获取对应的月份 | +| getMonthDayNumber | 根据年份、月份, 获取对应的天数 | +| getArrayToHH | 生成 HH 按时间排序数组 | +| getListToHH | 生成 HH 按时间排序集合 | +| getArrayToMM | 生成 MM 按时间排序数组 | +| getListToMM | 生成 MM 按时间排序集合 | +| getArrayToHHMM | 生成 HH:mm 按间隔时间排序数组 | +| getListToHHMM | 生成 HH:mm 按间隔时间排序集合 | +| getListToHHMMPosition | 获取 HH:mm 按间隔时间排序的集合中, 指定时间所在索引 | +| secToTimeRetain | 传入时间, 获取时间 (00:00:00 格式 ) - 不处理大于一天 | +| convertTimeArys | 传入时间, 时间参数 ( 小时、分钟、秒 ) | +| millisToFitTimeSpan | 转换时间 | +| millisToTimeArys | 转换时间为数组 | +| isInTimeHHmm | 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 | +| isInTimeHHmmss | 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 | +| isInTime | 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 | +| isInDate | 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 | +| getEndTimeDiffHHmm | 获取指定时间距离该时间第二天的指定时段的时间 ( 判断凌晨情况 ) | +| getEndTimeDiff | 获取指定时间距离该时间第二天的指定时段的时间差 ( 判断凌晨情况 ) | + + +* **开发常用方法工具类 ->** [DevCommonUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/DevCommonUtils.java) + +| 方法 | 注释 | +| :- | :- | +| timeRecord | 耗时时间记录 | +| getOperateTime | 获取操作时间 | +| sleepOperate | 堵塞操作 | +| isHttpRes | 判断是否网络资源 | +| whileMD5 | 循环 MD5 加密处理 | +| randomUUID | 获取随机唯一数 | +| randomUUIDToHashCode | 获取随机唯一数 HashCode | +| getRandomUUID | 获取随机规则生成 UUID | +| getRandomUUIDToString | 获取随机规则生成 UUID 字符串 | +| converHideMobile | 转换手机号 | +| converSymbolHide | 转换符号处理 | +| subEllipsize | 裁剪超出的内容, 并且追加符号 ( 如 ...) | +| subSymbolHide | 裁剪符号处理 | +| subSetSymbol | 裁剪内容, 设置符号处理 | +| substring | 裁剪字符串 | +| toReplaceSEWith | 替换 ( 删除 - 替换成 "") 字符串中符合 特定标记字符的 startsWith - endsWith | +| toReplaceStartsWith | 替换开头字符串 | +| toReplaceEndsWith | 替换结尾字符串 | +| toClearSEWiths | 这个方法功能主要把字符符合标记的 头部和尾部都替换成 "" | +| toClearStartsWith | 清空属于特定字符串开头的字段 | +| toClearEndsWith | 清空属于特定字符串结尾的字段 | +| replaceStr | 替换字符串 | +| replaceStrToNull | 替换字符串 | +| replaceStrs | 替换字符串 | +| isEmpty | 判断数组是否为 null | +| isNotEmpty | 判断数组是否不为 null | +| length | 获取数组长度 | +| isLength | 判断数组长度是否等于期望长度 | +| equals | 判断两个值是否一样 | +| isEquals | 判断多个字符串是否相等, 只有全相等才返回 true - 对比大小写 | +| isOrEquals | 判断多个字符串, 只要有一个符合条件则通过 | +| isContains | 判断一堆值中, 是否存在符合该条件的 ( 包含 ) | +| isStartsWith | 判断内容, 是否属于特定字符串开头 - 对比大小写 | +| isEndsWith | 判断内容, 是否属于特定字符串结尾 - 对比大小写 | +| isSpace | 判断字符串是否为 null 或全为空白字符 | +| toClearSpace | 清空字符串全部空格 | +| toClearSpaceTrim | 清空字符串前后所有空格 | +| appendSpace | 追加空格 | +| appendTab | 追加 Tab | +| appendLine | 追加换行 | +| toCheckValue | 检查字符串 | +| toCheckValues | 检查字符串 - 多个值 | +| toCheckValuesSpace | 检查字符串 - 多个值 ( 删除前后空格对比判断 ) | +| getFormatString | 获取格式化后的字符串 | +| getAutoFormatString | 获取自动数量格式化后的字符串 ( 可变参数 ) | +| getAutoFormatString2 | 获取自动数量格式化后的字符串 ( 可变参数 ) | +| appends | StringBuilder 拼接处理 | + + +* **编码工具类 ->** [EncodeUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/EncodeUtils.java) + +| 方法 | 注释 | +| :- | :- | +| base64Encode | Base64 编码 | +| base64EncodeToString | Base64 编码 | +| base64Decode | Base64 解码 | +| base64DecodeToString | Base64 解码 | +| htmlEncode | Html 字符串编码 | + + +* **变量字段工具类 ->** [FieldUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/FieldUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isSerializable | 判断是否序列化 | +| set | 设置字段的值 | +| get | 获取字段的值 | +| isLong | 是否 long/Long 类型 | +| isFloat | 是否 float/Float 类型 | +| isDouble | 是否 double/Double 类型 | +| isInteger | 是否 int/Integer 类型 | +| isString | 是否 String 类型 | +| getGenericType | 获取字段的泛型类型, 如果不带泛型返回 null | +| getComponentType | 获取数组的类型 | +| getAllDeclaredFields | 获取全部 Field, 包括父类 | +| isInvalid | 是静态常量或者内部结构属性 | + + +* **文件 (IO 流 ) 工具类 ->** [FileIOUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/FileIOUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setBufferSize | 设置缓冲区的大小, 默认大小等于 8192 字节 | +| writeFileFromIS | 通过输入流写入文件 | +| writeFileFromBytesByStream | 通过字节流写入文件 | +| writeFileFromBytesByChannel | 通过 FileChannel 把字节流写入文件 | +| writeFileFromBytesByMap | 通过 MappedByteBuffer 把字节流写入文件 | +| writeFileFromString | 通过字符串写入文件 | +| readFileToList | 读取文件内容, 返回换行 List | +| readFileToString | 读取文件内容, 返回字符串 | +| readFileToBytesByStream | 读取文件内容, 返回 byte[] | +| readFileToBytesByChannel | 通过 FileChannel, 读取文件内容, 返回 byte[] | +| readFileToBytesByMap | 通过 MappedByteBuffer, 读取文件内容, 返回 byte[] | + + +* **文件操作工具类 ->** [FileUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/FileUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getFile | 获取文件 | +| getFileByPath | 获取文件 | +| getFileCreateFolder | 获取路径, 并且进行创建目录 | +| getFilePathCreateFolder | 获取路径, 并且进行创建目录 | +| createFolder | 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) | +| createFolderByPath | 创建文件夹目录 - 可以传入文件名 | +| createFolderByPaths | 创建多个文件夹, 如果不存在则创建 | +| createOrExistsDir | 判断目录是否存在, 不存在则判断是否创建成功 | +| createOrExistsFile | 判断文件是否存在, 不存在则判断是否创建成功 | +| createFileByDeleteOldFile | 判断文件是否存在, 存在则在创建之前删除 | +| getPath | 获取文件路径 | +| getAbsolutePath | 获取文件绝对路径 | +| getName | 获取文件名 | +| getFileSuffix | 获取文件后缀名 ( 无 "." 单独后缀 ) | +| getFileNotSuffix | 获取文件名 ( 无后缀 ) | +| getFileNotSuffixToPath | 获取文件名 ( 无后缀 ) | +| getFileNameNoExtension | 获取路径中的不带拓展名的文件名 | +| getFileExtension | 获取路径中的文件拓展名 | +| isFileExists | 检查是否存在某个文件 | +| isFile | 判断是否文件 | +| isDirectory | 判断是否文件夹 | +| isHidden | 判断是否隐藏文件 | +| getFileLastModified | 获取文件最后修改的毫秒时间戳 | +| getFileCharsetSimple | 获取文件编码格式 | +| getFileLines | 获取文件行数 | +| getFileSize | 获取文件大小 | +| getDirSize | 获取目录大小 | +| getFileLength | 获取文件大小 | +| getDirLength | 获取目录全部文件大小 | +| getFileLengthNetwork | 获取文件大小 - 网络资源 | +| getFileName | 获取路径中的文件名 | +| getDirName | 获取路径中的最长目录地址 | +| rename | 重命名文件 - 同个目录下, 修改文件名 | +| formatFileSize | 传入文件路径, 返回对应的文件大小 | +| formatByteMemorySize | 字节数转合适内存大小 保留 3 位小数 (%.位数f) | +| deleteFile | 删除文件 | +| deleteFiles | 删除多个文件 | +| deleteFolder | 删除文件夹 | +| saveFile | 保存文件 | +| appendFile | 追加文件 ( 使用 FileWriter) | +| readFileBytes | 读取文件 | +| readFile | 读取文件 | +| copyFile | 复制单个文件 | +| copyFolder | 复制文件夹 | +| moveFile | 移动 ( 剪切 ) 文件 | +| moveFolder | 移动 ( 剪切 ) 文件夹 | +| copyOrMoveDir | 复制或移动目录 | +| copyOrMoveFile | 复制或移动文件 | +| copyDir | 复制目录 | +| moveDir | 移动目录 | +| deleteDir | 删除目录 | +| deleteAllInDir | 删除目录下所有东西 | +| deleteFilesInDir | 删除目录下所有文件 | +| deleteFilesInDirWithFilter | 删除目录下所有过滤的文件 | +| listFilesInDir | 获取目录下所有文件 - 不递归进子目录 | +| listFilesInDirWithFilter | 获取目录下所有过滤的文件 - 不递归进子目录 | +| getFileMD5 | 获取文件 MD5 值 | +| getFileMD5ToHexString | 获取文件 MD5 值 | + + +* **Http 参数工具类 ->** [HttpParamsUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/HttpParamsUtils.java) + +| 方法 | 注释 | +| :- | :- | +| splitParams | 拆分参数 | +| joinParams | 拼接请求参数 - value(String) | +| joinParamsObj | 拼接请求参数 - value(Object) | +| toConvertObjToMS | 进行转换对象处理 ( 请求发送对象 ) | +| toConvertObjToMO | 进行转换对象处理 ( 请求发送对象 ) | +| toUrlEncode | 进行 URL 编码, 默认 UTF-8 | + + +* **HttpURLConnection 网络工具类 ->** [HttpURLConnectionUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/HttpURLConnectionUtils.java) + +| 方法 | 注释 | +| :- | :- | +| doGetAsyn | 异步的 Get 请求 | +| doPostAsyn | 异步的 Post 请求 | +| request | 发送请求 | +| getNetTime | 获取网络时间 - 默认使用百度链接 | + + +* **Map 工具类 ->** [MapUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/MapUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断 Map 是否为 null | +| isNotEmpty | 判断 Map 是否不为 null | +| length | 获取 Map 长度 | +| isLength | 获取长度 to Map 是否等于期望长度 | +| greaterThan | 判断 Map 长度是否大于指定长度 | +| greaterThanOrEqual | 判断 Map 长度是否大于等于指定长度 | +| lessThan | 判断 Map 长度是否小于指定长度 | +| lessThanOrEqual | 判断 Map 长度是否小于等于指定长度 | +| get | 获取 value | +| getKeyByValue | 通过 value 获取 key | +| getKeysByValue | 通过 value 获取 key 集合 ( 返回等于 value 的 key 集合 ) | +| getKeys | 通过 Map 获取 key 集合 | +| getKeysToArrays | 通过 Map 获取 key 数组 | +| getValues | 通过 Map 获取 value 集合 | +| getValuesToArrays | 通过 Map 获取 value 数组 | +| getFirst | 获取第一条数据 | +| getLast | 获取最后一条数据 | +| getNext | 根据指定 key 获取 key 所在位置的下一条数据 | +| getPrevious | 根据指定 key 获取 key 所在位置的上一条数据 | +| put | 添加一条数据 | +| putNotNull | 添加一条数据, 不允许 key 为 null | +| putAll | 添加多条数据 | +| putAllNotNull | 添加多条数据, 不允许 key 为 null | +| remove | 移除一条数据 | +| removeToKeys | 移除多条数据 | +| removeToValue | 移除等于 value 的所有数据 | +| removeToValues | 移除等于 value 的所有数据 (Collection) | +| equals | 判断两个值是否一样 | +| toggle | 切换保存状态 | +| isNullToValue | 判断指定 key 的 value 是否为 null | +| containsKey | 判断 Map 是否存储了 key | +| containsValue | 判断 Map 是否存储了 value | +| putToList | 添加一条数据 - (Value) List | +| removeToList | 移除一条数据 - (Value) List | +| removeToLists | 移除多条数据 - (Value) List | +| removeToMap | 移除多条数据 - 通过 Map 进行移除 | + + +* **数字 ( 计算 ) 工具类 ->** [NumberUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/NumberUtils.java) + +| 方法 | 注释 | +| :- | :- | +| percentI | 计算百分比值 ( 最大 100%) | +| percentD | 计算百分比值 ( 最大 100%) | +| percentL | 计算百分比值 ( 最大 100%) | +| percentF | 计算百分比值 ( 最大 100%) | +| percentI2 | 计算百分比值 ( 可超出 100%) | +| percentD2 | 计算百分比值 ( 可超出 100%) | +| percentL2 | 计算百分比值 ( 可超出 100%) | +| percentF2 | 计算百分比值 ( 可超出 100%) | +| clamp | 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max | +| getMultiple | 获取倍数 ( 自动补 1) | +| getMultipleI | 获取倍数 | +| getMultipleD | 获取倍数 | +| getMultipleL | 获取倍数 | +| getMultipleF | 获取倍数 | +| numberToCHN | 数字转中文数值 | +| isNumber | 检验数字 | +| isNumberDecimal | 检验数字或包含小数点 | + + +* **对象相关工具类 ->** [ObjectUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/ObjectUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断对象是否为空 | +| isNotEmpty | 判断对象是否非空 | +| equals | 判断两个值是否一样 | +| requireNonNull | 检查对象是否为 null, 为 null 则抛出异常, 不为 null 则返回该对象 | +| getOrDefault | 获取非空或默认对象 | +| hashCode | 获取对象哈希值 | +| getObjectTag | 获取一个对象的独一无二的标记 | +| converObj | 获取转换对象 | + + +* **随机工具类 ->** [RandomUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/RandomUtils.java) + +| 方法 | 注释 | +| :- | :- | +| nextBoolean | 获取伪随机 boolean 值 | +| nextBytes | 获取伪随机 byte[] | +| nextDouble | 获取伪随机 double 值 | +| nextGaussian | 获取伪随机高斯分布值 | +| nextFloat | 获取伪随机 float 值 | +| nextInt | 获取伪随机 int 值 | +| nextLong | 获取伪随机 long 值 | +| getRandomNumbers | 获取数字自定义长度的随机数 | +| getRandomLowerCaseLetters | 获取小写字母自定义长度的随机数 | +| getRandomCapitalLetters | 获取大写字母自定义长度的随机数 | +| getRandomLetters | 获取大小写字母自定义长度的随机数 | +| getRandomNumbersAndLetters | 获取数字、大小写字母自定义长度的随机数 | +| getRandom | 获取自定义数据自定义长度的随机数 | +| shuffle | 洗牌算法 ( 第一种 ), 随机置换指定的数组使用的默认源的随机性 ( 随机数据源小于三个, 则无效 ) | +| shuffle2 | 洗牌算法 ( 第二种 ), 随机置换指定的数组使用的默认源的随机性 | +| nextIntRange | 获取指定范围 int 值 | +| nextLongRange | 获取指定范围 long 值 | +| nextDoubleRange | 获取指定范围 double 值 | +| ints | 获取随机 int[] | +| longs | 获取随机 long[] | +| doubles | 获取随机 double[] | + + +* **反射相关工具类 ->** [Reflect2Utils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/Reflect2Utils.java) + +| 方法 | 注释 | +| :- | :- | +| setProperty | 设置某个对象变量值 ( 可设置静态变量 ) | +| getProperty | 获取某个对象的变量 ( 可获取静态变量 ) | +| getStaticProperty | 获取某个类的静态变量 ( 只能获取静态变量 ) | +| invokeMethod | 执行某个对象方法 ( 可执行静态方法 ) | +| invokeStaticMethod | 执行某个类的静态方法 ( 只能执行静态方法 ) | +| newInstance | 新建实例 ( 构造函数创建 ) | +| isInstance | 是不是某个类的实例 | +| getArgsClass | 获取参数类型 | +| getPropertyByParent | 获取父类中的变量对象 | +| getDeclaredFieldParent | 获取父类中的变量对象 ( 循环向上转型, 获取对象的 DeclaredField) | + + +* **反射相关工具类 ->** [ReflectUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/ReflectUtils.java) + +| 方法 | 注释 | +| :- | :- | +| reflect | 设置要反射的类 | +| newInstance | 实例化反射对象 | +| field | 设置反射的字段 | +| setEnumVal | 设置枚举值 | +| method | 设置反射的方法 | +| proxy | 根据类, 代理创建并返回对象 | +| type | 获取类型 | +| get | 获取反射想要获取的 | +| hashCode | 获取 HashCode | +| equals | 判断反射的两个对象是否一样 | +| toString | 获取反射获取的对象 | + + +* **计算比例工具类 ->** [ScaleUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/ScaleUtils.java) + +| 方法 | 注释 | +| :- | :- | +| calcScaleToWidth | 计算缩放比例 - 根据宽度比例转换高度 | +| calcScaleToHeight | 计算缩放比例 - 根据高度比例转换宽度 | +| calcWidthHeightToScale | 通过宽度、高度根据对应的比例, 转换成对应的比例宽度高度 - 智能转换 | +| calcWidthToScale | 以宽度为基准, 转换对应比例的高度 | +| calcHeightToScale | 以高度为基准, 转换对应比例的宽度 | + + +* **流操作工具类 ->** [StreamUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/StreamUtils.java) + +| 方法 | 注释 | +| :- | :- | +| inputToOutputStream | 输入流转输出流 | +| outputToInputStream | 输出流转输入流 | +| inputStreamToBytes | 输入流转 byte[] | +| bytesToInputStream | byte[] 转输出流 | +| outputStreamToBytes | 输出流转 byte[] | +| bytesToOutputStream | byte[] 转 输出流 | +| inputStreamToString | 输入流转 String | +| stringToInputStream | String 转换输入流 | +| outputStreamToString | 输出流转 String | +| stringToOutputStream | String 转 输出流 | + + +* **字符串工具类 ->** [StringUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/StringUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断字符串是否为 null | +| isNotEmpty | 判断字符串是否不为 null | +| length | 获取字符串长度 | +| isLength | 获取字符串长度 是否等于期望长度 | +| equals | 判断两个值是否一样 | +| isEquals | 判断多个字符串是否相等, 只有全相等才返回 true - 对比大小写 | +| isOrEquals | 判断多个字符串, 只要有一个符合条件则通过 | +| countMatches | 统计字符串匹配个数 | +| countMatches2 | 统计字符串匹配个数 | +| isContains | 判断一堆值中, 是否存在符合该条件的 ( 包含 ) | +| isStartsWith | 判断内容, 是否属于特定字符串开头 - 对比大小写 | +| isEndsWith | 判断内容, 是否属于特定字符串结尾 - 对比大小写 | +| isSpace | 判断字符串是否为 null 或全为空白字符 | +| toClearSpace | 清空字符串全部空格 | +| toClearSpaceTrim | 清空字符串前后所有空格 | +| appendSpace | 追加空格 | +| appendTab | 追加 Tab | +| appendLine | 追加换行 | +| colonSplit | 冒号分割处理 | +| toCheckValue | 检查字符串 | +| toCheckValues | 检查字符串 - 多个值 | +| toCheckValuesSpace | 检查字符串 - 多个值 ( 删除前后空格对比判断 ) | +| getFormatString | 获取格式化后的字符串 | +| getAutoFormatString | 获取自动数量格式化后的字符串 ( 可变参数 ) | +| getAutoFormatString2 | 获取自动数量格式化后的字符串 ( 可变参数 ) | +| appends | StringBuilder 拼接处理 | +| toGBKEncode | 字符串进行 GBK 编码 | +| toGBK2312Encode | 字符串进行 GBK2312 编码 | +| toUTF8Encode | 字符串进行 UTF-8 编码 | +| toStrEncode | 进行字符串编码 | +| toUrlEncode | 进行 URL 编码, 默认 UTF-8 | +| toUrlDecode | 进行 URL 解码, 默认 UTF-8 | +| toASCII | 将字符串转移为 ASCII 码 | +| toUnicode | 将字符串转移为 Unicode 码 | +| toUnicodeString | 将字符数组转移为 Unicode 码 | +| toDBC | 转化为半角字符 | +| toSBC | 转化为全角字符 如: a = a, A = A | +| checkChineseToString | 检测字符串是否全是中文 | +| isChinese | 判定输入汉字 | +| upperFirstLetter | 首字母大写 | +| lowerFirstLetter | 首字母小写 | +| reverse | 反转字符串 | +| concat | 字符串连接, 将参数列表拼接为一个字符串 | +| concatSpiltWith | 字符串连接, 将参数列表拼接为一个字符串 | +| underScoreCaseToCamelCase | 下划线命名转为驼峰命名 | +| camelCaseToUnderScoreCase | 驼峰命名法转为下划线命名 | +| sqliteEscape | 字符串数据库字符转义 | + + +* **异常处理工具类 ->** [ThrowableUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/ThrowableUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getThrowable | 获取异常信息 | +| getThrowableStackTrace | 获取异常栈信息 | + + +* **压缩相关工具类 ->** [ZipUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/ZipUtils.java) + +| 方法 | 注释 | +| :- | :- | +| zipFiles | 批量压缩文件 | +| zipFile | 压缩文件 | +| unzipFile | 解压文件 | +| unzipFileByKeyword | 解压带有关键字的文件 | +| getFilesPath | 获取压缩文件中的文件路径链表 | +| getComments | 获取压缩文件中的注释链表 | + + +## **`dev.utils.common.assist`** + + +* **均值计算 ( 用以统计平均数 ) 辅助类 ->** [Averager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/assist/Averager.java) + +| 方法 | 注释 | +| :- | :- | +| add | 添加一个数字 | +| clear | 清除全部 | +| size | 获取参与均值计算的数字个数 | +| getAverage | 获取平均数 | +| print | 输出参与均值计算的数字 | + + +* **时间均值计算辅助类 ->** [TimeAverager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/assist/TimeAverager.java) + +| 方法 | 注释 | +| :- | :- | +| start | 开始计时 ( 毫秒 ) | +| end | 结束计时 ( 毫秒 ) | +| endAndRestart | 结束计时, 并重新启动新的计时 | +| average | 求全部计时均值 | +| print | 输出全部时间值 | +| clear | 清除计时数据 | + + +* **时间计时辅助类 ->** [TimeCounter.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/assist/TimeCounter.java) + +| 方法 | 注释 | +| :- | :- | +| start | 开始计时 ( 毫秒 ) | +| durationRestart | 获取持续的时间并重新启动 ( 毫秒 ) | +| duration | 获取持续的时间 ( 毫秒 ) | +| getStartTime | 获取开始时间 ( 毫秒 ) | + + +* **堵塞时间辅助类 ->** [TimeKeeper.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/assist/TimeKeeper.java) + +| 方法 | 注释 | +| :- | :- | +| waitForEndAsyn | 设置等待一段时间后, 通知方法 ( 异步 ) | +| waitForEnd | 设置等待一段时间后, 通知方法 ( 同步 ) | + + +## **`dev.utils.common.assist.search`** + + +* **文件广度优先搜索算法 ( 多线程 + 队列, 搜索某个目录下的全部文件 ) ->** [FileBreadthFirstSearchUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setSearchHandler | 设置搜索处理接口 | +| getQueueSameTimeNumber | 获取任务队列同时进行数量 | +| setQueueSameTimeNumber | 任务队列同时进行数量 | +| isRunning | 是否搜索中 | +| stop | 停止搜索 | +| isStop | 是否停止搜索 | +| getStartTime | 获取开始搜索时间 ( 毫秒 ) | +| getEndTime | 获取结束搜索时间 ( 毫秒 ) | +| getDelayTime | 获取延迟校验时间 ( 毫秒 ) | +| setDelayTime | 设置延迟校验时间 ( 毫秒 ) | +| query | 搜索目录 | + + +* **文件深度优先搜索算法 ( 递归搜索某个目录下的全部文件 ) ->** [FileDepthFirstSearchUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setSearchHandler | 设置搜索处理接口 | +| isRunning | 是否搜索中 | +| stop | 停止搜索 | +| isStop | 是否停止搜索 | +| getStartTime | 获取开始搜索时间 ( 毫秒 ) | +| getEndTime | 获取结束搜索时间 ( 毫秒 ) | +| query | 搜索目录 | + + +## **`dev.utils.common.cipher`** + + +* **Base64 工具类 ->** [Base64.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/cipher/Base64.java) + +| 方法 | 注释 | +| :- | :- | +| decode | Decode the Base64-encoded data in input and return the data in | +| encodeToString | Base64-encode the given data and return a newly allocated | +| encode | Base64-encode the given data and return a newly allocated | + + +* **Base64 编解码 ( 并进行 ) 加解密 ->** [Base64Cipher.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/cipher/Base64Cipher.java) + +| 方法 | 注释 | +| :- | :- | +| decrypt | 解码 | +| encrypt | 编码 | + + +* **加密工具类 ->** [CipherUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/cipher/CipherUtils.java) + +| 方法 | 注释 | +| :- | :- | +| encrypt | 加密方法 | +| decrypt | 解密方法 | + + +## **`dev.utils.common.encrypt`** + + +* **AES 对称加密工具类 ->** [AESUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/encrypt/AESUtils.java) + +| 方法 | 注释 | +| :- | :- | +| initKey | 生成密钥 | +| encrypt | AES 加密 | +| decrypt | AES 解密 | + + +* **CRC 工具类 ->** [CRCUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/encrypt/CRCUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getCRC32 | 获取 CRC32 值 | +| getCRC32ToHexString | 获取 CRC32 值 | +| getFileCRC32 | 获取文件 CRC32 值 | + + +* **DES 对称加密工具类 ->** [DESUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/encrypt/DESUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getDESKey | 获取可逆算法 DES 的密钥 | +| encrypt | DES 加密 | +| decrypt | DES 解密 | + + +* **加解密通用工具类 ->** [EncryptUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/encrypt/EncryptUtils.java) + +| 方法 | 注释 | +| :- | :- | +| encryptMD2 | MD2 加密 | +| encryptMD2ToHexString | MD2 加密 | +| encryptMD5 | MD5 加密 | +| encryptMD5ToHexString | MD5 加密 | +| encryptMD5File | 获取文件 MD5 值 | +| encryptMD5FileToHexString | 获取文件 MD5 值 | +| encryptSHA1 | SHA1 加密 | +| encryptSHA1ToHexString | SHA1 加密 | +| encryptSHA224 | SHA224 加密 | +| encryptSHA224ToHexString | SHA224 加密 | +| encryptSHA256 | SHA256 加密 | +| encryptSHA256ToHexString | SHA256 加密 | +| encryptSHA384 | SHA384 加密 | +| encryptSHA384ToHexString | SHA384 加密 | +| encryptSHA512 | SHA512 加密 | +| encryptSHA512ToHexString | SHA512 加密 | +| encryptHmacMD5 | HmacMD5 加密 | +| encryptHmacMD5ToHexString | HmacMD5 加密 | +| encryptHmacSHA1 | HmacSHA1 加密 | +| encryptHmacSHA1ToHexString | HmacSHA1 加密 | +| encryptHmacSHA224 | HmacSHA224 加密 | +| encryptHmacSHA224ToHexString | HmacSHA224 加密 | +| encryptHmacSHA256 | HmacSHA256 加密 | +| encryptHmacSHA256ToHexString | HmacSHA256 加密 | +| encryptHmacSHA384 | HmacSHA384 加密 | +| encryptHmacSHA384ToHexString | HmacSHA384 加密 | +| encryptHmacSHA512 | HmacSHA512 加密 | +| encryptHmacSHA512ToHexString | HmacSHA512 加密 | +| encryptDES | DES 加密 | +| encryptDESToBase64 | DES 加密 | +| encryptDESToHexString | DES 加密 | +| decryptDES | DES 解密 | +| decryptDESToBase64 | DES 解密 | +| decryptDESToHexString | DES 解密 | +| encrypt3DES | 3DES 加密 | +| encrypt3DESToBase64 | 3DES 加密 | +| encrypt3DESToHexString | 3DES 加密 | +| decrypt3DES | 3DES 解密 | +| decrypt3DESToBase64 | 3DES 解密 | +| decrypt3DESToHexString | 3DES 解密 | +| encryptAES | AES 加密 | +| encryptAESToBase64 | AES 加密 | +| encryptAESToHexString | AES 加密 | +| decryptAES | AES 解密 | +| decryptAESToBase64 | AES 解密 | +| decryptAESToHexString | AES 解密 | +| encryptRSA | RSA 加密 | +| encryptRSAToBase64 | RSA 加密 | +| encryptRSAToHexString | RSA 加密 | +| decryptRSA | RSA 解密 | +| decryptRSAToBase64 | RSA 解密 | +| decryptRSAToHexString | RSA 解密 | + + +* **字符串 ( 编解码 ) 工具类 ->** [EscapeUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/encrypt/EscapeUtils.java) + +| 方法 | 注释 | +| :- | :- | +| escape | 编码 | +| unescape | 解码 - 本方法不论参数 data 是否经过 escape() 编码, 均能获取正确的 ( 解码 ) 结果 | + + +* **MD5 加密工具类 ->** [MD5Utils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/encrypt/MD5Utils.java) + +| 方法 | 注释 | +| :- | :- | +| md5 | 加密内容 (32 位小写 MD5) | +| md5Upper | 加密内容 (32 位大写 MD5) | +| getFileMD5 | 获取文件 MD5 值 | +| getFileMD5ToHexString | 获取文件 MD5 值 | + + +* **SHA 加密工具类 ->** [SHAUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/encrypt/SHAUtils.java) + +| 方法 | 注释 | +| :- | :- | +| sha1 | 加密内容 SHA1 | +| sha224 | 加密内容 SHA224 | +| sha256 | 加密内容 SHA256 | +| sha384 | 加密内容 SHA384 | +| sha512 | 加密内容 SHA512 | +| getFileSHA1 | 获取文件 SHA1 值 | +| getFileSHA256 | 获取文件 SHA256 值 | +| shaHex | 加密内容 SHA 模板 | +| getFileSHA | 获取文件 SHA 值 | + + +* **3DES 对称加密工具类 ->** [TripleDESUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java) + +| 方法 | 注释 | +| :- | :- | +| initKey | 生成密钥 | +| encrypt | 3DES 加密 | +| decrypt | 3DES 解密 | + + +* **异或 ( 加密 ) 工具类 ->** [XorUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/encrypt/XorUtils.java) + +| 方法 | 注释 | +| :- | :- | +| encryptAsFix | 加解密 ( 固定 Key 方式 ) 这种方式 加解密 方法共用 | +| encrypt | 加密 ( 非固定 Key 方式 ) | +| decrypt | 解密 ( 非固定 Key 方式 ) | + + +## **`dev.utils.common.random`** + + +* **随机概率采样算法 ->** [AliasMethod.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/random/AliasMethod.java) + +| 方法 | 注释 | +| :- | :- | +| next | 获取随机索引 ( 对应几率索引 ) | + + +## **`dev.utils.common.thread`** + + +* **线程池管理工具类 ->** [DevThreadManager.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/thread/DevThreadManager.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 DevThreadManager 实例 | +| initConfig | 初始化配置信息 | +| putConfig | 添加配置信息 | +| removeConfig | 移除配置信息 | + + +* **线程池 ( 构建类 ) ->** [DevThreadPool.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/thread/DevThreadPool.java) + +| 方法 | 注释 | +| :- | :- | +| getThreads | 获取线程数 | +| getCalcThreads | 获取线程数 | +| execute | 加入到线程池任务队列 | +| shutdown | shutdown 会等待所有提交的任务执行完成, 不管是正在执行还是保存在任务队列中的已提交任务 | +| shutdownNow | shutdownNow 会尝试中断正在执行的任务 ( 其主要是中断一些指定方法如 sleep 方法 ), 并且停止执行等待队列中提交的任务 | +| isShutdown | 判断线程池是否已关闭 - isShutDown 当调用 shutdown() 方法后返回为 true | +| isTerminated | 若关闭后所有任务都已完成, 则返回 true | +| awaitTermination | 请求关闭、发生超时或者当前线程中断 | +| submit | 提交一个 Callable 任务用于执行 | +| invokeAll | 执行给定的任务 | +| invokeAny | 执行给定的任务 | +| schedule | 延迟执行 Runnable 命令 | +| scheduleWithFixedRate | 延迟并循环执行命令 | +| scheduleWithFixedDelay | 延迟并以固定休息时间循环执行命令 | + + +## **`dev.utils.common.validator`** + + +* **银行卡管理工具类 ->** [BankCheckUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/validator/BankCheckUtils.java) + +| 方法 | 注释 | +| :- | :- | +| checkBankCard | 校验银行卡卡号是否合法 | +| getBankCardCheckCode | 从不含校验位的银行卡卡号采用 Luhm 校验算法获取校验位 | +| getNameOfBank | 通过银行卡的 前六位确定 判断银行开户行及卡种 | + + +* **居民身份证工具类 ->** [IDCardUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/validator/IDCardUtils.java) + +| 方法 | 注释 | +| :- | :- | +| validateIdCard15 | 身份证校验规则, 验证 15 位身份编码是否合法 | +| validateIdCard18 | 身份证校验规则, 验证 18 位身份编码是否合法 | +| convert15CardTo18 | 将 15 位身份证号码转换为 18 位 | +| validateTWCard | 验证台湾身份证号码 | +| validateHKCard | 验证香港身份证号码 ( 部份特殊身份证无法检查 ) | +| validateIdCard10 | 判断 10 位数的身份证号, 是否合法 | +| validateCard | 验证身份证是否合法 | +| getAgeByIdCard | 根据身份编号获取年龄 | +| getBirthByIdCard | 根据身份编号获取生日 | +| getBirthdayByIdCard | 根据身份编号获取生日 | +| getYearByIdCard | 根据身份编号获取生日 ( 年份 ) | +| getMonthByIdCard | 根据身份编号获取生日 ( 月份 ) | +| getDateByIdCard | 根据身份编号获取生日 ( 天数 ) | +| getGenderByIdCard | 根据身份编号获取性别 | +| getProvinceByIdCard | 根据身份编号获取户籍省份 | +| getPowerSum | 将身份证的每位和对应位的加权因子相乘之后, 再获取和值 | +| getCheckCode18 | 将 POWER 和值与 11 取模获取余数进行校验码判断 | + + +* **校验工具类 ->** [ValidatorUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/validator/ValidatorUtils.java) + +| 方法 | 注释 | +| :- | :- | +| match | 通用匹配函数 | +| isNumber | 检验数字 | +| isNumberDecimal | 检验数字或包含小数点 | +| isLetter | 判断字符串是不是全是字母 | +| isContainNumber | 判断字符串是不是包含数字 | +| isNumberLetter | 判断字符串是不是只含字母和数字 | +| isSpec | 检验特殊符号 | +| isWx | 检验微信号 | +| isRealName | 检验真实姓名 | +| isNickName | 校验昵称 | +| isUserName | 校验用户名 | +| isPassword | 校验密码 | +| isEmail | 校验邮箱 | +| isUrl | 校验 URL | +| isIPAddress | 校验 IP 地址 | +| isChinese | 校验汉字 ( 无符号, 纯汉字 ) | +| isChineseAll | 判断字符串是不是全是中文 | +| isContainChinese | 判断字符串中包含中文、包括中文字符标点等 | + + +* **检验联系 ( 手机号, 座机 ) 工具类 ->** [ValiToPhoneUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevJava/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isPhoneCheck | 中国手机号格式验证, 在输入可以调用该方法, 点击发送验证码, 使用 isPhone | +| isPhone | 是否中国手机号 | +| isPhoneToChinaTelecom | 是否中国电信手机号码 | +| isPhoneToChinaUnicom | 是否中国联通手机号码 | +| isPhoneToChinaMobile | 是否中国移动手机号码 | +| isPhoneToHkMobile | 判断是否香港手机号 | +| isPhoneCallNum | 验证电话号码的格式 | \ No newline at end of file diff --git a/lib/DevJava/build.gradle b/lib/DevJava/build.gradle new file mode 100644 index 0000000000..5b429d95e3 --- /dev/null +++ b/lib/DevJava/build.gradle @@ -0,0 +1,12 @@ +apply { + from rootProject.file(files.java_lib_gradle) +} + +version versions.dev_java_version + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) +} + +// gradlew bintrayUpload +//apply from : rootProject.file(files.java_bintray_upload) \ No newline at end of file diff --git a/lib/DevJava/project.properties b/lib/DevJava/project.properties new file mode 100644 index 0000000000..c919d140ab --- /dev/null +++ b/lib/DevJava/project.properties @@ -0,0 +1,10 @@ +#project +project.name=DevJava +project.groupId=com.afkt +project.artifactId=DevJava +project.packaging=jar +project.siteUrl=https://github.com/afkT/DevUtils +project.gitUrl=https://github.com/afkT/DevUtils.git + +#javadoc +javadoc.name=DevJava \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/JCLogUtils.java b/lib/DevJava/src/main/java/dev/utils/JCLogUtils.java new file mode 100644 index 0000000000..91dc60871e --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/JCLogUtils.java @@ -0,0 +1,276 @@ +package dev.utils; + +import java.io.StringReader; +import java.io.StringWriter; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * detail: Java Common 日志打印工具类 ( 简化版 ) - 项目内部使用 ( 主要打印 Java 日志 ) + * @author Ttt + */ +public final class JCLogUtils { + + private JCLogUtils() { + } + + // 是否打印日志 线上 (release) = false, 开发 (debug) = true + private static boolean JUDGE_PRINT_LOG = false; + // 判断是否控制台打印信息 + private static boolean JUDGE_CONTROL_PRINT_LOG = false; + // 默认 DEFAULT_TAG + private static final String DEFAULT_TAG = JCLogUtils.class.getSimpleName(); + + // ============ + // = 日志类型 = + // ============ + + // INFO 模式 + public static final int INFO = 0; + // DEBUG 模式 + public static final int DEBUG = 1; + // ERROR 模式 + public static final int ERROR = 2; + + /** + * 判断是否打印日志 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPrintLog() { + return JUDGE_PRINT_LOG; + } + + /** + * 设置是否打印日志 + * @param judgePrintLog 是否允许打印日志 + */ + public static void setPrintLog(final boolean judgePrintLog) { + JUDGE_PRINT_LOG = judgePrintLog; + } + + /** + * 设置是否在控制台打印日志 + * @param judgeControlPrintLog 是否允许控制台打印日志 + */ + public static void setControlPrintLog(final boolean judgeControlPrintLog) { + JUDGE_CONTROL_PRINT_LOG = judgeControlPrintLog; + } + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + // = + + /** + * 最终打印日志方法 ( 全部调用此方法 ) + * @param logType 日志类型 + * @param tag 打印 Tag + * @param message 日志信息 + */ + private static void printLog(final int logType, final String tag, final String message) { + if (JCLogUtils.print != null) { + JCLogUtils.print.printLog(logType, tag, message); + } + + if (JUDGE_CONTROL_PRINT_LOG) { + // 打印信息 + if (isEmpty(tag)) { + System.out.println(message); + } else { + System.out.println(tag + " : " + message); + } + } + } + + /** + * 处理信息 + * @param message 日志信息 + * @param args 占位符替换 + * @return 处理 ( 格式化 ) 后准备打印的日志信息 + */ + private static String createMessage(final String message, final Object... args) { + String result; + try { + if (message != null) { + if (args == null) { + // 动态参数为 null + result = "params is null"; + } else { + // 格式化字符串 + result = (args.length == 0 ? message : String.format(message, args)); + } + } else { + // 打印内容为 null + result = "message is null"; + } + } catch (Exception e) { + // 出现异常 + result = e.toString(); + } + return result; + } + + /** + * 拼接错误信息 + * @param throwable 错误异常 + * @param message 需要打印的消息 + * @param args 动态参数 + * @return 处理 ( 格式化 ) 后准备打印的日志信息 + */ + private static String splitErrorMessage(final Throwable throwable, final String message, final Object... args) { + String result; + try { + if (throwable != null) { + if (message != null) { + result = createMessage(message, args) + " : " + throwable.toString(); + } else { + result = throwable.toString(); + } + } else { + result = createMessage(message, args); + } + } catch (Exception e) { + result = e.toString(); + } + return result; + } + + // =============================== + // = 对外公开方法 - 使用默认 TAG = + // =============================== + + public static void d(final String message, final Object... args) { + dTag(DEFAULT_TAG, message, args); + } + + public static void e(final Throwable throwable) { + eTag(DEFAULT_TAG, throwable); + } + + public static void e(final String message, final Object... args) { + e(null, message, args); + } + + public static void e(final Throwable throwable, final String message, final Object... args) { + eTag(DEFAULT_TAG, throwable, message, args); + } + + public static void i(final String message, final Object... args) { + iTag(DEFAULT_TAG, message, args); + } + + public static void xml(final String xml) { + xmlTag(DEFAULT_TAG, xml); + } + + // =============================== + // = 对外公开方法 - 日志打印方法 = + // =============================== + + public static void dTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(DEBUG, tag, createMessage(message, args)); + } + } + + public static void eTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(ERROR, tag, createMessage(message, args)); + } + } + + public static void eTag(final String tag, final Throwable throwable) { + if (JUDGE_PRINT_LOG) { + printLog(ERROR, tag, splitErrorMessage(throwable, null)); + } + } + + public static void eTag(final String tag, final Throwable throwable, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(ERROR, tag, splitErrorMessage(throwable, message, args)); + } + } + + public static void iTag(final String tag, final String message, final Object... args) { + if (JUDGE_PRINT_LOG) { + printLog(INFO, tag, createMessage(message, args)); + } + } + + public static void xmlTag(final String tag, final String xml) { + if (JUDGE_PRINT_LOG) { + // 判断传入 XML 格式信息是否为 null + if (isEmpty(xml)) { + printLog(ERROR, tag, "Empty/Null xml content"); + return; + } + try { + Source xmlInput = new StreamSource(new StringReader(xml)); + StreamResult xmlOutput = new StreamResult(new StringWriter()); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.transform(xmlInput, xmlOutput); + // 获取打印消息 + String message = xmlOutput.getWriter().toString().replaceFirst(">", ">\n"); + // 打印信息 + printLog(DEBUG, tag, message); + } catch (Exception e) { + String errorInfo = "null"; + if (e != null) { + Throwable throwable = e.getCause(); + if (throwable != null) { + errorInfo = throwable.toString(); + } else { + try { + errorInfo = e.toString(); + } catch (Exception e1) { + errorInfo = e1.toString(); + } + } + } + printLog(ERROR, tag, errorInfo + "\n" + xml); + } + } + } + + // ============ + // = 通知输出 = + // ============ + + private static Print print; + + /** + * 设置日志输出接口 + * @param print 日志输出接口 + */ + public static void setPrint(final Print print) { + JCLogUtils.print = print; + } + + /** + * detail: 日志输出接口 + * @author Ttt + */ + public interface Print { + + /** + * 日志打印 + * @param logType 日志类型 + * @param tag 打印 Tag + * @param message 日志信息 + */ + void printLog(final int logType, final String tag, final String message); + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/ArrayUtils.java b/lib/DevJava/src/main/java/dev/utils/common/ArrayUtils.java new file mode 100644 index 0000000000..bab2ee8e9f --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/ArrayUtils.java @@ -0,0 +1,3088 @@ +package dev.utils.common; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: Array 数组工具类 + * @author Ttt + */ +public final class ArrayUtils { + + private ArrayUtils() { + } + + // 日志 TAG + private static final String TAG = ArrayUtils.class.getSimpleName(); + + // ========= + // = Array = + // ========= + + /** + * 判断数组是否为 null + * @param objects object[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Object[] objects) { + return objects == null || objects.length == 0; + } + + /** + * 判断数组是否为 null + * @param ints int[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final int[] ints) { + return ints == null || ints.length == 0; + } + + /** + * 判断数组是否为 null + * @param bytes byte[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final byte[] bytes) { + return bytes == null || bytes.length == 0; + } + + /** + * 判断数组是否为 null + * @param chars char[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final char[] chars) { + return chars == null || chars.length == 0; + } + + /** + * 判断数组是否为 null + * @param shorts short[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final short[] shorts) { + return shorts == null || shorts.length == 0; + } + + /** + * 判断数组是否为 null + * @param longs long[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final long[] longs) { + return longs == null || longs.length == 0; + } + + /** + * 判断数组是否为 null + * @param floats float[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final float[] floats) { + return floats == null || floats.length == 0; + } + + /** + * 判断数组是否为 null + * @param doubles double[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final double[] doubles) { + return doubles == null || doubles.length == 0; + } + + /** + * 判断数组是否为 null + * @param booleans boolean[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final boolean[] booleans) { + return booleans == null || booleans.length == 0; + } + + // = + + /** + * 判断数组是否不为 null + * @param objects object[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Object[] objects) { + return objects != null && objects.length != 0; + } + + /** + * 判断数组是否不为 null + * @param ints int[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final int[] ints) { + return ints != null && ints.length != 0; + } + + /** + * 判断数组是否不为 null + * @param bytes byte[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final byte[] bytes) { + return bytes != null && bytes.length != 0; + } + + /** + * 判断数组是否不为 null + * @param chars char[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final char[] chars) { + return chars != null && chars.length != 0; + } + + /** + * 判断数组是否不为 null + * @param shorts short[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final short[] shorts) { + return shorts != null && shorts.length != 0; + } + + /** + * 判断数组是否不为 null + * @param longs long[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final long[] longs) { + return longs != null && longs.length != 0; + } + + /** + * 判断数组是否不为 null + * @param floats float[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final float[] floats) { + return floats != null && floats.length != 0; + } + + /** + * 判断数组是否不为 null + * @param doubles double[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final double[] doubles) { + return doubles != null && doubles.length != 0; + } + + /** + * 判断数组是否不为 null + * @param booleans boolean[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final boolean[] booleans) { + return booleans != null && booleans.length != 0; + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取数组长度 + * @param objects object[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final Object[] objects) { + return length(objects, 0); + } + + /** + * 获取数组长度 + * @param ints int[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final int[] ints) { + return length(ints, 0); + } + + /** + * 获取数组长度 + * @param bytes byte[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final byte[] bytes) { + return length(bytes, 0); + } + + /** + * 获取数组长度 + * @param chars char[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final char[] chars) { + return length(chars, 0); + } + + /** + * 获取数组长度 + * @param shorts short[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final short[] shorts) { + return length(shorts, 0); + } + + /** + * 获取数组长度 + * @param longs long[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final long[] longs) { + return length(longs, 0); + } + + /** + * 获取数组长度 + * @param floats float[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final float[] floats) { + return length(floats, 0); + } + + /** + * 获取数组长度 + * @param doubles double[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final double[] doubles) { + return length(doubles, 0); + } + + /** + * 获取数组长度 + * @param booleans boolean[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final boolean[] booleans) { + return length(booleans, 0); + } + + // = + + /** + * 获取数组长度 + * @param objects object[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final Object[] objects, final int defaultLength) { + return objects != null ? objects.length : defaultLength; + } + + /** + * 获取数组长度 + * @param ints int[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final int[] ints, final int defaultLength) { + return ints != null ? ints.length : defaultLength; + } + + /** + * 获取数组长度 + * @param bytes byte[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final byte[] bytes, final int defaultLength) { + return bytes != null ? bytes.length : defaultLength; + } + + /** + * 获取数组长度 + * @param chars char[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final char[] chars, final int defaultLength) { + return chars != null ? chars.length : defaultLength; + } + + /** + * 获取数组长度 + * @param shorts short[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final short[] shorts, final int defaultLength) { + return shorts != null ? shorts.length : defaultLength; + } + + /** + * 获取数组长度 + * @param longs long[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final long[] longs, final int defaultLength) { + return longs != null ? longs.length : defaultLength; + } + + /** + * 获取数组长度 + * @param floats float[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final float[] floats, final int defaultLength) { + return floats != null ? floats.length : defaultLength; + } + + /** + * 获取数组长度 + * @param doubles double[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final double[] doubles, final int defaultLength) { + return doubles != null ? doubles.length : defaultLength; + } + + /** + * 获取数组长度 + * @param booleans boolean[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final boolean[] booleans, final int defaultLength) { + return booleans != null ? booleans.length : defaultLength; + } + + // = + + /** + * 判断数组长度是否等于期望长度 + * @param objects object[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Object[] objects, final int length) { + return objects != null && objects.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param ints int[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final int[] ints, final int length) { + return ints != null && ints.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param bytes byte[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final byte[] bytes, final int length) { + return bytes != null && bytes.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param chars char[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final char[] chars, final int length) { + return chars != null && chars.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param shorts short[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final short[] shorts, final int length) { + return shorts != null && shorts.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param longs long[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final long[] longs, final int length) { + return longs != null && longs.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param floats float[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final float[] floats, final int length) { + return floats != null && floats.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param doubles double[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final double[] doubles, final int length) { + return doubles != null && doubles.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param booleans boolean[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final boolean[] booleans, final int length) { + return booleans != null && booleans.length == length; + } + + // ============ + // = 数据获取 = + // ============ + + /** + * 获取数组对应索引数据 + * @param array 数组 + * @param pos 索引 + * @param 泛型 + * @return 数组指定索引的值 + */ + public static T get(final Object array, final int pos) { + if (array == null || pos < 0) return null; + try { + return (T) Array.get(array, pos); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getByArray"); + } + return null; + } + + /** + * 获取数组对应索引数据 + * @param array 数组 + * @param pos 索引 + * @param 泛型 + * @return 数组指定索引的值 + */ + public static T get(final T[] array, final int pos) { + return get(array, pos, null); + } + + /** + * 获取数组对应索引数据 + * @param ints int[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static int get(final int[] ints, final int pos) { + return get(ints, pos, -1); + } + + /** + * 获取数组对应索引数据 + * @param bytes byte[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static byte get(final byte[] bytes, final int pos) { + return get(bytes, pos, (byte) -1); + } + + /** + * 获取数组对应索引数据 + * @param chars char[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static char get(final char[] chars, final int pos) { + return get(chars, pos, (char) -1); + } + + /** + * 获取数组对应索引数据 + * @param shorts short[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static short get(final short[] shorts, final int pos) { + return get(shorts, pos, (short) -1); + } + + /** + * 获取数组对应索引数据 + * @param longs long[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static long get(final long[] longs, final int pos) { + return get(longs, pos, -1L); + } + + /** + * 获取数组对应索引数据 + * @param floats float[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static float get(final float[] floats, final int pos) { + return get(floats, pos, -1f); + } + + /** + * 获取数组对应索引数据 + * @param doubles double[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static double get(final double[] doubles, final int pos) { + return get(doubles, pos, -1d); + } + + /** + * 获取数组对应索引数据 + * @param booleans boolean[] + * @param pos 索引 + * @return 数组指定索引的值 + */ + public static boolean get(final boolean[] booleans, final int pos) { + return get(booleans, pos, false); + } + + // = + + /** + * 获取数组对应索引数据 + * @param array 数组 + * @param pos 索引 + * @param defaultValue 默认值 + * @param 泛型 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static T get(final T[] array, final int pos, final T defaultValue) { + if (array != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return array[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param ints int[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static int get(final int[] ints, final int pos, final int defaultValue) { + if (ints != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return ints[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param bytes byte[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static byte get(final byte[] bytes, final int pos, final byte defaultValue) { + if (bytes != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return bytes[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param chars char[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static char get(final char[] chars, final int pos, final char defaultValue) { + if (chars != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return chars[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param shorts short[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static short get(final short[] shorts, final int pos, final short defaultValue) { + if (shorts != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return shorts[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param longs long[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static long get(final long[] longs, final int pos, final long defaultValue) { + if (longs != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return longs[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param floats float[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static float get(final float[] floats, final int pos, final float defaultValue) { + if (floats != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return floats[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param doubles double[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static double get(final double[] doubles, final int pos, final double defaultValue) { + if (doubles != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return doubles[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + /** + * 获取数组对应索引数据 + * @param booleans boolean[] + * @param pos 索引 + * @param defaultValue 默认值 + * @return 数组指定索引的值, 如果获取失败, 则返回 defaultValue + */ + public static boolean get(final boolean[] booleans, final int pos, final boolean defaultValue) { + if (booleans != null) { + // 防止索引为负数 + if (pos < 0) { + return defaultValue; + } + try { + return booleans[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + return defaultValue; + } + + // = + + /** + * 获取数组第一条数据 + * @param array 数组 + * @param 泛型 + * @return 数组索引为 0 的值 + */ + public static T getFirst(final T[] array) { + return get(array, 0); + } + + /** + * 获取数组第一条数据 + * @param ints int[] + * @return 数组索引为 0 的值 + */ + public static int getFirst(final int[] ints) { + return get(ints, 0); + } + + /** + * 获取数组第一条数据 + * @param bytes byte[] + * @return 数组索引为 0 的值 + */ + public static byte getFirst(final byte[] bytes) { + return get(bytes, 0); + } + + /** + * 获取数组第一条数据 + * @param chars char[] + * @return 数组索引为 0 的值 + */ + public static char getFirst(final char[] chars) { + return get(chars, 0); + } + + /** + * 获取数组第一条数据 + * @param shorts short[] + * @return 数组索引为 0 的值 + */ + public static short getFirst(final short[] shorts) { + return get(shorts, 0); + } + + /** + * 获取数组第一条数据 + * @param longs long[] + * @return 数组索引为 0 的值 + */ + public static long getFirst(final long[] longs) { + return get(longs, 0); + } + + /** + * 获取数组第一条数据 + * @param floats float[] + * @return 数组索引为 0 的值 + */ + public static float getFirst(final float[] floats) { + return get(floats, 0); + } + + /** + * 获取数组第一条数据 + * @param doubles double[] + * @return 数组索引为 0 的值 + */ + public static double getFirst(final double[] doubles) { + return get(doubles, 0); + } + + /** + * 获取数组第一条数据 + * @param booleans boolean[] + * @return 数组索引为 0 的值 + */ + public static boolean getFirst(final boolean[] booleans) { + return get(booleans, 0); + } + + // = + + /** + * 获取数组最后一条数据 + * @param array 数组 + * @param 泛型 + * @return 数组索引 length - 1 的值 + */ + public static T getLast(final T[] array) { + return get(array, length(array) - 1); + } + + /** + * 获取数组最后一条数据 + * @param ints int[] + * @return 数组索引 length - 1 的值 + */ + public static int getLast(final int[] ints) { + return get(ints, length(ints) - 1); + } + + /** + * 获取数组最后一条数据 + * @param bytes byte[] + * @return 数组索引 length - 1 的值 + */ + public static byte getLast(final byte[] bytes) { + return get(bytes, length(bytes) - 1); + } + + /** + * 获取数组最后一条数据 + * @param chars char[] + * @return 数组索引 length - 1 的值 + */ + public static char getLast(final char[] chars) { + return get(chars, length(chars) - 1); + } + + /** + * 获取数组最后一条数据 + * @param shorts short[] + * @return 数组索引 length - 1 的值 + */ + public static short getLast(final short[] shorts) { + return get(shorts, length(shorts) - 1); + } + + /** + * 获取数组最后一条数据 + * @param longs long[] + * @return 数组索引 length - 1 的值 + */ + public static long getLast(final long[] longs) { + return get(longs, length(longs) - 1); + } + + /** + * 获取数组最后一条数据 + * @param floats float[] + * @return 数组索引 length - 1 的值 + */ + public static float getLast(final float[] floats) { + return get(floats, length(floats) - 1); + } + + /** + * 获取数组最后一条数据 + * @param doubles double[] + * @return 数组索引 length - 1 的值 + */ + public static double getLast(final double[] doubles) { + return get(doubles, length(doubles) - 1); + } + + /** + * 获取数组最后一条数据 + * @param booleans boolean[] + * @return 数组索引 length - 1 的值 + */ + public static boolean getLast(final boolean[] booleans) { + return get(booleans, length(booleans) - 1); + } + + // =================== + // = 数据获取 - 特殊 = + // =================== + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param offset 偏移量 + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final T[] array, final T value, final int number, final boolean notNull, final int offset) { + if (array != null) { + if (notNull && value == null) { + return null; + } + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + T t = array[i]; + // 判断是否一样 + if (equals(t, value)) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return null; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param offset 偏移量 + * @param 泛型 + * @return 对应索引的值 + */ + public static int getPosition(final T[] array, final T value, final int number, final boolean notNull, final int offset) { + if (array != null) { + if (notNull && value == null) { + return -1; + } + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + T t = array[i]; + // 判断是否一样 + if (equals(t, value)) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值 + * @param array 数组 + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final T[] array, final T value) { + return get(array, value, 0, false, 0); + } + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final T[] array, final T value, final int number) { + return get(array, value, number, false, 0); + } + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值 + * @param array 数组 + * @param value 值 + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final T[] array, final T value, final boolean notNull) { + return get(array, value, 0, notNull, 0); + } + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final T[] array, final T value, final int number, final boolean notNull) { + return get(array, value, number, notNull, 0); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值, 不允许值为 null + * @param array 数组 + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNotNull(final T[] array, final T value) { + return get(array, value, 0, true, 0); + } + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值, 不允许值为 null + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNotNull(final T[] array, final T value, final int number) { + return get(array, value, number, true, 0); + } + + // = + + /** + * 根据指定 value 获取索引 + * @param array 数组 + * @param value 值 + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final T[] array, final T value) { + return getPosition(array, value, 0, false, 0); + } + + /** + * 根据指定 value 获取索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final T[] array, final T value, final int number) { + return getPosition(array, value, number, false, 0); + } + + /** + * 根据指定 value 获取索引 + * @param array 数组 + * @param value 值 + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final T[] array, final T value, final boolean notNull) { + return getPosition(array, value, 0, notNull, 0); + } + + /** + * 根据指定 value 获取索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final T[] array, final T value, final int number, final boolean notNull) { + return getPosition(array, value, number, notNull, 0); + } + + // = + + /** + * 根据指定 value 获取索引, 不允许值为 null + * @param array 数组 + * @param value 值 + * @param 泛型 + * @return 对应的索引 + */ + public static int getPositionNotNull(final T[] array, final T value) { + return getPosition(array, value, 0, true, 0); + } + + /** + * 根据指定 value 获取索引, 不允许值为 null + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应的索引 + */ + public static int getPositionNotNull(final T[] array, final T value, final int number) { + return getPosition(array, value, number, true, 0); + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static int get(final int[] array, final int value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + int valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final int[] array, final int value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + int valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static byte get(final byte[] array, final byte value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + byte valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (byte) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final byte[] array, final byte value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + byte valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static char get(final char[] array, final char value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + char valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (char) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final char[] array, final char value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + char valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static short get(final short[] array, final short value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + short valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (short) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final short[] array, final short value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + short valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static long get(final long[] array, final long value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + long valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (long) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final long[] array, final long value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + long valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static float get(final float[] array, final float value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + float valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (float) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final float[] array, final float value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + float valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static double get(final double[] array, final double value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + double valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return (double) -1; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final double[] array, final double value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + double valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应索引的值 + */ + public static boolean get(final boolean[] array, final boolean value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + boolean valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return array[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return false; + } + + /** + * 根据指定值获取 value 所在位置 + 偏移量的索引 + * @param array 数组 + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param offset 偏移量 + * @return 对应的索引 + */ + public static int getPosition(final boolean[] array, final boolean value, final int number, final int offset) { + if (array != null) { + try { + // 保存当前临时次数 + int temp = number; + // 进行循环判断 + for (int i = 0, len = array.length; i < len; i++) { + boolean valueI = array[i]; + // 判断是否一样 + if (valueI == value) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // ============ + // = 转换处理 = + // ============ + + /** + * int[] 转换 Integer[] + * @param ints int[] + * @return {@link Integer[]} + */ + public static Integer[] intsToIntegers(final int[] ints) { + if (ints != null) { + // 获取数组长度 + int len = ints.length; + // 创建数组 + Integer[] array = new Integer[len]; + for (int i = 0; i < len; i++) { + array[i] = ints[i]; + } + return array; + } + return null; + } + + /** + * byte[] 转换 Byte[] + * @param bytes byte[] + * @return {@link Byte[]} + */ + public static Byte[] bytesToBytes(final byte[] bytes) { + if (bytes != null) { + // 获取数组长度 + int len = bytes.length; + // 创建数组 + Byte[] array = new Byte[len]; + for (int i = 0; i < len; i++) { + array[i] = bytes[i]; + } + return array; + } + return null; + } + + /** + * char[] 转换 Character[] + * @param chars char[] + * @return {@link Character[]} + */ + public static Character[] charsToCharacters(final char[] chars) { + if (chars != null) { + // 获取数组长度 + int len = chars.length; + // 创建数组 + Character[] array = new Character[len]; + for (int i = 0; i < len; i++) { + array[i] = chars[i]; + } + return array; + } + return null; + } + + /** + * short[] 转换 Short[] + * @param shorts short[] + * @return {@link Short[]} + */ + public static Short[] shortsToShorts(final short[] shorts) { + if (shorts != null) { + // 获取数组长度 + int len = shorts.length; + // 创建数组 + Short[] array = new Short[len]; + for (int i = 0; i < len; i++) { + array[i] = shorts[i]; + } + return array; + } + return null; + } + + /** + * long[] 转换 Long[] + * @param longs long[] + * @return {@link Long[]} + */ + public static Long[] longsToLongs(final long[] longs) { + if (longs != null) { + // 获取数组长度 + int len = longs.length; + // 创建数组 + Long[] array = new Long[len]; + for (int i = 0; i < len; i++) { + array[i] = longs[i]; + } + return array; + } + return null; + } + + /** + * float[] 转换 Float[] + * @param floats float[] + * @return {@link Float[]} + */ + public static Float[] floatsToFloats(final float[] floats) { + if (floats != null) { + // 获取数组长度 + int len = floats.length; + // 创建数组 + Float[] array = new Float[len]; + for (int i = 0; i < len; i++) { + array[i] = floats[i]; + } + return array; + } + return null; + } + + /** + * double[] 转换 Double[] + * @param doubles double[] + * @return {@link Double[]} + */ + public static Double[] doublesToDoubles(final double[] doubles) { + if (doubles != null) { + // 获取数组长度 + int len = doubles.length; + // 创建数组 + Double[] array = new Double[len]; + for (int i = 0; i < len; i++) { + array[i] = doubles[i]; + } + return array; + } + return null; + } + + /** + * boolean[] 转换 Boolean[] + * @param booleans boolean[] + * @return {@link Boolean[]} + */ + public static Boolean[] booleansToBooleans(final boolean[] booleans) { + if (booleans != null) { + // 获取数组长度 + int len = booleans.length; + // 创建数组 + Boolean[] array = new Boolean[len]; + for (int i = 0; i < len; i++) { + array[i] = booleans[i]; + } + return array; + } + return null; + } + + // = + + /** + * Integer[] 转换 int[] + * @param integers Integer[] + * @param defaultValue 转换失败使用得默认值 + * @return int[] + */ + public static int[] integersToInts(final Integer[] integers, final int defaultValue) { + if (integers != null) { + // 获取数组长度 + int len = integers.length; + // 创建数组 + int[] array = new int[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = integers[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Byte[] 转换 byte[] + * @param bytes Byte[] + * @param defaultValue 转换失败使用得默认值 + * @return byte[] + */ + public static byte[] bytesToBytes(final Byte[] bytes, final byte defaultValue) { + if (bytes != null) { + // 获取数组长度 + int len = bytes.length; + // 创建数组 + byte[] array = new byte[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = bytes[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Character[] 转换 char[] + * @param characters Character[] + * @param defaultValue 转换失败使用得默认值 + * @return char[] + */ + public static char[] charactersToChars(final Character[] characters, final char defaultValue) { + if (characters != null) { + // 获取数组长度 + int len = characters.length; + // 创建数组 + char[] array = new char[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = characters[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Short[] 转换 short[] + * @param shorts Short[] + * @param defaultValue 转换失败使用得默认值 + * @return short[] + */ + public static short[] shortsToShorts(final Short[] shorts, final short defaultValue) { + if (shorts != null) { + // 获取数组长度 + int len = shorts.length; + // 创建数组 + short[] array = new short[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = shorts[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Long[] 转换 long[] + * @param longs Long[] + * @param defaultValue 转换失败使用得默认值 + * @return long[] + */ + public static long[] longsToLongs(final Long[] longs, final long defaultValue) { + if (longs != null) { + // 获取数组长度 + int len = longs.length; + // 创建数组 + long[] array = new long[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = longs[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Float[] 转换 float[] + * @param floats Float[] + * @param defaultValue 转换失败使用得默认值 + * @return float[] + */ + public static float[] floatsToFloats(final Float[] floats, final float defaultValue) { + if (floats != null) { + // 获取数组长度 + int len = floats.length; + // 创建数组 + float[] array = new float[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = floats[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Double[] 转换 double[] + * @param doubles Double[] + * @param defaultValue 转换失败使用得默认值 + * @return double[] + */ + public static double[] doublesToDoubles(final Double[] doubles, final double defaultValue) { + if (doubles != null) { + // 获取数组长度 + int len = doubles.length; + // 创建数组 + double[] array = new double[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = doubles[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + /** + * Boolean[] 转换 boolean[] + * @param booleans Boolean[] + * @param defaultValue 转换失败使用得默认值 + * @return boolean[] + */ + public static boolean[] booleansToBooleans(final Boolean[] booleans, final boolean defaultValue) { + if (booleans != null) { + // 获取数组长度 + int len = booleans.length; + // 创建数组 + boolean[] array = new boolean[len]; + for (int i = 0; i < len; i++) { + try { + array[i] = booleans[i]; + } catch (Exception e) { + array[i] = defaultValue; + } + } + return array; + } + return null; + } + + // ============= + // = 转换 List = + // ============= + + /** + * 转换数组为集合 + * @param array 数组 + * @param 泛型 + * @return {@link List} + */ + public static List asList(final T[] array) { + if (array != null) { + try { + return new ArrayList<>(Arrays.asList(array)); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "asList"); + } + } + return null; + } + + // = + + /** + * 转换数组为集合 + * @param ints int[] + * @return {@link List} + */ + public static List asList(final int[] ints) { + if (ints != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = ints.length; i < len; i++) { + lists.add(ints[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param bytes byte[] + * @return {@link List} + */ + public static List asList(final byte[] bytes) { + if (bytes != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = bytes.length; i < len; i++) { + lists.add(bytes[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param chars char[] + * @return {@link List} + */ + public static List asList(final char[] chars) { + if (chars != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = chars.length; i < len; i++) { + lists.add(chars[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param shorts short[] + * @return {@link List} + */ + public static List asList(final short[] shorts) { + if (shorts != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = shorts.length; i < len; i++) { + lists.add(shorts[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param longs long[] + * @return {@link List} + */ + public static List asList(final long[] longs) { + if (longs != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = longs.length; i < len; i++) { + lists.add(longs[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param floats float[] + * @return {@link List} + */ + public static List asList(final float[] floats) { + if (floats != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = floats.length; i < len; i++) { + lists.add(floats[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param doubles double[] + * @return {@link List} + */ + public static List asList(final double[] doubles) { + if (doubles != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = doubles.length; i < len; i++) { + lists.add(doubles[i]); + } + return lists; + } + return null; + } + + /** + * 转换数组为集合 + * @param booleans boolean[] + * @return {@link List} + */ + public static List asList(final boolean[] booleans) { + if (booleans != null) { + List lists = new ArrayList<>(); + for (int i = 0, len = booleans.length; i < len; i++) { + lists.add(booleans[i]); + } + return lists; + } + return null; + } + + // ============ + // = 快捷判断 = + // ============ + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + // = + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @param 泛型 + * @return 拼接后的数组集合 + */ + public static T[] arraycopy(final T[] prefix, final T[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建集合 + List lists = new ArrayList<>(prefixLength + suffixLength); + // 进行判断处理 + if (prefixLength != 0) { + for (int i = 0; i < prefixLength; i++) { + lists.add(prefix[i]); + } + } + if (suffixLength != 0) { + for (int i = 0; i < suffixLength; i++) { + lists.add(suffix[i]); + } + } + if (prefix != null) { + return (T[]) Arrays.copyOf(lists.toArray(), lists.size(), prefix.getClass()); + } else { + return (T[]) Arrays.copyOf(lists.toArray(), lists.size(), suffix.getClass()); + } + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static int[] arraycopy(final int[] prefix, final int[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + int[] arrays = new int[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static byte[] arraycopy(final byte[] prefix, final byte[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + byte[] arrays = new byte[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static char[] arraycopy(final char[] prefix, final char[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + char[] arrays = new char[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static short[] arraycopy(final short[] prefix, final short[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + short[] arrays = new short[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static long[] arraycopy(final long[] prefix, final long[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + long[] arrays = new long[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static float[] arraycopy(final float[] prefix, final float[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + float[] arrays = new float[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static double[] arraycopy(final double[] prefix, final double[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + double[] arrays = new double[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + public static boolean[] arraycopy(final boolean[] prefix, final boolean[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + boolean[] arrays = new boolean[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + // = + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @param 泛型 + * @return 指定长度数组 + */ + public static T[] newarray(final int length, final T[] data) { + if (data != null && length > 0) { + List lists = new ArrayList<>(); + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + lists.add(data[i]); + } + } else { + for (int i = 0; i < dataLength; i++) { + lists.add(data[i]); + } + // 补充长度 + for (int i = 0, len = length - dataLength; i < len; i++) { + lists.add(null); + } + } + // 返回数据 + return (T[]) Arrays.copyOf(lists.toArray(), length, data.getClass()); + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static int[] newarray(final int length, final int[] data) { + if (data != null && length > 0) { + int[] arrays = new int[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static byte[] newarray(final int length, final byte[] data) { + if (data != null && length > 0) { + byte[] arrays = new byte[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static char[] newarray(final int length, final char[] data) { + if (data != null && length > 0) { + char[] arrays = new char[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static short[] newarray(final int length, final short[] data) { + if (data != null && length > 0) { + short[] arrays = new short[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static long[] newarray(final int length, final long[] data) { + if (data != null && length > 0) { + long[] arrays = new long[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static float[] newarray(final int length, final float[] data) { + if (data != null && length > 0) { + float[] arrays = new float[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static double[] newarray(final int length, final double[] data) { + if (data != null && length > 0) { + double[] arrays = new double[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + /** + * 创建指定长度数组 + * @param length 保留长度 + * @param data 待处理数组 + * @return 指定长度数组 + */ + public static boolean[] newarray(final int length, final boolean[] data) { + if (data != null && length > 0) { + boolean[] arrays = new boolean[length]; + // 获取数据长度 + int dataLength = data.length; + // 判断是否超过需要的长度 + if (dataLength >= length) { + for (int i = 0; i < length; i++) { + arrays[i] = data[i]; + } + } else { + for (int i = 0; i < dataLength; i++) { + arrays[i] = data[i]; + } + } + return arrays; + } + return null; + } + + // = + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @param 泛型 + * @return 裁剪后的数组 + */ + public static T[] subarray(final T[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + List lists = new ArrayList<>(length); + for (int i = off; i < off + length; i++) { + lists.add(data[i]); + } + return (T[]) Arrays.copyOf(lists.toArray(), length, data.getClass()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static int[] subarray(final int[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + int[] arrays = new int[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static byte[] subarray(final byte[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + byte[] arrays = new byte[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static char[] subarray(final char[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + char[] arrays = new char[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static short[] subarray(final short[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + short[] arrays = new short[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static long[] subarray(final long[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + long[] arrays = new long[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static float[] subarray(final float[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + float[] arrays = new float[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static double[] subarray(final double[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + double[] arrays = new double[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + /** + * 从数组上截取一段 + * @param data 数组 + * @param off 起始值 + * @param length 所需长度 + * @return 裁剪后的数组 + */ + public static boolean[] subarray(final boolean[] data, final int off, final int length) { + if (data == null || off < 0 || length < 0) return null; + try { + boolean[] arrays = new boolean[length]; + System.arraycopy(data, off, arrays, 0, length); + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subarray"); + } + return null; + } + + // = + + /** + * 追加数组内容字符串 + * @param data 数组 + * @param 泛型 + * @return 追加数组内容字符串 + */ + public static String appendToString(final T[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + ((data[0] == null) ? "null" : data[0].toString()) + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + T t = data[i]; + builder.append("\"").append(((t == null) ? "null" : t.toString())).append("\","); + } + T end = data[len - 1]; + builder.append("\"" + ((end == null) ? "null" : end.toString()) + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final int[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final byte[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final char[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final short[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final long[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final float[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final double[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } + + /** + * 追加数组内容字符串 + * @param data 数组 + * @return 追加数组内容字符串 + */ + public static String appendToString(final boolean[] data) { + if (data != null) { + int len = data.length; + if (len != 0) { + if (len == 1) { + return "\"" + data[0] + "\""; + } else { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + builder.append("\"").append(data[i]).append("\","); + } + builder.append("\"" + data[len - 1] + "\""); + return builder.toString(); + } + } + } + return ""; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/BigDecimalUtils.java b/lib/DevJava/src/main/java/dev/utils/common/BigDecimalUtils.java new file mode 100644 index 0000000000..6bf8a3bb23 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/BigDecimalUtils.java @@ -0,0 +1,764 @@ +package dev.utils.common; + +import java.math.BigDecimal; + +import dev.utils.JCLogUtils; + +/** + * detail: 资金运算工具类 + * @author Ttt + *
+ *     @see 
+ *     向下取 round = BigDecimal.ROUND_DOWN;
+ * 
+ */ +public final class BigDecimalUtils { + + private BigDecimalUtils() { + } + + // 日志 TAG + private static final String TAG = BigDecimalUtils.class.getSimpleName(); + // 默认保留位数 + private static final int DEF_DIV_SCALE = 10; + + /** + * 提供精确的加法运算 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(final double v1, final double v2) { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的加法运算 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static BigDecimal add(final String v1, final String v2) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + return b1.add(b2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add"); + } + return null; + } + + // = + + /** + * 提供精确的加法运算 + * @param v1 被加数 + * @param v2 加数 + * @param scale 保留 scale 位小数 + * @return 两个参数的和 + */ + public static double add(final double v1, final double v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (scale <= 0) { + return b1.add(b2).intValue(); + } else { + return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add"); + } + return 0; + } + + /** + * 提供精确的加法运算 + * @param v1 被加数 + * @param v2 加数 + * @param scale 保留 scale 位小数 + * @return 两个参数的和 + */ + public static String add(final String v1, final String v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + if (scale <= 0) { + return b1.add(b2).intValue() + ""; + } else { + return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add"); + } + return null; + } + + // = + + /** + * 提供精确的减法运算 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double substract(final double v1, final double v2) { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的减法运算 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static BigDecimal substract(final String v1, final String v2) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + return b1.subtract(b2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "substract"); + } + return null; + } + + // = + + /** + * 提供精确的减法运算 + * @param v1 被减数 + * @param v2 减数 + * @param scale 保留 scale 位小数 + * @return 两个参数的差 + */ + public static String substract(final double v1, final double v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (scale <= 0) { + return b1.subtract(b2).intValue() + ""; + } else { + return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "substract"); + } + return null; + } + + /** + * 提供精确的减法运算 + * @param v1 被减数 + * @param v2 减数 + * @param scale 保留 scale 位小数 + * @return 两个参数的差 + */ + public static String substract(final String v1, final String v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + if (scale <= 0) { + return b1.subtract(b2).intValue() + ""; + } else { + return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "substract"); + } + return null; + } + + // = + + /** + * 提供精确的乘法运算 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double multiply(final double v1, final double v2) { + try { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "multiply"); + } + return 0d; + } + + /** + * 提供精确的乘法运算 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double multiply(final String v1, final String v2) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + return b1.multiply(b2).doubleValue(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "multiply"); + } + return 0d; + } + + // = + + /** + * 提供精确的乘法运算 + * @param v1 被乘数 + * @param v2 乘数 + * @param scale 保留 scale 位小数 + * @return 两个参数的积 + */ + public static double multiply(final double v1, final double v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (scale <= 0) { + return b1.multiply(b2).intValue(); + } else { + return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "multiply"); + } + return 0d; + } + + // = + + /** + * 提供精确的乘法运算 + * @param v1 被乘数 + * @param v2 乘数 + * @param scale 保留 scale 位小数 + * @return 两个参数的积 + */ + public static String multiply(final String v1, final String v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + if (scale <= 0) { + return b1.multiply(b2).intValue() + ""; + } else { + return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "multiply"); + } + return null; + } + + // = + + /** + * 提供 ( 相对 ) 精确的除法运算 + * 当发生除不尽的情况时, 精确到小数点以后 10 位, 以后的数字四舍五入 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double divide(final double v1, final double v2) { + return divide(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供 ( 相对 ) 精确的除法运算 + * 当发生除不尽的情况时, 由 scale 参数指定精度, 以后的数字四舍五入 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示需要精确到小数点以后几位 + * @return 两个参数的商 + */ + public static double divide(final double v1, final double v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (scale <= 0) { + return b1.divide(b2).intValue(); + } else { + return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "divide"); + } + return 0d; + } + + /** + * 提供 ( 相对 ) 精确的除法运算 + * 当发生除不尽的情况时, 由 scale 参数指定精度, 以后的数字四舍五入 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示需要精确到小数点以后几位 + * @return 两个参数的商 + */ + public static String divide(final String v1, final String v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + if (scale <= 0) { + return b1.divide(b2).intValue() + ""; + } else { + return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "divide"); + } + return null; + } + + // = + + /** + * 提供精确的小数位四舍五入处理 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(final double v, final int scale) { + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = new BigDecimal("1"); + if (scale <= 0) { + return b.divide(one).intValue(); + } else { + return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + } + + /** + * 提供精确的小数位四舍五入处理 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static String round(final String v, final int scale) { + try { + BigDecimal b = new BigDecimal(v); + if (scale <= 0) { + return b.intValue() + ""; + } else { + return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "round"); + } + return null; + } + + // = + + /** + * 提供精确的小数位获取 + * @param v 待处理的数字 + * @param scale 小数点后保留几位 + * @param roundingMode 取小数点模式 + * @return 最后的结果 + */ + public static double round(final double v, final int scale, final int roundingMode) { + try { + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = new BigDecimal("1"); + if (scale <= 0) { + return b.divide(one).intValue(); + } else { + return b.divide(one, scale, roundingMode).doubleValue(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "round"); + } + return 0d; + } + + // = + + /** + * 取余数 + * @param v1 被除数 + * @param v2 除数 + * @param scale 小数点后保留几位 + * @return 余数 + */ + public static String remainder(final String v1, final String v2, final int scale) { + try { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + if (scale <= 0) { + return b1.remainder(b2).intValue() + ""; + } else { + return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remainder"); + } + return null; + } + + /** + * 取余数 + * @param v1 被除数 + * @param v2 除数 + * @param scale 小数点后保留几位 + * @return 余数 + */ + public static BigDecimal remainder(final BigDecimal v1, final BigDecimal v2, final int scale) { + try { + if (scale <= 0) { + return v1.remainder(v2); + } else { + return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remainder"); + } + return null; + } + + // = + + /** + * 比较大小 + * @param v1 输入的数值 + * @param v2 被比较的数字 + * @return {@code true} v1 >= v2, {@code false} v1 < v2 + */ + public static Boolean compare(final String v1, final String v2) { + try { + return new BigDecimal(v1).compareTo(new BigDecimal(v2)) != -1; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "compare"); + } + return true; + } + + /** + * 比较大小 + * @param v1 输入的数值 + * @param v2 被比较的数字 + * @return {@code true} v1 >= v2, {@code false} v1 < v2 + */ + public static Boolean compare(final BigDecimal v1, final BigDecimal v2) { + try { + return v1.compareTo(v2) != -1; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "compare"); + } + return false; + } + + /** + * 比较大小 + * @param v1 输入的数值 + * @param v2 被比较的数字 + * @return {@code true} v1 >= v2, {@code false} v1 < v2 + */ + public static Boolean compare(final String v1, final double v2) { + try { + return new BigDecimal(v1).compareTo(new BigDecimal(Double.toString(v2))) != -1; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "compare"); + } + return true; + } + + // = + + /** + * 金额分割, 四舍五入金额 + * @param value 金额数值 + * @return 指定格式处理的字符串 + */ + public static String formatMoney(final BigDecimal value) { + return formatMoney(value, 2, BigDecimal.ROUND_HALF_UP, 3, ","); + } + + /** + * 金额分割, 四舍五入金额 + * @param value 金额数值 + * @param scale 小数点后保留几位 + * @return 指定格式处理的字符串 + */ + public static String formatMoney(final BigDecimal value, final int scale) { + return formatMoney(value, scale, BigDecimal.ROUND_HALF_UP, 3, ","); + } + + /** + * 金额分割, 四舍五入金额 + * @param value 金额数值 + * @param scale 小数点后保留几位 + * @param mode 处理模式 + * @return 指定格式处理的字符串 + */ + public static String formatMoney(final BigDecimal value, final int scale, final int mode) { + return formatMoney(value, scale, mode, 3, ","); + } + + /** + * 金额分割, 四舍五入金额 + * @param value 金额数值 + * @param scale 小数点后保留几位 + * @param mode 处理模式 + * @param splitNumber 拆分位数 + * @return 指定格式处理的字符串 + */ + public static String formatMoney(final BigDecimal value, final int scale, final int mode, final int splitNumber) { + return formatMoney(value, scale, mode, splitNumber, ","); + } + + /** + * 金额分割, 四舍五入金额 + * @param value 金额数值 + * @param scale 小数点后保留几位 + * @param mode 处理模式 + * @param splitNumber 拆分位数 + * @param splitSymbol 拆分符号 + * @return 指定格式处理的字符串 + */ + public static String formatMoney(final BigDecimal value, final int scale, final int mode, final int splitNumber, final String splitSymbol) { + if (value == null) return null; + try { + // 如果等于 0, 直接返回 + if (value.doubleValue() == 0) { + return value.setScale(scale, mode).toPlainString(); + } + // 获取原始值字符串 - 非科学计数法 + String valuePlain = value.toPlainString(); + // 判断是否负数 + boolean isNegative = valuePlain.startsWith("-"); + // 处理后的数据 + BigDecimal bigDecimal = new BigDecimal(isNegative ? valuePlain.substring(1) : valuePlain); + // 范围处理 + valuePlain = bigDecimal.setScale(scale, mode).toPlainString(); + // 进行拆分小数点处理 + String[] values = valuePlain.split("\\."); + // 判断是否存在小数点 + boolean isDecimal = (values.length == 2); + + // 拼接符号 + String symbol = (splitSymbol != null) ? splitSymbol : ""; + // 防止出现负数 + int number = Math.max(splitNumber, 0); + // 格式化数据 - 拼接处理 + StringBuilder builder = new StringBuilder(); + // 进行处理小数点前的数值 + for (int len = values[0].length() - 1, i = len, splitPos = 1; i >= 0; i--) { + // 获取数据 + char ch = values[0].charAt(i); + builder.append(ch); // 保存数据 + // 判断是否需要追加符号 + if (number > 0 && splitPos % number == 0 && i != 0) { + builder.append(symbol); + } + splitPos++; + } + // 倒序处理 + builder.reverse(); + // 存在小数点, 则进行拼接 + if (isDecimal) { + builder.append(".").append(values[1]); + } + // 判断是否负数 + return isNegative ? "-" + builder.toString() : builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "formatMoney"); + } + return null; + } + + // = + + /** + * 获取自己想要的数据格式 + * @param value 需处理的数据 + * @param numOfIntPart 整数位数 + * @param numOfDecimalPart 小数位数 + * @return 处理过的数据 + */ + public static String adjustDouble(final String value, final int numOfIntPart, final int numOfDecimalPart) { + if (value == null) return null; + try { + // 按小数点的位置分割成整数部分和小数部分 + String[] array = value.split("\\."); + char[] tempA = new char[numOfIntPart]; + char[] tempB = new char[numOfDecimalPart]; + // 整数部分满足精度要求 ( 情况 1) + if (array[0].length() == numOfIntPart) { + // 直接获取整数部分长度字符 + for (int i = 0; i < array[0].length(); i++) { + tempA[i] = array[0].charAt(i); + } + // 小数部分精度大于或等于指定的精度 + if (numOfDecimalPart <= array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + tempB[i] = array[1].charAt(i); + } + } + // 小数部分精度小于指定的精度 + if (numOfDecimalPart > array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + if (i < array[1].length()) { + tempB[i] = array[1].charAt(i); + } else { + tempB[i] = '0'; + } + } + } + if (numOfDecimalPart == 0) { + return String.valueOf(tempA) + String.valueOf(tempB); + } + return String.valueOf(tempA) + "." + String.valueOf(tempB); + } + // 整数部分位数大于精度要求 ( 情况 2) + if (array[0].length() > numOfIntPart) { + // 先倒序获取指定位数的整数 + for (int i = array[0].length() - 1, j = 0; (i >= array[0].length() - numOfIntPart) && (j < numOfIntPart); i--, j++) { + tempA[j] = array[0].charAt(i); + } + char[] tempA1 = new char[numOfIntPart]; + // 调整顺序 + for (int j = 0, k = tempA.length - 1; j < numOfIntPart && (k >= 0); j++, k--) { + tempA1[j] = tempA[k]; + } + // 小数部分精度大于或等于指定的精度 + if (numOfDecimalPart <= array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + tempB[i] = array[1].charAt(i); + } + } + // 小数部分精度小于指定的精度 + if (numOfDecimalPart > array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + if (i < array[1].length()) { + tempB[i] = array[1].charAt(i); + } else { + tempB[i] = '0'; + } + } + } + return String.valueOf(tempA1) + "." + String.valueOf(tempB); + } + // 整数部分满足精度要求 ( 情况 3) + if (array[0].length() == numOfIntPart) { + // 直接获取整数部分长度字符 + for (int i = 0; i < array[0].length(); i++) { + tempA[i] = array[0].charAt(i); + } + // 小数部分精度小于指定的精度 + if (numOfDecimalPart > array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + if (i < array[1].length()) { + tempB[i] = array[1].charAt(i); + } else { + tempB[i] = '0'; + } + } + } + // 小数部分精度大于或等于指定的精度 + if (numOfDecimalPart <= array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + tempB[i] = array[1].charAt(i); + } + } + if (numOfDecimalPart == 0) { + return String.valueOf(tempA) + String.valueOf(tempB); + } + return String.valueOf(tempA) + "." + String.valueOf(tempB); + } + // 整数部分大于精度要求 ( 情况 4) + if (array[0].length() > numOfIntPart) { + // 先倒序获取指定位数的整数 + for (int i = array[0].length() - 1, j = 0; (i >= array[0].length() - numOfIntPart + 1) && (j < numOfIntPart); i--, j++) { + tempA[j] = array[0].charAt(i); + } + char[] tempA1 = new char[numOfIntPart]; + // 调整顺序 + for (int j = 0, k = tempA.length - 1; j < numOfIntPart && (k >= 0); j++) { + tempA1[j] = tempA[k]; + k--; + } + // 小数部分精度小于指定的精度 + if (numOfDecimalPart > array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + if (i >= array[1].length()) { + tempB[i] = '0'; + } else { + tempB[i] = array[1].charAt(i); + } + } + } + // 小数部分精度大于或等于指定的精度 + if (numOfDecimalPart <= array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + tempB[i] = array[1].charAt(i); + } + } + if (numOfDecimalPart == 0) { + return String.valueOf(tempA1) + String.valueOf(tempB); + } + return String.valueOf(tempA1) + "." + String.valueOf(tempB); + } + // 整数部分小于精度要求 ( 情况 5) + if (array[0].length() < numOfIntPart) { + // 先倒序获取指定位数的整数 + char[] tempA1 = new char[numOfIntPart]; + for (int i = array[0].length() - 1, j = 0; (i >= numOfIntPart - array[0].length() - (numOfIntPart - array[0].length())) && (j < numOfIntPart); i--, j++) { + tempA1[j] = array[0].charAt(i); + } + // 补 0 + for (int i = array[0].length(); i < array[0].length() + numOfIntPart - array[0].length(); i++) { + tempA1[i] = '0'; + } + + char[] tempA2 = new char[numOfIntPart]; + // 调整顺序 + for (int j = 0, k = tempA1.length - 1; j < numOfIntPart && (k >= 0); j++) { + tempA2[j] = tempA1[k]; + k--; + } + // 小数部分精度小于指定的精度 + if (numOfDecimalPart > array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + if (i < array[1].length()) { + tempB[i] = array[1].charAt(i); + } else { + tempB[i] = '0'; + } + } + } + // 小数部分精度大于或等于指定的精度 + if (numOfDecimalPart <= array[1].length()) { + for (int i = 0; i < numOfDecimalPart; i++) { + tempB[i] = array[1].charAt(i); + } + } + if (numOfDecimalPart == 0) { + return String.valueOf(tempA2) + String.valueOf(tempB); + } + return String.valueOf(tempA2) + "." + String.valueOf(tempB); + } + // ( 情况 6) + if ((array[0].length() < numOfIntPart) && (array[1].length() < numOfDecimalPart)) { + String data = value; + for (int i = 0; i < numOfIntPart - array[0].length(); i++) { + data = "0" + data; + } + for (int i = 0; i < numOfDecimalPart - array[1].length(); i++) { + data = data + "0"; + } + return data; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "adjustDouble"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/ClassUtils.java b/lib/DevJava/src/main/java/dev/utils/common/ClassUtils.java new file mode 100644 index 0000000000..06c506f3c0 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/ClassUtils.java @@ -0,0 +1,233 @@ +package dev.utils.common; + +import java.lang.reflect.Constructor; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.Date; + +import dev.utils.JCLogUtils; + +/** + * detail: 类 (Class) 工具类 + * @author Ttt + */ +public final class ClassUtils { + + private ClassUtils() { + } + + // 日志 TAG + private static final String TAG = ClassUtils.class.getSimpleName(); + + /** + * 判断类是否是基础数据类型 - 目前支持 11 种 + * @param clazz {@link Class} + * @return {@code true} yes, {@code false} no + */ + public static boolean isBaseDataType(final Class clazz) { + return clazz != null && (clazz.isPrimitive() || clazz.equals(String.class) || clazz.equals(Boolean.class) + || clazz.equals(Integer.class) || clazz.equals(Long.class) || clazz.equals(Float.class) + || clazz.equals(Double.class) || clazz.equals(Byte.class) || clazz.equals(Character.class) + || clazz.equals(Short.class) || clazz.equals(Date.class) || clazz.equals(byte[].class) + || clazz.equals(Byte[].class)); + } + + /** + * 根据类获取对象, 不再必须一个无参构造 + * @param clazz {@link Class} + * @param 泛型 + * @return 初始化后的对象 + */ + public static T newInstance(final Class clazz) { + if (clazz == null) return null; + try { + Constructor[] cons = clazz.getDeclaredConstructors(); + for (Constructor c : cons) { + Class[] cls = c.getParameterTypes(); + if (cls.length == 0) { + c.setAccessible(true); + return (T) c.newInstance(); + } else { + Object[] objs = new Object[cls.length]; + for (int i = 0; i < cls.length; i++) { + objs[i] = getDefaultPrimiticeValue(cls[i]); + } + c.setAccessible(true); + return (T) c.newInstance(objs); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "newInstance"); + } + return null; + } + + /** + * 判断 Class 是否为原始类型 (boolean、char、byte、short、int、long、float、double) + * @param clazz {@link Class} + * @return 原始类型值 + */ + public static Object getDefaultPrimiticeValue(final Class clazz) { + if (clazz != null && clazz.isPrimitive()) { + return clazz == boolean.class ? false : 0; + } + return null; + } + + /** + * 判断是否集合类型 + * @param clazz {@link Class} + * @return {@code true} yes, {@code false} no + */ + public static boolean isCollection(final Class clazz) { + return (clazz != null && Collection.class.isAssignableFrom(clazz)); + } + + /** + * 判断是否数组类型 + * @param clazz {@link Class} + * @return {@code true} yes, {@code false} no + */ + public static boolean isArray(final Class clazz) { + return (clazz != null && clazz.isArray()); + } + + // = + + /** + * 获取父类泛型类型 + * @param object Object + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericSuperclass(final Object object) { + return getGenericSuperclass(object, 0); + } + + /** + * 获取父类泛型类型 + * @param object Object + * @param pos 泛型参数索引 + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericSuperclass(final Object object, final int pos) { + if (object != null) { + return getGenericSuperclass(object.getClass(), pos); + } + return null; + } + + // = + + /** + * 获取父类泛型类型 + * @param clazz {@link Class} + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericSuperclass(final Class clazz) { + return getGenericSuperclass(clazz, 0); + } + + /** + * 获取父类泛型类型 + * @param clazz {@link Class} + * @param pos 泛型参数索引 + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericSuperclass(final Class clazz, final int pos) { + if (clazz != null && pos >= 0) { + try { + return (Class) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getGenericSuperclass"); + } + } + return null; + } + + // = + + /** + * 获取接口泛型类型 + * @param object Object + * @param interfaceClazz 接口 Class + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericInterfaces(final Object object, final Class interfaceClazz) { + return getGenericInterfaces(object, interfaceClazz, 0); + } + + /** + * 获取接口泛型类型 + * @param object Object + * @param interfaceClazz 接口 Class + * @param pos 泛型参数索引 + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericInterfaces(final Object object, final Class interfaceClazz, final int pos) { + if (object != null) { + return getGenericInterfaces(object.getClass(), interfaceClazz, pos); + } + return null; + } + + // = + + /** + * 获取接口泛型类型 + * @param clazz {@link Class} + * @param interfaceClazz 接口 Class + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericInterfaces(final Class clazz, final Class interfaceClazz) { + return getGenericInterfaces(clazz, interfaceClazz, 0); + } + + /** + * 获取接口泛型类型 + * @param clazz {@link Class} + * @param interfaceClazz 接口 Class + * @param pos 泛型参数索引 + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericInterfaces(final Class clazz, final Class interfaceClazz, final int pos) { + if (clazz != null && interfaceClazz != null && pos >= 0) { + try { + // 获取接口类名 + String iName = interfaceClazz.getName(); + if (iName.equals("")) return null; + // 获取接口泛型类型数组 + Type[] types = clazz.getGenericInterfaces(); + // 循环类型 + for (Type type : types) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + // 判断是否属于该接口 + String rawType = parameterizedType.getRawType().toString(); + // 判断接口包名是否一致 + if (rawType.startsWith("interface ")) { + if (rawType.equals("interface " + iName)) { + return (Class) parameterizedType.getActualTypeArguments()[pos]; + } + } else { + if (rawType.equals(iName)) { + return (Class) parameterizedType.getActualTypeArguments()[pos]; + } + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getGenericInterfaces"); + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/CloneUtils.java b/lib/DevJava/src/main/java/dev/utils/common/CloneUtils.java new file mode 100644 index 0000000000..b11e17b42a --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/CloneUtils.java @@ -0,0 +1,147 @@ +package dev.utils.common; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: 克隆工具类 + * @author Ttt + */ +public final class CloneUtils { + + private CloneUtils() { + } + + // 日志 TAG + private static final String TAG = CloneUtils.class.getSimpleName(); + + /** + * 进行克隆 + * @param data Object implements {@link Serializable} + * @param 泛型 + * @return 克隆后的对象 + */ + public static T deepClone(final Serializable data) { + if (data == null) return null; + return (T) bytesToObject(serializableToBytes(data)); + } + + /** + * 通过序列化实体类, 获取对应的 byte[] 数据 + * @param serializable Object implements {@link Serializable} + * @return 克隆后 byte[] + */ + public static byte[] serializableToBytes(final Serializable serializable) { + if (serializable == null) return null; + ObjectOutputStream oos = null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(baos); + oos.writeObject(serializable); + return baos.toByteArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "serializableToBytes"); + return null; + } finally { + if (oos != null) { + try { + oos.close(); + } catch (Exception e) { + } + } + } + } + + // = + + /** + * 进行克隆 + * @param map 存储集合 + * @param datas 需要克隆的数据源 + * @param key + * @param value + */ + public static void deepClone(final Map map, final Map datas) { + if (map != null && datas != null && datas.size() > 0) { + Iterator> iterator = datas.entrySet().iterator(); + while (iterator.hasNext()) { + try { + Map.Entry entry = iterator.next(); + // 获取 key + K key = entry.getKey(); + // 克隆对象 + V cloneObj = (V) bytesToObject(serializableToBytes((Serializable) entry.getValue())); + if (cloneObj != null) { + // 保存到集合 + map.put(key, cloneObj); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "deepClone"); + } + } + } + } + + /** + * 进行克隆 + * @param collection 存储集合 + * @param datas 需要克隆的数据源 + * @param 泛型 + */ + public static void deepClone(final Collection collection, final Collection datas) { + if (collection != null && datas != null && datas.size() > 0) { + Iterator iterator = datas.iterator(); + while (iterator.hasNext()) { + try { + // 克隆对象 + T cloneObj = (T) bytesToObject(serializableToBytes((Serializable) iterator.next())); + if (cloneObj != null) { + collection.add(cloneObj); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "deepClone"); + } + } + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + /** + * byte[] 转为 Object + * @param bytes byte[] + * @return {@link Object} + */ + private static Object bytesToObject(final byte[] bytes) { + if (bytes == null) return null; + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); + return ois.readObject(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToObject"); + } finally { + if (ois != null) { + try { + ois.close(); + } catch (Exception e) { + } + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/CloseUtils.java b/lib/DevJava/src/main/java/dev/utils/common/CloseUtils.java new file mode 100644 index 0000000000..d67d24982c --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/CloseUtils.java @@ -0,0 +1,51 @@ +package dev.utils.common; + +import java.io.Closeable; + +import dev.utils.JCLogUtils; + +/** + * detail: 关闭 (IO 流 ) 工具类 + * @author Ttt + */ +public final class CloseUtils { + + private CloseUtils() { + } + + // 日志 TAG + private static final String TAG = CloseUtils.class.getSimpleName(); + + /** + * 关闭 IO + * @param closeables Closeable[] + */ + public static void closeIO(final Closeable... closeables) { + if (closeables == null) return; + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "closeIO"); + } + } + } + } + + /** + * 安静关闭 IO + * @param closeables Closeable[] + */ + public static void closeIOQuietly(final Closeable... closeables) { + if (closeables == null) return; + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception ignore) { + } + } + } + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/CollectionUtils.java b/lib/DevJava/src/main/java/dev/utils/common/CollectionUtils.java new file mode 100644 index 0000000000..c7f4bbdc60 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/CollectionUtils.java @@ -0,0 +1,1170 @@ +package dev.utils.common; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import dev.utils.JCLogUtils; + +/** + * detail: 集合工具类 (Collection - List、Set、Queue) 等 + * @author Ttt + *
+ *     @see 
+ * 
+ */ +public final class CollectionUtils { + + private CollectionUtils() { + } + + // 日志 TAG + private static final String TAG = CollectionUtils.class.getSimpleName(); + + // ============== + // = Collection = + // ============== + + /** + * 判断 Collection 是否为 null + * @param collection {@link Collection} + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Collection collection) { + return (collection == null || collection.size() == 0); + } + + /** + * 判断 Collection 是否不为 null + * @param collection {@link Collection} + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Collection collection) { + return (collection != null && collection.size() != 0); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取 Collection 长度 + * @param collection {@link Collection} + * @return 如果 Collection 为 null, 则返回默认长度, 如果不为 null, 则返回 collection.size() + */ + public static int length(final Collection collection) { + return length(collection, 0); + } + + /** + * 获取 Collection 长度 + * @param collection {@link Collection} + * @param defaultLength 集合为 null 默认长度 + * @return 如果 Collection 为 null, 则返回 defaultLength, 如果不为 null, 则返回 collection.size() + */ + public static int length(final Collection collection, final int defaultLength) { + return collection != null ? collection.size() : defaultLength; + } + + // = + + /** + * 获取长度 to Collection 是否等于期望长度 + * @param collection {@link Collection} + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Collection collection, final int length) { + return collection != null && collection.size() == length; + } + + // = + + /** + * 判断 Collection 长度是否大于指定长度 + * @param collection {@link Collection} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean greaterThan(final Collection collection, final int length) { + return collection != null && collection.size() > length; + } + + /** + * 判断 Collection 长度是否大于等于指定长度 + * @param collection {@link Collection} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean greaterThanOrEqual(final Collection collection, final int length) { + return collection != null && collection.size() >= length; + } + + // = + + /** + * 判断 Collection 长度是否小于指定长度 + * @param collection {@link Collection} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean lessThan(final Collection collection, final int length) { + return collection != null && collection.size() < length; + } + + /** + * 判断 Collection 长度是否小于等于指定长度 + * @param collection {@link Collection} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean lessThanOrEqual(final Collection collection, final int length) { + return collection != null && collection.size() <= length; + } + + // ============ + // = 数据获取 = + // ============ + + /** + * 获取数据 + * @param collection {@link Collection} + * @param pos 索引 + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final Collection collection, final int pos) { + if (collection != null) { + // 防止索引为负数 + if (pos < 0) { + return null; + } + if (collection instanceof List) { + try { + return (T) ((List) collection).get(pos); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } else { + try { + return (T) collection.toArray()[pos]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get - " + pos); + } + } + } + return null; + } + + /** + * 获取第一条数据 + * @param collection {@link Collection} + * @param 泛型 + * @return 索引为 0 的值 + */ + public static T getFirst(final Collection collection) { + return get(collection, 0); + } + + /** + * 获取最后一条数据 + * @param collection {@link Collection} + * @param 泛型 + * @return 索引 length - 1 的值 + */ + public static T getLast(final Collection collection) { + return get(collection, length(collection) - 1); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的值 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param offset 偏移量 + * @param 泛型 + * @return 对应索引的值 + */ + public static T get(final Collection collection, final T value, final int number, final boolean notNull, final int offset) { + if (collection != null) { + if (notNull && value == null) { + return null; + } + try { + // 保存当前临时次数 + int temp = number; + // 转换数组 + T[] arrays = (T[]) collection.toArray(); + // 进行循环判断 + for (int i = 0, len = arrays.length; i < len; i++) { + T t = arrays[i]; + // 判断值是否一样 + if (equals(t, value)) { + if (temp - 1 < 0) { + return arrays[i + offset]; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return null; + } + + /** + * 根据指定 value 获取 value 所在位置 + 偏移量的索引 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param offset 偏移量 + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final Collection collection, final T value, final int number, final boolean notNull, final int offset) { + if (collection != null) { + if (notNull && value == null) { + return -1; + } + try { + // 保存当前临时次数 + int temp = number; + // 转换数组 + T[] arrays = (T[]) collection.toArray(); + // 进行循环判断 + for (int i = 0, len = arrays.length; i < len; i++) { + T t = arrays[i]; + // 判断值是否一样 + if (equals(t, value)) { + if (temp - 1 < 0) { + return i + offset; + } + temp--; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPosition"); + } + } + return -1; + } + + // = + + /** + * 根据指定 value 获取索引 + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final Collection collection, final T value) { + return getPosition(collection, value, 0, false, 0); + } + + /** + * 根据指定 value 获取索引 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final Collection collection, final T value, final int number) { + return getPosition(collection, value, number, false, 0); + } + + /** + * 根据指定 value 获取索引 + * @param collection {@link Collection} + * @param value 值 + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final Collection collection, final T value, final boolean notNull) { + return getPosition(collection, value, 0, notNull, 0); + } + + /** + * 根据指定 value 获取索引 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应的索引 + */ + public static int getPosition(final Collection collection, final T value, final int number, final boolean notNull) { + return getPosition(collection, value, number, notNull, 0); + } + + // = + + /** + * 根据指定 value 获取索引, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应的索引 + */ + public static int getPositionNotNull(final Collection collection, final T value) { + return getPosition(collection, value, 0, true, 0); + } + + /** + * 根据指定 value 获取索引, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应的索引 + */ + public static int getPositionNotNull(final Collection collection, final T value, final int number) { + return getPosition(collection, value, number, true, 0); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置的下一个值 + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNext(final Collection collection, final T value) { + return get(collection, value, 0, false, 1); + } + + /** + * 根据指定 value 获取 value 所在位置的下一个值 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNext(final Collection collection, final T value, final int number) { + return get(collection, value, number, false, 1); + } + + /** + * 根据指定 value 获取 value 所在位置的下一个值 + * @param collection {@link Collection} + * @param value 值 + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNext(final Collection collection, final T value, final boolean notNull) { + return get(collection, value, 0, notNull, 1); + } + + /** + * 根据指定 value 获取 value 所在位置的下一个值 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNext(final Collection collection, final T value, final int number, final boolean notNull) { + return get(collection, value, number, notNull, 1); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置的下一个值, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNextNotNull(final Collection collection, final T value) { + return get(collection, value, 0, true, 1); + } + + /** + * 根据指定 value 获取 value 所在位置的下一个值, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T getNextNotNull(final Collection collection, final T value, final int number) { + return get(collection, value, number, true, 1); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置的上一个值 + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPrevious(final Collection collection, final T value) { + return get(collection, value, 0, false, -1); + } + + /** + * 根据指定 value 获取 value 所在位置的上一个值 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPrevious(final Collection collection, final T value, final int number) { + return get(collection, value, number, false, -1); + } + + /** + * 根据指定 value 获取 value 所在位置的上一个值 + * @param collection {@link Collection} + * @param value 值 + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPrevious(final Collection collection, final T value, final boolean notNull) { + return get(collection, value, 0, notNull, -1); + } + + /** + * 根据指定 value 获取 value 所在位置的上一个值 + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param notNull 是否不允许值为 null + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPrevious(final Collection collection, final T value, final int number, final boolean notNull) { + return get(collection, value, number, notNull, -1); + } + + // = + + /** + * 根据指定 value 获取 value 所在位置的上一个值, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPreviousNotNull(final Collection collection, final T value) { + return get(collection, value, 0, true, -1); + } + + /** + * 根据指定 value 获取 value 所在位置的上一个值, 不允许值为 null + * @param collection {@link Collection} + * @param value 值 + * @param number 符合条件次数 ( 从 0 开始, 0 = 1) + * @param 泛型 + * @return 对应索引的值 + */ + public static T getPreviousNotNull(final Collection collection, final T value, final int number) { + return get(collection, value, number, true, -1); + } + + // ============ + // = 添加数据 = + // ============ + + /** + * 添加一条数据 + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean add(final Collection collection, final T value) { + return add(collection, value, false); + } + + /** + * 添加一条数据 + * @param collection {@link Collection} + * @param value 值 + * @param notNull 是否不允许添加 null 数据 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean add(final Collection collection, final T value, final boolean notNull) { + if (collection != null) { + if (notNull) { + if (value != null) { + try { + return collection.add(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add notNull"); + } + } + } else { + try { + return collection.add(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add"); + } + } + } + return false; + } + + /** + * 添加一条数据 - value 不允许为 null + * @param collection {@link Collection} + * @param value 值 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean addNotNull(final Collection collection, final T value) { + return add(collection, value, true); + } + + // = + + /** + * 添加集合数据 + * @param collection {@link Collection} + * @param values 准备添加的值 ( 集合 ) + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean addAll(final Collection collection, final Collection values) { + return addAll(collection, values, false); + } + + /** + * 添加集合数据 + * @param collection {@link Collection} + * @param values 准备添加的值 ( 集合 ) + * @param notNull 是否不允许添加 null 值 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean addAll(final Collection collection, final Collection values, final boolean notNull) { + if (collection != null && values != null) { + if (notNull) { + try { + for (T t : values) { + if (t != null) { + collection.add(t); + } + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addAll notNull"); + } + } else { + try { + return collection.addAll(values); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addAll"); + } + } + } + return false; + } + + /** + * 添加集合数据 - values 内的值不允许为 null + * @param collection {@link Collection} + * @param values 准备添加的值 ( 集合 ) + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean addAllNotNull(final Collection collection, final Collection values) { + return addAll(collection, values, true); + } + + // ================================ + // = 添加数据 - 到指定索引 (List) = + // ================================ + + /** + * 添加一条数据到指定索引后 + * @param index 索引 + * @param list 集合 + * @param value 值 + * @param 泛型 + */ + public static void add(final int index, final List list, final T value) { + add(index, list, value, false); + } + + /** + * 添加一条数据到指定索引后 + * @param index 索引 + * @param list 集合 + * @param value 值 + * @param notNull 是否不允许添加 null 数据 + * @param 泛型 + */ + public static void add(final int index, final List list, final T value, final boolean notNull) { + if (list != null) { + if (notNull) { + if (value != null) { + try { + list.add(index, value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add notNull"); + } + } + } else { + try { + list.add(index, value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "add"); + } + } + } + } + + /** + * 添加一条数据到指定索引后 - value 不允许为 null + * @param index 索引 + * @param list 集合 + * @param value 值 + * @param 泛型 + */ + public static void addNotNull(final int index, final List list, final T value) { + add(index, list, value, true); + } + + // = + + /** + * 添加集合数据到指定索引后 + * @param index 索引 + * @param list 集合 + * @param values 准备添加的值 ( 集合 ) + * @param 泛型 + */ + public static void addAll(final int index, final List list, final List values) { + addAll(index, list, values, false); + } + + /** + * 添加集合数据到指定索引后 + * @param index 索引 + * @param list 集合 + * @param values 准备添加的值 ( 集合 ) + * @param notNull 是否不允许添加 null 值 + * @param 泛型 + */ + public static void addAll(final int index, final List list, final List values, final boolean notNull) { + if (list != null && values != null) { + if (notNull) { + try { + List tempList = new ArrayList<>(); + for (T t : values) { + if (t != null) { + tempList.add(t); + } + } + // 添加到集合中 + list.addAll(index, tempList); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addAll notNull"); + } + } else { + try { + list.addAll(index, values); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "addAll"); + } + } + } + } + + /** + * 添加集合数据到指定索引后 - values 内的值不允许为 null + * @param index 索引 + * @param list 集合 + * @param values 准备添加的值 ( 集合 ) + * @param 泛型 + */ + public static void addAllNotNull(final int index, final List list, final List values) { + addAll(index, list, values, true); + } + + // ============ + // = 删除数据 = + // ============ + + /** + * 移除一条数据 + * @param collection {@link Collection} + * @param value 准备删除的值 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean remove(final Collection collection, final T value) { + if (collection != null) { + try { + return collection.remove(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remove"); + } + } + return false; + } + + /** + * 移除一条数据 + * @param list 集合 + * @param pos 准备删除的索引 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static T remove(final List list, final int pos) { + if (list != null) { + // 防止索引为负数 + if (pos < 0) { + return null; + } + try { + return list.remove(pos); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remove"); + } + } + return null; + } + + /** + * 移除集合数据 + * @param collection {@link Collection} + * @param values 准备删除的集合 + * @param 泛型 + * @return {@code true} success, {@code false} fail + */ + public static boolean removeAll(final Collection collection, final Collection values) { + if (collection != null && values != null) { + try { + return collection.removeAll(values); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeAll"); + } + } + return false; + } + + // = + + /** + * 清空集合中符合指定 value 的全部数据 + * @param collection {@link Collection} + * @param value 准备对比移除的值 + * @param 泛型 + */ + public static void clear(final Collection collection, final T value) { + if (collection != null) { + try { + Iterator iterator = collection.iterator(); + while (iterator.hasNext()) { + T t = iterator.next(); + // 判断值是否一样 + if (equals(t, value)) { + iterator.remove(); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "clear"); + } + } + } + + /** + * 保留集合中符合指定 value 的全部数据 + * @param collection {@link Collection} + * @param value 准备对比保留的值 + * @param 泛型 + */ + public static void clearNotBelong(final Collection collection, final T value) { + if (collection != null) { + try { + Iterator iterator = collection.iterator(); + while (iterator.hasNext()) { + T t = iterator.next(); + // 判断值是否不一样 ( 保留不一样的 ) + if (!equals(t, value)) { + iterator.remove(); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "clearNotBelong"); + } + } + } + + /** + * 清空集合全部数据 + * @param collection {@link Collection} + * @param 泛型 + */ + public static void clearAll(final Collection collection) { + if (collection != null) { + try { + collection.clear(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "clearAll"); + } + } + } + + /** + * 清空集合中为 null 的值 + * @param collection {@link Collection} + * @param 泛型 + */ + public static void clearNull(final Collection collection) { + clear(collection, null); + } + + // ==================== + // = 判断集合是否相同 = + // ==================== + + /** + * 判断两个集合是否相同 + * @param collection1 第一个集合 + * @param collection2 第二个集合 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEqualCollection(final Collection collection1, final Collection collection2) { + // 数据长度 + int len = 0; + // 判断数据是否相同 + if (collection1 != null && collection2 != null && (len = collection1.size()) == collection2.size()) { + if (len == 0) return true; + + // 进行判断类型, 如果不同, 则直接跳过不处理 + if (!collection1.getClass().getName().equals(collection2.getClass().getName())) { + return false; + } + + // 如果集合相等, 直接跳过 + if (collection1 == collection2) { + return true; + } + + T[] arrays1, arrays2; + try { + // 转换数组, 防止异常 + arrays1 = (T[]) collection1.toArray(); + arrays2 = (T[]) collection2.toArray(); + } catch (Exception e) { + return false; + } + if (arrays1 == null || arrays2 == null) { + return false; + } + + for (int i = 0; i < len; i++) { + // 判断两个值是否一样 + boolean equals = equals(arrays1[i], arrays2[i]); + // 如果不一样, 直接 return + if (!equals) { + return false; + } + } + return true; + } + // 如果不符合条件, 防止两个集合都是为 null + return (collection1 == null && collection2 == null); + } + + /** + * 判断多个集合是否相同 + * @param collections 集合数组 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEqualCollections(final Collection... collections) { + if (collections != null && collections.length >= 2) { + // 获取长度 + int len = collections.length; + // 设置临时值为第一个 + Collection temp = collections[0]; + // 进行判断 + for (int i = 1; i < len; i++) { + // 判断是否一样 + boolean equalCollection = isEqualCollection(temp, collections[i]); + // 如果不一样, 直接返回 + if (!equalCollection) { + return false; + } + } + return true; + } + return false; + } + + // ============ + // = 集合处理 = + // ============ + + // ======== + // = 并集 = + // ======== + + /** + * 两个集合并集处理 + * @param collection1 第一个集合 + * @param collection2 第二个集合 + * @param 泛型 + * @return 并集集合 + */ + public static Collection union(final Collection collection1, final Collection collection2) { + if (collection1 != null && collection2 != null) { + try { + // 初始化新的集合, 默认保存第一个集合的数据 + Set sets = new LinkedHashSet<>(collection1); + sets.addAll(collection2); + // 返回集合 + return sets; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "union"); + } + return null; + } else if (collection1 != null) { + return collection1; + } + return collection2; + } + + /** + * 多个集合并集处理 + * @param collections 集合数组 + * @param 泛型 + * @return 并集集合 + */ + public static Collection unions(final Collection... collections) { + if (collections != null) { + int len = collections.length; + if (len >= 2) { + try { + // 保存第一个集合 + Set sets = new LinkedHashSet<>(); + // 防止集合为 null + if (collections[0] != null) { + sets.addAll(collections[0]); + } + // 进行循环处理 + for (int i = 1; i < len; i++) { + // 获取集合值 + Collection value = collections[i]; + if (value != null) { + sets.addAll(value); + } + } + return sets; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "unions"); + } + return null; + } + return collections[0]; + } + return null; + } + + // ======== + // = 交集 = + // ======== + + /** + * 两个集合交集处理 + * @param collection1 第一个集合 + * @param collection2 第二个集合 + * @param 泛型 + * @return 交集集合 + */ + public static Collection intersection(final Collection collection1, final Collection collection2) { + if (collection1 != null && collection2 != null) { + try { + // 专门用于返回中转的集合 + Set sets = new LinkedHashSet<>(); + + // 初始化新的集合, 默认保存第一个集合的数据 + Set setsTemp = new LinkedHashSet<>(collection1); + // 循环第二个集合 + Iterator iterator = collection2.iterator(); + while (iterator.hasNext()) { + T t = iterator.next(); + // 判断是否存在, 存在则保存 + if (setsTemp.contains(t)) { + sets.add(t); + } + } + // 返回集合 + return sets; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "intersection"); + } + return null; + } else if (collection1 != null) { + return collection1; + } + return collection2; + } + + /** + * 两个集合交集的补集处理 + * @param collection1 第一个集合 + * @param collection2 第二个集合 + * @param 泛型 + * @return 交集集合 + */ + public static Collection disjunction(final Collection collection1, final Collection collection2) { + try { + // 先进行并集处理 + Collection unionC = union(collection1, collection2); + // 在进行交集处理 + Collection intersectionC = intersection(collection1, collection2); + // 再进行移除处理 + if (unionC != null && intersectionC != null) { + try { + // 移除数据 + unionC.removeAll(intersectionC); + return unionC; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "disjunction"); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "disjunction"); + } + return null; + } + + /** + * 两个集合差集 ( 扣除 ) 处理 + * @param collection1 第一个集合 + * @param collection2 第二个集合 + * @param 泛型 + * @return 差集 ( 扣除 ) 集合 + */ + public static Collection subtract(final Collection collection1, final Collection collection2) { + try { + // 先进行交集处理 + Collection intersectionC = intersection(collection1, collection2); + // 保存到新的集合中 + Set sets = new LinkedHashSet<>(collection1); + // 进行移除 + sets.removeAll(intersectionC); + // 返回集合 + return sets; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subtract"); + } + return null; + } + + // ============ + // = 快捷判断 = + // ============ + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + // ============ + // = 转换处理 = + // ============ + + /** + * 转换数组 to Object + * @param collection {@link Collection} + * @param 泛型 + * @return 转换后的数组 + */ + public static Object[] toArray(final Collection collection) { + if (collection != null) { + try { + return collection.toArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toArray"); + } + } + return null; + } + + /** + * 集合翻转处理 + * @param collection {@link Collection} + * @param 泛型 + * @return 翻转后的集合 + */ + public static Collection reverse(final Collection collection) { + try { + // 返回集合 + List lists = new ArrayList<>(); + // 转换数据 + T[] arrays = (T[]) collection.toArray(); + // 循环处理 + for (int i = arrays.length - 1; i >= 0; i--) { + lists.add(arrays[i]); + } + // 保存新的数据 + return lists; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "reverse"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/ColorUtils.java b/lib/DevJava/src/main/java/dev/utils/common/ColorUtils.java new file mode 100644 index 0000000000..a05a7f7d64 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/ColorUtils.java @@ -0,0 +1,670 @@ +package dev.utils.common; + +import java.util.HashMap; +import java.util.Locale; + +import dev.utils.JCLogUtils; + +/** + * detail: 颜色工具类 ( 包括常用的色值 ) + * @author Ttt + */ +public final class ColorUtils { + + private ColorUtils() { + } + + // 日志 TAG + private static final String TAG = ColorUtils.class.getSimpleName(); + // 透明 + public static final int TRANSPARENT = 0x00000000; + // 白色 + public static final int WHITE = 0xffffffff; + // 白色 - 半透明 + public static final int WHITE_TRANSLUCENT = 0x80ffffff; + // 黑色 + public static final int BLACK = 0xff000000; + // 黑色 - 半透明 + public static final int BLACK_TRANSLUCENT = 0x80000000; + // 红色 + public static final int RED = 0xffff0000; + // 红色 - 半透明 + public static final int RED_TRANSLUCENT = 0x80ff0000; + // 绿色 + public static final int GREEN = 0xff00ff00; + // 绿色 - 半透明 + public static final int GREEN_TRANSLUCENT = 0x8000ff00; + // 蓝色 + public static final int BLUE = 0xff0000ff; + // 蓝色 - 半透明 + public static final int BLUE_TRANSLUCENT = 0x800000ff; + // 灰色 + public static final int GRAY = 0xff969696; + // 灰色 - 半透明 + public static final int GRAY_TRANSLUCENT = 0x80969696; + // 天蓝 + public static final int SKYBLUE = 0xff87ceeb; + // 橙色 + public static final int ORANGE = 0xffffa500; + // 金色 + public static final int GOLD = 0xffffd700; + // 粉色 + public static final int PINK = 0xffffc0cb; + // 紫红色 + public static final int FUCHSIA = 0xffff00ff; + // 灰白色 + public static final int GRAYWHITE = 0xfff2f2f2; + // 紫色 + public static final int PURPLE = 0xff800080; + // 青色 + public static final int CYAN = 0xff00ffff; + // 黄色 + public static final int YELLOW = 0xffffff00; + // 巧克力色 + public static final int CHOCOLATE = 0xffd2691e; + // 番茄色 + public static final int TOMATO = 0xffff6347; + // 橙红色 + public static final int ORANGERED = 0xffff4500; + // 银白色 + public static final int SILVER = 0xffc0c0c0; + // 深灰色 + public static final int DKGRAY = 0xFF444444; + // 亮灰色 + public static final int LTGRAY = 0xFFCCCCCC; + // 洋红色 + public static final int MAGENTA = 0xFFFF00FF; + // 高光 + public static final int HIGHLIGHT = 0x33ffffff; + // 低光 + public static final int LOWLIGHT = 0x33000000; + + /** + * 0-255 十进值转换成十六进制, 如 255 就是 ff + * 255 * 0.x = 十进制 转 十六进制 + *

+ * 透明度 0-100 + * 00、19、33、4C、66、7F、99、B2、CC、E5、FF + */ + + /** + * 获取十六进制透明度字符串 + * @param alpha 0-255 + * @return 透明度 ( 十六进制 ) 值 + */ + public static String toHexAlpha(final int alpha) { + try { + if (alpha >= 0 && alpha <= 255) { + return Integer.toHexString(alpha); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexAlpha"); + } + return null; + } + + // = + + /** + * 返回一个颜色中的透明度值 ( 返回十进制 ) + * @param color argb color + * @return alpha 值 + */ + public static int alpha(final int color) { + return color >>> 24; + } + + /** + * 返回一个颜色中的透明度百分比值 + * @param color argb color + * @return alpha 百分比值 + */ + public static float alphaPercent(final int color) { + return percentF(alpha(color), 255); + } + + // = + + /** + * 返回一个颜色中红色的色值 ( 返回十进制 ) + * @param color argb/rgb color + * @return red 值 + */ + public static int red(final int color) { + return (color >> 16) & 0xFF; + } + + /** + * 返回一个颜色中红色的百分比值 + * @param color argb/rgb color + * @return red 百分比值 + */ + public static float redPercent(final int color) { + return percentF(red(color), 255); + } + + // = + + /** + * 返回一个颜色中绿色的色值 ( 返回十进制 ) + * @param color argb/rgb color + * @return green 百分比值 + */ + public static int green(final int color) { + return (color >> 8) & 0xFF; + } + + /** + * 返回一个颜色中绿色的百分比值 + * @param color argb/rgb color + * @return green 百分比值 + */ + public static float greenPercent(final int color) { + return percentF(green(color), 255); + } + + // = + + /** + * 返回一个颜色中蓝色的色值 ( 返回十进制 ) + * @param color argb/rgb color + * @return blue 百分比值 + */ + public static int blue(final int color) { + return color & 0xFF; + } + + /** + * 返回一个颜色中蓝色的百分比值 + * @param color argb/rgb color + * @return blue 百分比值 + */ + public static float bluePercent(final int color) { + return percentF(blue(color), 255); + } + + // = + + /** + * 根据对应的 red、green、blue 生成一个颜色值 + * @param red 红色值 [0-255] + * @param green 绿色值 [0-255] + * @param blue 蓝色值 [0-255] + * @return rgb 颜色值 + */ + public static int rgb(final int red, final int green, final int blue) { + return 0xff000000 | (red << 16) | (green << 8) | blue; + } + + /** + * 根据对应的 red、green、blue 生成一个颜色值 + * @param red 红色值 [0-255] + * @param green 绿色值 [0-255] + * @param blue 蓝色值 [0-255] + * @return rgb 颜色值 + */ + public static int rgb(final float red, final float green, final float blue) { + return 0xff000000 | + ((int) (red * 255.0f + 0.5f) << 16) | + ((int) (green * 255.0f + 0.5f) << 8) | + (int) (blue * 255.0f + 0.5f); + } + + // = + + /** + * 根据对应的 alpha、red、green、blue 生成一个颜色值 ( 含透明度 ) + * @param alpha 透明度 [0-255] + * @param red 红色值 [0-255] + * @param green 绿色值 [0-255] + * @param blue 蓝色值 [0-255] + * @return argb 颜色值 + */ + public static int argb(final int alpha, final int red, final int green, final int blue) { + return (alpha << 24) | (red << 16) | (green << 8) | blue; + } + + /** + * 根据对应的 alpha、red、green、blue 生成一个颜色值 ( 含透明度 ) + * @param alpha 透明度 [0-255] + * @param red 红色值 [0-255] + * @param green 绿色值 [0-255] + * @param blue 蓝色值 [0-255] + * @return argb 颜色值 + */ + public static int argb(final float alpha, final float red, final float green, final float blue) { + return ((int) (alpha * 255.0f + 0.5f) << 24) | + ((int) (red * 255.0f + 0.5f) << 16) | + ((int) (green * 255.0f + 0.5f) << 8) | + (int) (blue * 255.0f + 0.5f); + } + + // = + + /** + * 判断颜色 RGB 是否有效 + * @param color rgb color + * @return {@code true} yes, {@code false} no + */ + public static boolean isRGB(final int color) { + int red = red(color); + int green = green(color); + int blue = blue(color); + return (red <= 255 && red >= 0) && + (green <= 255 && green >= 0) && + (blue <= 255 && blue >= 0); + } + + /** + * 判断颜色 ARGB 是否有效 + * @param color argb color + * @return {@code true} yes, {@code false} no + */ + public static boolean isARGB(final int color) { + int alpha = alpha(color); + int red = red(color); + int green = green(color); + int blue = blue(color); + return (alpha <= 255 && alpha >= 0) && + (red <= 255 && red >= 0) && + (green <= 255 && green >= 0) && + (blue <= 255 && blue >= 0); + } + + // = + + /** + * 设置透明度 + * @param color argb/rgb color + * @param alpha 透明度 [0-255] + * @return argb 颜色值 + */ + public static int setAlpha(final int color, final int alpha) { + return (color & 0x00ffffff) | (alpha << 24); + } + + /** + * 设置透明度 + * @param color argb/rgb color + * @param alpha 透明度 [0-255] + * @return argb 颜色值 + */ + public static int setAlpha(final int color, final float alpha) { + return (color & 0x00ffffff) | ((int) (alpha * 255.0f + 0.5f) << 24); + } + + /** + * 改变颜色值中的红色色值 + * @param color argb/rgb color + * @param red 红色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setRed(final int color, final int red) { + return (color & 0xff00ffff) | (red << 16); + } + + /** + * 改变颜色值中的红色色值 + * @param color argb/rgb color + * @param red 红色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setRed(final int color, final float red) { + return (color & 0xff00ffff) | ((int) (red * 255.0f + 0.5f) << 16); + } + + /** + * 改变颜色值中的绿色色值 + * @param color argb/rgb color + * @param green 绿色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setGreen(final int color, final int green) { + return (color & 0xffff00ff) | (green << 8); + } + + /** + * 改变颜色值中的绿色色值 + * @param color argb/rgb color + * @param green 绿色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setGreen(final int color, final float green) { + return (color & 0xffff00ff) | ((int) (green * 255.0f + 0.5f) << 8); + } + + /** + * 改变颜色值中的蓝色色值 + * @param color argb/rgb color + * @param blue 蓝色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setBlue(final int color, final int blue) { + return (color & 0xffffff00) | blue; + } + + /** + * 改变颜色值中的蓝色色值 + * @param color argb/rgb color + * @param blue 蓝色值 [0-255] + * @return argb/rgb 颜色值 + */ + public static int setBlue(final int color, final float blue) { + return (color & 0xffffff00) | (int) (blue * 255.0f + 0.5f); + } + + // = + + /** + * 解析颜色字符串, 返回对应的颜色值 + * @param colorStr argb/rgb color String + * @return argb/rgb 颜色值 + */ + private static int priParseColor(final String colorStr) { + if (colorStr.charAt(0) == '#') { + // Use a long to avoid rollovers on #ffXXXXXX + long color = Long.parseLong(colorStr.substring(1), 16); + if (colorStr.length() == 7) { + // Set the alpha value + color |= 0x00000000ff000000; + } else if (colorStr.length() != 9) { + throw new IllegalArgumentException("Unknown color"); + } + return (int) color; + } else { + Integer color = sColorNameMaps.get(colorStr.toLowerCase(Locale.ROOT)); + if (color != null) { + return color; + } + } + throw new IllegalArgumentException("Unknown color"); + } + + /** + * 解析颜色字符串, 返回对应的颜色值 + *
+     *     支持的格式:
+     *     #RRGGBB
+     *     #AARRGGBB
+     *     'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta', 'yellow', 'lightgray', 'darkgray'
+     * 
+ * @param colorStr argb/rgb color String + * @return argb/rgb 颜色值 + */ + public static int parseColor(final String colorStr) { + if (colorStr != null) { + try { + return priParseColor(colorStr); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseColor"); + } + } + return -1; + } + + /** + * 颜色值 转换 RGB 颜色字符串 + * @param colorInt rgb int color + * @return rgb color String + */ + public static String intToRgbString(final int colorInt) { + int color = colorInt; + color = color & 0x00ffffff; + String colorStr = Integer.toHexString(color); + while (colorStr.length() < 6) { + colorStr = "0" + colorStr; + } + return "#" + colorStr; + } + + /** + * 颜色值 转换 ARGB 颜色字符串 + * @param colorInt argb int color + * @return argb color String + */ + public static String intToArgbString(final int colorInt) { + String colorString = Integer.toHexString(colorInt); + while (colorString.length() < 6) { + colorString = "0" + colorString; + } + while (colorString.length() < 8) { + colorString = "f" + colorString; + } + return "#" + colorString; + } + + // = + + /** + * 获取随机颜色值 + * @return 随机颜色值 + */ + public static int getRandomColor() { + return getRandomColor(true); + } + + /** + * 获取随机颜色值 + * @param supportAlpha 是否支持透明度 + * @return argb/rgb 颜色值 + */ + public static int getRandomColor(final boolean supportAlpha) { + int high = supportAlpha ? (int) (Math.random() * 0x100) << 24 : 0xFF000000; + return high | (int) (Math.random() * 0x1000000); + } + + /** + * 判断是否为 ARGB 格式的十六进制颜色, 例如: FF990587 + * @param colorStr color String + * @return {@code true} yes, {@code false} no + */ + public static boolean judgeColorString(final String colorStr) { + if (colorStr != null && colorStr.length() == 8) { + for (int i = 0; i < colorStr.length(); i++) { + char cc = colorStr.charAt(i); + return !(cc != '0' && cc != '1' && cc != '2' && cc != '3' && cc != '4' && cc != '5' && cc != '6' && cc != '7' && cc != '8' && cc != '9' + && cc != 'A' && cc != 'B' && cc != 'C' && cc != 'D' && cc != 'E' && cc != 'F' + && cc != 'a' && cc != 'b' && cc != 'c' && cc != 'd' && cc != 'e' && cc != 'f'); + } + } + return false; + } + + // = + + /** + * 颜色加深 ( 单独修改 RGB 值, 不变动透明度 ) + * @param colorStr color String + * @param darkValue 加深值 + * @return 加深后的颜色值 + */ + public static int setDark(final String colorStr, final int darkValue) { + int color = parseColor(colorStr); + if (color == -1) return -1; + return setDark(color, darkValue); + } + + /** + * 颜色加深 ( 单独修改 RGB 值, 不变动透明度 ) + * @param color int color + * @param darkValue 加深值 + * @return 加深后的颜色值 + */ + public static int setDark(final int color, final int darkValue) { + int red = red(color); + int green = green(color); + int blue = blue(color); + // 进行加深 ( 累减 ) + red -= darkValue; + green -= darkValue; + blue -= darkValue; + // 颜色值 + int colorTemp = color; + // 进行设置 + colorTemp = setRed(colorTemp, clamp(red, 255, 0)); + colorTemp = setGreen(colorTemp, clamp(green, 255, 0)); + colorTemp = setBlue(colorTemp, clamp(blue, 255, 0)); + return colorTemp; + } + + /** + * 颜色变浅, 变亮 ( 单独修改 RGB 值, 不变动透明度 ) + * @param colorStr color String + * @param lightValue 变亮 ( 变浅 ) 值 + * @return 变亮 ( 变浅 ) 后的颜色值 + */ + public static int setLight(final String colorStr, final int lightValue) { + int color = parseColor(colorStr); + if (color == -1) return -1; + return setLight(color, lightValue); + } + + /** + * 颜色变浅, 变亮 ( 单独修改 RGB 值, 不变动透明度 ) + * @param color int color + * @param lightValue 变亮 ( 变浅 ) 值 + * @return 变亮 ( 变浅 ) 后的颜色值 + */ + public static int setLight(final int color, final int lightValue) { + int red = red(color); + int green = green(color); + int blue = blue(color); + // 进行变浅, 变亮 ( 累加 ) + red += lightValue; + green += lightValue; + blue += lightValue; + // 颜色值 + int colorTemp = color; + // 进行设置 + colorTemp = setRed(colorTemp, clamp(red, 255, 0)); + colorTemp = setGreen(colorTemp, clamp(green, 255, 0)); + colorTemp = setBlue(colorTemp, clamp(blue, 255, 0)); + return colorTemp; + } + + /** + * 设置透明度加深 + * @param colorStr color String + * @param darkValue 加深值 + * @return 透明度加深后的颜色值 + */ + public static int setAlphaDark(final String colorStr, final int darkValue) { + int color = parseColor(colorStr); + if (color == -1) return -1; + return setAlphaDark(color, darkValue); + } + + /** + * 设置透明度加深 + * @param color int color + * @param darkValue 加深值 + * @return 透明度加深后的颜色值 + */ + public static int setAlphaDark(final int color, final int darkValue) { + int alpha = alpha(color); + // 透明度加深 + alpha += darkValue; + // 进行设置 + return setAlpha(color, clamp(alpha, 255, 0)); + } + + /** + * 设置透明度变浅 + * @param colorStr color String + * @param lightValue 变浅值 + * @return 透明度变浅后的颜色值 + */ + public static int setAlphaLight(final String colorStr, final int lightValue) { + int color = parseColor(colorStr); + if (color == -1) return -1; + return setAlphaLight(color, lightValue); + } + + /** + * 设置透明度变浅 + * @param color int color + * @param lightValue 变浅值 + * @return 透明度变浅后的颜色值 + */ + public static int setAlphaLight(final int color, final int lightValue) { + int alpha = alpha(color); + // 透明度变浅 + alpha -= lightValue; + // 进行设置 + return setAlpha(color, clamp(alpha, 255, 0)); + } + + // = + + // 颜色字典集合 + private static final HashMap sColorNameMaps; + + static { + sColorNameMaps = new HashMap<>(); + sColorNameMaps.put("transparent", TRANSPARENT); + sColorNameMaps.put("white", WHITE); + sColorNameMaps.put("black", BLACK); + sColorNameMaps.put("red", RED); + sColorNameMaps.put("green", GREEN); + sColorNameMaps.put("blue", BLUE); + sColorNameMaps.put("gray", GRAY); + sColorNameMaps.put("grey", GRAY); + sColorNameMaps.put("skyblue", SKYBLUE); + sColorNameMaps.put("orange", ORANGE); + sColorNameMaps.put("gold", GOLD); + sColorNameMaps.put("pink", PINK); + sColorNameMaps.put("fuchsia", FUCHSIA); + sColorNameMaps.put("graywhite", GRAYWHITE); + sColorNameMaps.put("purple", PURPLE); + sColorNameMaps.put("cyan", CYAN); + sColorNameMaps.put("yellow", YELLOW); + sColorNameMaps.put("chocolate", CHOCOLATE); + sColorNameMaps.put("tomato", TOMATO); + sColorNameMaps.put("orangered", ORANGERED); + sColorNameMaps.put("silver", SILVER); + sColorNameMaps.put("darkgray", DKGRAY); + sColorNameMaps.put("lightgray", LTGRAY); + sColorNameMaps.put("lightgrey", LTGRAY); + sColorNameMaps.put("magenta", MAGENTA); + sColorNameMaps.put("highlight", HIGHLIGHT); + sColorNameMaps.put("lowlight", LOWLIGHT); + sColorNameMaps.put("aqua", 0xFF00FFFF); + sColorNameMaps.put("lime", 0xFF00FF00); + sColorNameMaps.put("maroon", 0xFF800000); + sColorNameMaps.put("navy", 0xFF000080); + sColorNameMaps.put("olive", 0xFF808000); + sColorNameMaps.put("teal", 0xFF008080); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = NumberUtils = + // =============== + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + private static float percentF(final int value, final int max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return (float) value / (float) max; + } + + /** + * 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max + * @param value 指定值 + * @param max 最大值 + * @param min 最小值 + * @return 介于 max、min 之间的 value + */ + private static int clamp(final int value, final int max, final int min) { + return value > max ? max : value < min ? min : value; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/ConvertUtils.java b/lib/DevJava/src/main/java/dev/utils/common/ConvertUtils.java new file mode 100644 index 0000000000..b16a3efe6d --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/ConvertUtils.java @@ -0,0 +1,1824 @@ +package dev.utils.common; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Arrays; + +import dev.utils.JCLogUtils; + +/** + * detail: 转换工具类 (Byte、Hex 等 ) + * @author Ttt + *
+ *     byte 是字节数据类型、有符号型的、占 1 个字节、大小范围为 [ -128 - 127]
+ *     当大于 127 时则开始缩进 127 = 127, 128 = -128, 129 = -127
+ *     char 是字符数据类型、无符号型的、占 2 个字节 (unicode 码 )、大小范围为 [0 - 65535]
+ *     

+ * Binary( 二进制 ) toBinaryString + * Oct( 八进制 ) + * Dec( 十进制 ) + * Hex( 十六进制 ) 以 0x 开始的数据表示十六进制 + *

+ * 位移加密: bytesBitwiseAND(byte[] bytes) + * @see
+ *
+ */ +public final class ConvertUtils { + + private ConvertUtils() { + } + + // 日志 TAG + private static final String TAG = ConvertUtils.class.getSimpleName(); + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + // 用于建立十六进制字符的输出的大写字符数组 + private static final char[] HEX_DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * Object 转换所需类型对象 + * @param object Object + * @param 泛型 + * @return Object convert T object + */ + public static T convert(final Object object){ + try { + return (T) object; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "convert"); + } + return null; + } + + // = + + /** + * char[] 转 String + * @param data char[] + * @return {@link String} + */ + public static String toString(final char[] data) { + return toString(data, null); + } + + /** + * byte[] 转 String + * @param data byte[] + * @return {@link String} + */ + public static String toString(final byte[] data) { + return toString(data, null); + } + + /** + * char[] 转 String + * @param data char[] + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + public static String toString(final char[] data, final String defaultStr) { + if (length(data) == 0) { + try { + return new String(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + } + return defaultStr; + } + + /** + * byte[] 转 String + * @param data byte[] + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + public static String toString(final byte[] data, final String defaultStr) { + if (data != null) { + try { + return new String(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + } + return defaultStr; + } + + // = + + /** + * char 转 String + * @param data char + * @return {@link String} + */ + public static String toString(final char data) { + return toString(data, null); + } + + /** + * byte 转 String + * @param data byte + * @return {@link String} + */ + public static String toString(final byte data) { + return toString(data, null); + } + + /** + * char 转 String + * @param data char + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + public static String toString(final char data, final String defaultStr) { + try { + return Character.toString(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + return defaultStr; + } + + /** + * byte 转 String + * @param data byte + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + public static String toString(final byte data, final String defaultStr) { + try { + return Byte.toString(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + return defaultStr; + } + + // = + + /** + * Object 转 String + * @param object Object + * @return {@link String} + */ + public static String toString(final Object object) { + return toString(object, null); + } + + /** + * Object 转 String + * @param object Object + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + public static String toString(final Object object, final String defaultStr) { + if (object != null) { + try { + if (object instanceof String) { + return (String) object; + } else { + Class clazz = object.getClass(); + // 判断是否数组类型 + if (clazz.isArray()) { + // = 基本数据类型 = + if (clazz.isAssignableFrom(int[].class)) { + return Arrays.toString((int[]) object); + } else if (clazz.isAssignableFrom(boolean[].class)) { + return Arrays.toString((boolean[]) object); + } else if (clazz.isAssignableFrom(long[].class)) { + return Arrays.toString((long[]) object); + } else if (clazz.isAssignableFrom(double[].class)) { + return Arrays.toString((double[]) object); + } else if (clazz.isAssignableFrom(float[].class)) { + return Arrays.toString((float[]) object); + } else if (clazz.isAssignableFrom(byte[].class)) { + return Arrays.toString((byte[]) object); + } else if (clazz.isAssignableFrom(char[].class)) { + return Arrays.toString((char[]) object); + } else if (clazz.isAssignableFrom(short[].class)) { + return Arrays.toString((short[]) object); + } + // = 基本类型封装 = + if (clazz.isAssignableFrom(Integer[].class)) { + return Arrays.toString((Integer[]) object); + } else if (clazz.isAssignableFrom(Boolean[].class)) { + return Arrays.toString((Boolean[]) object); + } else if (clazz.isAssignableFrom(Long[].class)) { + return Arrays.toString((Long[]) object); + } else if (clazz.isAssignableFrom(Double[].class)) { + return Arrays.toString((Double[]) object); + } else if (clazz.isAssignableFrom(Float[].class)) { + return Arrays.toString((Float[]) object); + } else if (clazz.isAssignableFrom(Byte[].class)) { + return Arrays.toString((Byte[]) object); + } else if (clazz.isAssignableFrom(Character[].class)) { + return Arrays.toString((Character[]) object); + } else if (clazz.isAssignableFrom(Short[].class)) { + return Arrays.toString((Short[]) object); + } + } + return object.toString(); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + } + return defaultStr; + } + + // = + + /** + * 字符串 转 int + * @param str String + * @return int + */ + public static int toInt(final String str) { + return toInt(str, 0); + } + + /** + * 字符串 转 int + * @param str String + * @param defaultValue 默认值 + * @return int 如果转换失败, 则返回 defaultValue + */ + public static int toInt(final String str, final int defaultValue) { + if (str == null) return defaultValue; + try { + return Integer.parseInt(str); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toInt"); + } + return defaultValue; + } + + /** + * 字符串 转 boolean + * @param str String + * @return boolean + */ + public static boolean toBoolean(final String str) { + return toBoolean(str, false); + } + + /** + * 字符串 转 boolean + * @param str String + * @param defaultValue 默认值 + * @return boolean 如果转换失败, 则返回 defaultValue + */ + public static boolean toBoolean(final String str, final boolean defaultValue) { + if (str == null) return defaultValue; + try { + // 判断是否 0 + return str.equalsIgnoreCase("true") || str.equalsIgnoreCase("1"); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toBoolean"); + } + return defaultValue; + } + + /** + * 字符串 转 float + * @param str String + * @return float + */ + public static float toFloat(final String str) { + return toFloat(str, 0f); + } + + /** + * 字符串 转 float + * @param str String + * @param defaultValue 默认值 + * @return float 如果转换失败, 则返回 defaultValue + */ + public static float toFloat(final String str, final float defaultValue) { + if (str == null) return defaultValue; + try { + return Float.parseFloat(str); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toFloat"); + } + return defaultValue; + } + + /** + * 字符串 转 double + * @param str String + * @return double + */ + public static double toDouble(final String str) { + return toDouble(str, 0d); + } + + /** + * 字符串 转 double + * @param str String + * @param defaultValue 默认值 + * @return double 如果转换失败, 则返回 defaultValue + */ + public static double toDouble(final String str, final double defaultValue) { + if (str == null) return defaultValue; + try { + return Double.parseDouble(str); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toDouble"); + } + return defaultValue; + } + + /** + * 字符串 转 long + * @param str String + * @return long + */ + public static long toLong(final String str) { + return toLong(str, 0L); + } + + /** + * 字符串 转 long + * @param str String + * @param defaultValue 默认值 + * @return long 如果转换失败, 则返回 defaultValue + */ + public static long toLong(final String str, final long defaultValue) { + if (str == null) return defaultValue; + try { + return Long.parseLong(str); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toLong"); + } + return defaultValue; + } + + // ============ + // = 转换对象 = + // ============ + + /** + * 基本类型对象 转 int + * @param value Integer + * @return int + */ + public static int toInt(final Integer value) { + return toInt(value, 0); + } + + /** + * 基本类型对象 转 int + * @param value Integer + * @param defaultValue 默认值 + * @return int 如果转换失败, 则返回 defaultValue + */ + public static int toInt(final Integer value, final int defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toInt"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 boolean + * @param value Boolean + * @return boolean 如果转换失败, 则返回 defaultValue + */ + public static boolean toBoolean(final Boolean value) { + return toBoolean(value, false); + } + + /** + * 基本类型对象 转 boolean + * @param value Boolean + * @param defaultValue 默认值 + * @return boolean 如果转换失败, 则返回 defaultValue + */ + public static boolean toBoolean(final Boolean value, final boolean defaultValue) { + if (value == null) return defaultValue; + return value; + } + + /** + * 基本类型对象 转 float + * @param value Float + * @return float 如果转换失败, 则返回 defaultValue + */ + public static float toFloat(final Float value) { + return toFloat(value, 0f); + } + + /** + * 基本类型对象 转 float + * @param value Float + * @param defaultValue 默认值 + * @return float 如果转换失败, 则返回 defaultValue + */ + public static float toFloat(final Float value, final float defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toFloat"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 double + * @param value Double + * @return double 如果转换失败, 则返回 defaultValue + */ + public static double toDouble(final Double value) { + return toDouble(value, 0d); + } + + /** + * 基本类型对象 转 double + * @param value Double + * @param defaultValue 默认值 + * @return double 如果转换失败, 则返回 defaultValue + */ + public static double toDouble(final Double value, final double defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toDouble"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 long + * @param value Long + * @return long 如果转换失败, 则返回 defaultValue + */ + public static long toLong(final Long value) { + return toLong(value, 0L); + } + + /** + * 基本类型对象 转 long + * @param value Long + * @param defaultValue 默认值 + * @return long 如果转换失败, 则返回 defaultValue + */ + public static long toLong(final Long value, final long defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toLong"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 short + * @param value Short + * @return short 如果转换失败, 则返回 defaultValue + */ + public static short toShort(final Short value) { + return toShort(value, (short) 0); + } + + /** + * 基本类型对象 转 short + * @param value Short + * @param defaultValue 默认值 + * @return short 如果转换失败, 则返回 defaultValue + */ + public static short toShort(final Short value, final short defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toShort"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 char + * @param value Character + * @return char 如果转换失败, 则返回 defaultValue + */ + public static char toChar(final Character value) { + return toChar(value, (char) 0); + } + + /** + * 基本类型对象 转 char + * @param value Character + * @param defaultValue 默认值 + * @return char 如果转换失败, 则返回 defaultValue + */ + public static char toChar(final Character value, final char defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toChar"); + } + return defaultValue; + } + + /** + * 基本类型对象 转 byte + * @param value Byte + * @return byte 如果转换失败, 则返回 defaultValue + */ + public static byte toByte(final Byte value) { + return toByte(value, (byte) 0); + } + + /** + * 基本类型对象 转 byte + * @param value Byte + * @param defaultValue 默认值 + * @return byte 如果转换失败, 则返回 defaultValue + */ + public static byte toByte(final Byte value, final byte defaultValue) { + if (value == null) return defaultValue; + try { + return value; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toByte"); + } + return defaultValue; + } + + // ======== + // = 其他 = + // ======== + + /** + * 字符串 获取 char ( 默认第一位 ) + * @param str String + * @param defaultValue 默认值 + * @return 第一位值, 如果获取失败则返回 defaultValue + */ + public static char toChar(final String str, final char defaultValue) { + return toChar(str, 0, defaultValue); + } + + /** + * 字符串 获取 char + * @param str String + * @param pos 索引 + * @param defaultValue 默认值 + * @return 指定索引的值, 如果获取失败则返回 defaultValue + */ + public static char toChar(final String str, final int pos, final char defaultValue) { + if (str == null || pos < 0) return defaultValue; + try { + return str.charAt(pos); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toChar"); + } + return defaultValue; + } + + // = + + /** + * char 转换 unicode 编码 + * @param value char + * @return int + */ + public static int toCharInt(final char value) { + return (int) value; + } + + /** + * 字符串 获取 char[] + * @param str String + * @return char[] + */ + public static char[] toChars(final String str) { + if (str != null) { + try { + return str.toCharArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toChars"); + } + } + return null; + } + + /** + * 字符串 获取 byte[] + * @param str String + * @return byte[] + */ + public static byte[] toBytes(final String str) { + if (str != null) { + try { + return str.getBytes(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toBytes"); + } + } + return null; + } + + // = + + /** + * 字符串转换对应的进制 + *
+     *     如: parseInt("1f603", 16) = 128515
+     * 
+ * @param str String + * @param radix 进制 + * @return 对应进制的值 + */ + public static int parseInt(final String str, final int radix) { + if (str == null) return -1; + try { + return Integer.parseInt(str, radix); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseInt"); + } + return -1; + } + + /** + * 字符串转换对应的进制 + * @param str String + * @param radix 进制 + * @return 对应进制的值 + */ + public static long parseLong(final String str, final int radix) { + if (str == null) return -1; + try { + return Long.parseLong(str, radix); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseLong"); + } + return -1; + } + + // = + + /** + * 将 short 转换成字节数组 + * @param data short + * @return byte[] + */ + public static byte[] valueOf(final short data) { + try { + byte[] bytes = new byte[2]; + for (int i = 0; i < 2; i++) { + int offset = (bytes.length - 1 - i) * 8; + bytes[i] = (byte) ((data >>> offset) & 0xff); + } + return bytes; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "valueOf"); + } + return null; + } + + /** + * 将 int 转换成字节数组 + * @param data int + * @return byte[] + */ + public static byte[] valueOf(final int data) { + try { + byte[] bytes = new byte[4]; + for (int i = 0; i < 4; i++) { + int offset = (bytes.length - 1 - i) * 8; + bytes[i] = (byte) ((data >>> offset) & 0xFF); + } + return bytes; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "valueOf"); + } + return null; + } + + // = + + /** + * byte[] 转为 Object + * @param bytes byte[] + * @return {@link Object} + */ + public static Object bytesToObject(final byte[] bytes) { + if (bytes == null) return null; + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); + return ois.readObject(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToObject"); + } finally { + if (ois != null) { + try { + ois.close(); + } catch (Exception e) { + } + } + } + return null; + } + + /** + * Object 转为 byte[] + * @param object Object + * @return byte[] + */ + public static byte[] objectToBytes(final Object object) { + if (object == null) return null; + ObjectOutputStream oos = null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(baos); + oos.writeObject(object); + return baos.toByteArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "objectToBytes"); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (Exception e) { + } + } + } + return null; + } + + // = + + /** + * byte[] 转换 char[], 并且进行补码 + * @param data byte[] + * @return char[] + */ + public static char[] bytesToChars(final byte[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + try { + char[] chars = new char[len]; + for (int i = 0; i < len; i++) { + chars[i] = (char) (data[i] & 0xff); + } + return chars; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToChars"); + } + return null; + } + + /** + * char[] 转换 byte[] + * @param data char[] + * @return byte[] + */ + public static byte[] charsToBytes(final char[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + try { + byte[] bytes = new byte[len]; + for (int i = 0; i < len; i++) { + bytes[i] = (byte) (data[i]); + } + return bytes; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "charsToBytes"); + } + return null; + } + + // ============================================== + // = (int、double、long、float)[] 转换 String[] = + // ============================================== + + /** + * int[] 转换 string[] + * @param datas int[] + * @return String[] + */ + public static String[] intsToStrings(final int[] datas) { + return intsToStrings(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 string[] + * @param off 起始值 + * @param datas int[] + * @return String[] + */ + public static String[] intsToStrings(final int off, final int[] datas) { + return intsToStrings(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 string[] + * @param off 起始值 + * @param length 所需长度 + * @param datas int[] + * @return String[] + */ + public static String[] intsToStrings(final int off, final int length, final int[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + String[] strings = new String[length - off]; + for (int i = 0, len = strings.length; i < len; i++) { + strings[i] = datas[off + i] + ""; + } + return strings; + } + + // = + + /** + * double[] 转换 string[] + * @param datas double[] + * @return String[] + */ + public static String[] doublesToStrings(final double[] datas) { + return doublesToStrings(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * double[] 转换 string[] + * @param off 起始值 + * @param datas double[] + * @return String[] + */ + public static String[] doublesToStrings(final int off, final double[] datas) { + return doublesToStrings(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * double[] 转换 string[] + * @param off 起始值 + * @param length 所需长度 + * @param datas double[] + * @return String[] + */ + public static String[] doublesToStrings(final int off, final int length, final double[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + String[] strings = new String[length - off]; + for (int i = 0, len = strings.length; i < len; i++) { + strings[i] = datas[off + i] + ""; + } + return strings; + } + + // = + + /** + * long[] 转换 string[] + * @param datas long[] + * @return String[] + */ + public static String[] longsToStrings(final long[] datas) { + return longsToStrings(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * long[] 转换 string[] + * @param off 起始值 + * @param datas long[] + * @return String[] + */ + public static String[] longsToStrings(final int off, final long[] datas) { + return longsToStrings(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * long[] 转换 string[] + * @param off 起始值 + * @param length 所需长度 + * @param datas long[] + * @return String[] + */ + public static String[] longsToStrings(final int off, final int length, final long[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + String[] strings = new String[length - off]; + for (int i = 0, len = strings.length; i < len; i++) { + strings[i] = datas[off + i] + ""; + } + return strings; + } + + // = + + /** + * float[] 转换 string[] + * @param datas float[] + * @return String[] + */ + public static String[] floatsToStrings(final float[] datas) { + return floatsToStrings(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * float[] 转换 string[] + * @param off 起始值 + * @param datas float[] + * @return String[] + */ + public static String[] floatsToStrings(final int off, final float[] datas) { + return floatsToStrings(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * float[] 转换 string[] + * @param off 起始值 + * @param length 所需长度 + * @param datas float[] + * @return String[] + */ + public static String[] floatsToStrings(final int off, final int length, final float[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + String[] strings = new String[length - off]; + for (int i = 0, len = strings.length; i < len; i++) { + strings[i] = datas[off + i] + ""; + } + return strings; + } + + // ====================================== + // = int[] 转换 (double、long、float)[] = + // ====================================== + + /** + * int[] 转换 double[] + * @param datas int[] + * @return double[] + */ + public static double[] intsToDoubles(final int[] datas) { + return intsToDoubles(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 double[] + * @param off 起始值 + * @param datas int[] + * @return double[] + */ + public static double[] intsToDoubles(final int off, final int[] datas) { + return intsToDoubles(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 double[] + * @param off 起始值 + * @param length 所需长度 + * @param datas int[] + * @return double[] + */ + public static double[] intsToDoubles(final int off, final int length, final int[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + double[] doubles = new double[length - off]; + for (int i = 0, len = doubles.length; i < len; i++) { + doubles[i] = datas[off + i]; + } + return doubles; + } + + // = + + /** + * int[] 转换 long[] + * @param datas int[] + * @return long[] + */ + public static long[] intsToLongs(final int[] datas) { + return intsToLongs(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 long[] + * @param off 起始值 + * @param datas int[] + * @return long[] + */ + public static long[] intsToLongs(final int off, final int[] datas) { + return intsToLongs(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 long[] + * @param off 起始值 + * @param length 所需长度 + * @param datas int[] + * @return long[] + */ + public static long[] intsToLongs(final int off, final int length, final int[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + long[] longs = new long[length - off]; + for (int i = 0, len = longs.length; i < len; i++) { + longs[i] = datas[off + i]; + } + return longs; + } + + // = + + /** + * int[] 转换 float[] + * @param datas int[] + * @return float[] + */ + public static float[] intsToFloats(final int[] datas) { + return intsToFloats(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 float[] + * @param off 起始值 + * @param datas int[] + * @return float[] + */ + public static float[] intsToFloats(final int off, final int[] datas) { + return intsToFloats(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * int[] 转换 float[] + * @param off 起始值 + * @param length 所需长度 + * @param datas int[] + * @return float[] + */ + public static float[] intsToFloats(final int off, final int length, final int[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + float[] floats = new float[length - off]; + for (int i = 0, len = floats.length; i < len; i++) { + floats[i] = datas[off + i]; + } + return floats; + } + + // ============================================== + // = String[] 转换 (int、double、long、float)[] = + // ============================================== + + /** + * string[] 转换 int[] + * @param datas String[] + * @return int[] + */ + public static int[] stringsToInts(final String... datas) { + return stringsToInts(0, (datas != null) ? datas.length : 0, -1, datas); + } + + /** + * string[] 转换 int[] + * @param off 起始值 + * @param datas String[] + * @return int[] + */ + public static int[] stringsToInts(final int off, final String... datas) { + return stringsToInts(off, (datas != null) ? datas.length : 0, -1, datas); + } + + /** + * string[] 转换 int[] + * @param off 起始值 + * @param length 所需长度 + * @param datas String[] + * @return int[] + */ + public static int[] stringsToInts(final int off, final int length, final String... datas) { + return stringsToInts(off, length, -1, datas); + } + + /** + * string[] 转换 int[] + * @param off 起始值 + * @param length 所需长度 + * @param errorValue 转换错误使用值 + * @param datas String[] + * @return int[] + */ + public static int[] stringsToInts(final int off, final int length, final int errorValue, final String... datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + int[] ints = new int[length - off]; + for (int i = 0, len = ints.length; i < len; i++) { + try { + ints[i] = Integer.parseInt(datas[off + i]); + } catch (Exception e) { + ints[i] = errorValue; + } + } + return ints; + } + + // = + + /** + * string[] 转换 double[] + * @param datas String[] + * @return double[] + */ + public static double[] stringsToDoubles(final String... datas) { + return stringsToDoubles(0, (datas != null) ? datas.length : 0, -1d, datas); + } + + /** + * string[] 转换 double[] + * @param off 起始值 + * @param datas String[] + * @return double[] + */ + public static double[] stringsToDoubles(final int off, final String... datas) { + return stringsToDoubles(off, (datas != null) ? datas.length : 0, -1d, datas); + } + + /** + * string[] 转换 double[] + * @param off 起始值 + * @param length 所需长度 + * @param datas String[] + * @return double[] + */ + public static double[] stringsToDoubles(final int off, final int length, final String... datas) { + return stringsToDoubles(off, length, -1d, datas); + } + + /** + * string[] 转换 double[] + * @param off 起始值 + * @param length 所需长度 + * @param errorValue 转换错误使用值 + * @param datas String[] + * @return double[] + */ + public static double[] stringsToDoubles(final int off, final int length, final double errorValue, final String... datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + double[] doubles = new double[length - off]; + for (int i = 0, len = doubles.length; i < len; i++) { + try { + doubles[i] = Double.parseDouble(datas[off + i]); + } catch (Exception e) { + doubles[i] = errorValue; + } + } + return doubles; + } + + // = + + /** + * string[] 转换 long[] + * @param datas String[] + * @return long[] + */ + public static long[] stringsToLongs(final String... datas) { + return stringsToLongs(0, (datas != null) ? datas.length : 0, -1L, datas); + } + + /** + * string[] 转换 long[] + * @param off 起始值 + * @param datas String[] + * @return long[] + */ + public static long[] stringsToLongs(final int off, final String... datas) { + return stringsToLongs(off, (datas != null) ? datas.length : 0, -1L, datas); + } + + /** + * string[] 转换 long[] + * @param off 起始值 + * @param length 所需长度 + * @param datas String[] + * @return long[] + */ + public static long[] stringsToLongs(final int off, final int length, final String... datas) { + return stringsToLongs(off, length, -1L, datas); + } + + /** + * string[] 转换 long[] + * @param off 起始值 + * @param length 所需长度 + * @param errorValue 转换错误使用值 + * @param datas String[] + * @return long[] + */ + public static long[] stringsToLongs(final int off, final int length, final long errorValue, final String... datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + long[] longs = new long[length - off]; + for (int i = 0, len = longs.length; i < len; i++) { + try { + longs[i] = Long.parseLong(datas[off + i]); + } catch (Exception e) { + longs[i] = errorValue; + } + } + return longs; + } + + // = + + /** + * string[] 转换 float[] + * @param datas String[] + * @return float[] + */ + public static float[] stringsToFloats(final String... datas) { + return stringsToFloats(0, (datas != null) ? datas.length : 0, -1f, datas); + } + + /** + * string[] 转换 float[] + * @param off 起始值 + * @param datas String[] + * @return float[] + */ + public static float[] stringsToFloats(final int off, final String... datas) { + return stringsToFloats(off, (datas != null) ? datas.length : 0, -1f, datas); + } + + /** + * string[] 转换 float[] + * @param off 起始值 + * @param length 所需长度 + * @param datas String[] + * @return float[] + */ + public static float[] stringsToFloats(final int off, final int length, final String... datas) { + return stringsToFloats(off, length, -1f, datas); + } + + /** + * string[] 转换 float[] + * @param off 起始值 + * @param length 所需长度 + * @param errorValue 转换错误使用值 + * @param datas String[] + * @return float[] + */ + public static float[] stringsToFloats(final int off, final int length, final float errorValue, final String... datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + float[] floats = new float[length - off]; + for (int i = 0, len = floats.length; i < len; i++) { + try { + floats[i] = Float.parseFloat(datas[off + i]); + } catch (Exception e) { + floats[i] = errorValue; + } + } + return floats; + } + + // ====================================== + // = (double、long、float)[] 转换 int[] = + // ====================================== + + /** + * double[] 转换 int[] + * @param datas double[] + * @return int[] + */ + public static int[] doublesToInts(final double[] datas) { + return doublesToInts(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * double[] 转换 int[] + * @param off 起始值 + * @param datas double[] + * @return int[] + */ + public static int[] doublesToInts(final int off, final double[] datas) { + return doublesToInts(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * double[] 转换 int[] + * @param off 起始值 + * @param length 所需长度 + * @param datas double[] + * @return int[] + */ + public static int[] doublesToInts(final int off, final int length, final double[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + int[] ints = new int[length - off]; + for (int i = 0, len = ints.length; i < len; i++) { + try { + ints[i] = (int) datas[off + i]; + } catch (Exception e) { + } + } + return ints; + } + + // = + + /** + * long[] 转换 int[] + * @param datas long[] + * @return int[] + */ + public static int[] longsToInts(final long[] datas) { + return longsToInts(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * long[] 转换 int[] + * @param off 起始值 + * @param datas long[] + * @return int[] + */ + public static int[] longsToInts(final int off, final long[] datas) { + return longsToInts(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * long[] 转换 int[] + * @param off 起始值 + * @param length 所需长度 + * @param datas long[] + * @return int[] + */ + public static int[] longsToInts(final int off, final int length, final long[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + int[] ints = new int[length - off]; + for (int i = 0, len = ints.length; i < len; i++) { + try { + ints[i] = (int) datas[off + i]; + } catch (Exception e) { + } + } + return ints; + } + + // = + + /** + * float[] 转换 int[] + * @param datas float[] + * @return int[] + */ + public static int[] floatsToInts(final float[] datas) { + return floatsToInts(0, (datas != null) ? datas.length : 0, datas); + } + + /** + * float[] 转换 int[] + * @param off 起始值 + * @param datas float[] + * @return int[] + */ + public static int[] floatsToInts(final int off, final float[] datas) { + return floatsToInts(off, (datas != null) ? datas.length : 0, datas); + } + + /** + * float[] 转换 int[] + * @param off 起始值 + * @param length 所需长度 + * @param datas float[] + * @return int[] + */ + public static int[] floatsToInts(final int off, final int length, final float[] datas) { + if (off < 0 || length < 1 || off >= length || datas == null || length > datas.length) + return null; + int[] ints = new int[length - off]; + for (int i = 0, len = ints.length; i < len; i++) { + try { + ints[i] = (int) datas[off + i]; + } catch (Exception e) { + } + } + return ints; + } + + // =================== + // = Binary - 二进制 = + // =================== + + /** + * 将 字节转换 为 二进制字符串 + * @param datas byte[] + * @return 二进制字符串 + */ + public static String toBinaryString(final byte... datas) { + if (datas == null || datas.length == 0) return null; + try { + StringBuilder builder = new StringBuilder(); + for (byte value : datas) { + for (int j = 7; j >= 0; --j) { + builder.append(((value >> j) & 0x01) == 0 ? '0' : '1'); + } + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToBits"); + } + return null; + } + + /** + * 二进制字符串 转换 byte[] 解码 + *
+     *     例: "011000010111001101100100" 传入 decodeBinary, 返回 byte[], 通过 new String(byte()) 获取配合 toBinaryString 使用
+     * 
+ * @param str String + * @return 解码后的 byte[] + */ + public static byte[] decodeBinary(final String str) { + if (str == null) return null; + try { + String data = str; + int lenMod = data.length() % 8; + int byteLen = data.length() / 8; + // add "0" until length to 8 times + if (lenMod != 0) { + for (int i = lenMod; i < 8; i++) { + data = "0" + data; + } + byteLen++; + } + byte[] bytes = new byte[byteLen]; + for (int i = 0; i < byteLen; ++i) { + for (int j = 0; j < 8; ++j) { + bytes[i] <<= 1; + bytes[i] |= data.charAt(i * 8 + j) - '0'; + } + } + return bytes; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bitsToBytes"); + } + return null; + } + + // ====================== + // = Hex - 十六进制处理 = + // ====================== + + /** + * 判断是否十六进制数据 + * @param data 待检验数据 + * @return {@code true} yes, {@code false} no + */ + public static boolean isHex(final String data) { + if (data == null) return false; + // 获取数据长度 + int len = data.length(); + if (len > 0) { + for (int i = len - 1; i >= 0; i--) { + char c = data.charAt(i); + if (!(c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f')) { + return false; + } + } + return true; + } + return false; + } + + /** + * 将十六进制字节数组解码 + * @param data 十六进制 byte[] + * @return 十六进制转 ( 解 ) 码后的数据 + */ + public static byte[] decodeHex(final byte[] data) { + return decodeHex((length(data) == 0) ? null : bytesToChars(data)); + } + + /** + * 将十六进制字符串解码 + * @param str 十六进制 String + * @return 十六进制转 ( 解 ) 码后的数据 + */ + public static byte[] decodeHex(final String str) { + return decodeHex(isEmpty(str) ? null : str.toCharArray()); + } + + /** + * 将十六进制字符数组解码 + * @param data 十六进制 char[] + * @return 十六进制转 ( 解 ) 码后的数据 + */ + public static byte[] decodeHex(final char[] data) { + if (data == null) return null; + try { + int len = data.length; + byte[] out = new byte[len >> 1]; + // 十六进制由两个字符组成 + for (int i = 0, j = 0; j < len; i++) { + int d = toDigit(data[j], j) << 4; + j++; + d = d | toDigit(data[j], j); + j++; + out[i] = (byte) (d & 0xFF); + } + return out; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "decodeHex"); + } + return null; + } + + /** + * 十六进制 char 转换 int + * @param hexChar 十六进制 char + * @return 十六进制转 ( 解 ) 码后的整数 + */ + public static int hexToInt(final char hexChar) { + if (hexChar >= '0' && hexChar <= '9') { + return hexChar - '0'; + } else if (hexChar >= 'A' && hexChar <= 'F') { + return hexChar - 'A' + 10; + } else { + throw new IllegalArgumentException(); + } + } + + /** + * 将十六进制字符转换成一个整数 + * @param ch 十六进制 char + * @param index 十六进制字符在字符数组中的位置 + * @return 一个整数 + * @throws Exception 当 ch 不是一个合法的十六进制字符时, 抛出运行时异常 + */ + private static int toDigit(final char ch, final int index) throws Exception { + int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new Exception(String.format("Illegal hexadecimal character %s at index %s", ch, index)); + } + return digit; + } + + // = + + // toHexString(0x1f603) = 1f603 + // parseInt("1f603", 16) = 128515 + // toHexString(128515) = 1f603 + + /** + * int 转换十六进制 + *
+     *     如: toHexString(0x1f603) 返回: 1f603
+     * 
+ * @param value int + * @return 十六进制字符串 + */ + public static String toHexString(final int value) { + try { + return Integer.toHexString(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + /** + * long 转换十六进制 + * @param value long + * @return 十六进制字符串 + */ + public static String toHexString(final long value) { + try { + return Long.toHexString(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + /** + * double 转换十六进制 + * @param value double + * @return 十六进制字符串 + */ + public static String toHexString(final double value) { + try { + return Double.toHexString(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + /** + * float 转换十六进制 + * @param value float + * @return 十六进制字符串 + */ + public static String toHexString(final float value) { + try { + return Float.toHexString(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // = + + /** + * 将 string 转换为 十六进制 char[] + * @param str String + * @return 十六进制 char[] + */ + public static char[] toHexChars(final String str) { + return toHexChars(str, true); + } + + /** + * 将 string 转换为 十六进制 char[] + * @param str String + * @param toLowerCase {@code true} 小写格式, {@code false} 大写格式 + * @return 十六进制 char[] + */ + public static char[] toHexChars(final String str, final boolean toLowerCase) { + return toHexChars(isEmpty(str) ? null : str.getBytes(), toLowerCase ? HEX_DIGITS : HEX_DIGITS_UPPER); + } + + // = + + /** + * 将 byte[] 转换为 十六进制 char[] + * @param data byte[] + * @return 十六进制 char[] + */ + public static char[] toHexChars(final byte[] data) { + return toHexChars(data, true); + } + + /** + * 将 byte[] 转换为 十六进制 char[] + * @param data byte[] + * @param toLowerCase {@code true} 小写格式, {@code false} 大写格式 + * @return 十六进制 char[] + */ + public static char[] toHexChars(final byte[] data, final boolean toLowerCase) { + return toHexChars(data, toLowerCase ? HEX_DIGITS : HEX_DIGITS_UPPER); + } + + /** + * 将 byte[] 转换为 十六进制 char[] + * @param data byte[] + * @param hexDigits {@link #HEX_DIGITS}、{@link #HEX_DIGITS_UPPER} + * @return 十六进制 char[] + */ + private static char[] toHexChars(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + return toHexString(data, hexDigits).toCharArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexChars"); + } + return null; + } + + // = + + /** + * 将 string 转换 十六进制字符串 + * @param str 待转换数据 + * @return 十六进制字符串 + */ + public static String toHexString(final String str) { + return toHexString(str, true); + } + + /** + * 将 string 转换 十六进制字符串 + * @param str 待转换数据 + * @param toLowerCase {@code true} 小写格式, {@code false} 大写格式 + * @return 十六进制字符串 + */ + public static String toHexString(final String str, final boolean toLowerCase) { + return toHexString(isEmpty(str) ? null : str.getBytes(), toLowerCase ? HEX_DIGITS : HEX_DIGITS_UPPER); + } + + // = + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制字符串 + */ + public static String toHexString(final byte[] data) { + return toHexString(data, true); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param toLowerCase {@code true} 小写格式, {@code false} 大写格式 + * @return 十六进制字符串 + */ + public static String toHexString(final byte[] data, final boolean toLowerCase) { + return toHexString(data, toLowerCase ? HEX_DIGITS : HEX_DIGITS_UPPER); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS}、{@link #HEX_DIGITS_UPPER} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // = + +// String data = "test"; +// // 转换二进制字符串 +// String result = toBinaryString(data.getBytes()); +// // 获取二进制数据 +// byte[] bytes = result.getBytes(); +// // 位移编码 +// bytesBitwiseAND(bytes); +// // = +// // 位移解码 +// bytesBitwiseAND(bytes); +// // 二进制数据解码 +// byte[] byteResult = decodeBinary(new String(bytes)); +// // 转换为原始数据 +// String data1 = new String(byteResult); +// // 判断是否一致 +// boolean equals = data.equals(data1); + + /** + * 按位求补 byte[] 位移编解码 ( 共用同一个方法 ) + * @param data byte[] + */ + public static void bytesBitwiseAND(final byte[] data) { + if (data == null) return; + for (int i = 0, len = data.length; i < len; i++) { + int d = data[i]; + d = ~d; // 按位补运算符, 翻转操作数的每一位, 即 0 变成 1, 1 变成 0, 再通过反转后的二进制初始化回十六进制 + data[i] = (byte) d; + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + // ============== + // = ArrayUtils = + // ============== + + /** + * 获取数组长度 + * @param objects object[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + private static int length(final Object... objects) { + return objects != null ? objects.length : 0; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/CoordinateUtils.java b/lib/DevJava/src/main/java/dev/utils/common/CoordinateUtils.java new file mode 100644 index 0000000000..93b22d6e80 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/CoordinateUtils.java @@ -0,0 +1,165 @@ +package dev.utils.common; + +import static java.lang.Math.PI; + +/** + * detail: 坐标 (GPS 纠偏 ) 相关工具类 + * @author Ttt + *
+ *     地球坐标系 (WGS-84)
+ *     火星坐标系 (GCJ-02)
+ *     百度坐标系 (BD09)
+ *     

+ * @see
+ *

+ * 1. WGS84 坐标系: 即地球坐标系, 国际上通用的坐标系, 设备一般包含 GPS 芯片或者北斗芯片获取的经纬度为 WGS84 地理坐标系 + * 谷歌地图采用的是 WGS84 地理坐标系 ( 中国范围除外 ) GPS 设备得到的经纬度就是在 WGS84 坐标系下的经纬度, 通常通过底层接口得到的定位信息都是 WGS84 坐标系 + *

+ * 2. GCJ02 坐标系: 即火星坐标系, 是由中国国家测绘局制订的地理信息系统的坐标系统, 由 WGS84 坐标系经加密后的坐标系 + * 国家规定, 中国大陆所有公开地理数据都需要至少用 GCJ-02 进行加密, 也就是说我们从国内公司的产品中得到的数据, 一定是经过了加密的 + * 绝大部分国内互联网地图提供商都是使用 GCJ-02 坐标系, 包括高德地图, 谷歌地图中国区等 + *

+ * 3. BD09 坐标系: 即百度坐标系, 其在 GCJ-02 上多增加了一次变换, 用来保护用户隐私, 从百度产品中得到的坐标都是 BD-09 坐标系 + *
+ */ +public final class CoordinateUtils { + + private CoordinateUtils() { + } + + private static final double X_PI = 3.14159265358979324 * 3000.0 / 180.0; + private static final double A = 6378245.0; + private static final double EE = 0.00669342162296594323; + + /** + * BD09 坐标转 GCJ02 坐标 + * @param lng BD09 坐标纬度 + * @param lat BD09 坐标经度 + * @return GCJ02 坐标 [ 经度, 纬度 ] + */ + public static double[] bd09ToGcj02(final double lng, final double lat) { + double x = lng - 0.0065; + double y = lat - 0.006; + double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI); + double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI); + double gg_lng = z * Math.cos(theta); + double gg_lat = z * Math.sin(theta); + return new double[]{gg_lng, gg_lat}; + } + + /** + * GCJ02 坐标转 BD09 坐标 + * @param lng GCJ02 坐标经度 + * @param lat GCJ02 坐标纬度 + * @return BD09 坐标 [ 经度, 纬度 ] + */ + public static double[] gcj02ToBd09(final double lng, final double lat) { + double z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * X_PI); + double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * X_PI); + double bd_lng = z * Math.cos(theta) + 0.0065; + double bd_lat = z * Math.sin(theta) + 0.006; + return new double[]{bd_lng, bd_lat}; + } + + /** + * GCJ02 坐标转 WGS84 坐标 + * @param lng GCJ02 坐标经度 + * @param lat GCJ02 坐标纬度 + * @return WGS84 坐标 [ 经度, 纬度 ] + */ + public static double[] gcj02ToWGS84(final double lng, final double lat) { + if (outOfChina(lng, lat)) return new double[]{lng, lat}; + double dlat = transformLat(lng - 105.0, lat - 35.0); + double dlng = transformLng(lng - 105.0, lat - 35.0); + double radlat = lat / 180.0 * PI; + double magic = Math.sin(radlat); + magic = 1 - EE * magic * magic; + double sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((A * (1 - EE)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (A / sqrtmagic * Math.cos(radlat) * PI); + double mglat = lat + dlat; + double mglng = lng + dlng; + return new double[]{lng * 2 - mglng, lat * 2 - mglat}; + } + + /** + * WGS84 坐标转 GCJ02 坐标 + * @param lng WGS84 坐标经度 + * @param lat WGS84 坐标纬度 + * @return GCJ02 坐标 [ 经度, 纬度 ] + */ + public static double[] wgs84ToGcj02(final double lng, final double lat) { + if (outOfChina(lng, lat)) return new double[]{lng, lat}; + double dlat = transformLat(lng - 105.0, lat - 35.0); + double dlng = transformLng(lng - 105.0, lat - 35.0); + double radlat = lat / 180.0 * PI; + double magic = Math.sin(radlat); + magic = 1 - EE * magic * magic; + double sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((A * (1 - EE)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (A / sqrtmagic * Math.cos(radlat) * PI); + double mglat = lat + dlat; + double mglng = lng + dlng; + return new double[]{mglng, mglat}; + } + + /** + * BD09 坐标转 WGS84 坐标 + * @param lng BD09 坐标经度 + * @param lat BD09 坐标纬度 + * @return WGS84 坐标 [ 经度, 纬度 ] + */ + public static double[] bd09ToWGS84(final double lng, final double lat) { + double[] gcj = bd09ToGcj02(lng, lat); + return gcj02ToWGS84(gcj[0], gcj[1]); + } + + /** + * WGS84 坐标转 BD09 坐标 + * @param lng WGS84 坐标经度 + * @param lat WGS84 坐标纬度 + * @return BD09 坐标 [ 经度, 纬度 ] + */ + public static double[] wgs84ToBd09(final double lng, final double lat) { + double[] gcj = wgs84ToGcj02(lng, lat); + return gcj02ToBd09(gcj[0], gcj[1]); + } + + /** + * 转换经度 + * @param lng 经度 + * @param lat 纬度 + * @return 转换后的经度 + */ + private static double transformLat(final double lng, final double lat) { + double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0; + ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0; + return ret; + } + + /** + * 转换纬度 + * @param lng 经度 + * @param lat 纬度 + * @return 转换后的纬度 + */ + private static double transformLng(final double lng, final double lat) { + double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0; + ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0; + return ret; + } + + /** + * 判断是否中国境外 + * @param lng 经度 + * @param lat 纬度 + * @return {@code true} yes, {@code false} no + */ + public static boolean outOfChina(final double lng, final double lat) { + return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/DateUtils.java b/lib/DevJava/src/main/java/dev/utils/common/DateUtils.java new file mode 100644 index 0000000000..9350b4d0fe --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/DateUtils.java @@ -0,0 +1,1133 @@ +package dev.utils.common; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 日期工具类 + * @author Ttt + */ +public final class DateUtils { + + private DateUtils() { + } + + // 日志 TAG + private static final String TAG = DateUtils.class.getSimpleName(); + + // ================ + // = 日期格式类型 = + // ================ + + public static final String yyyy = "yyyy"; + public static final String yyyyMMdd = "yyyy-MM-dd"; + public static final String yyyyMMdd2 = "yyyyMMdd"; + public static final String yyyyMMdd3 = "yyyy年MM月dd日"; + public static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; + public static final String yyyyMMddHHmmss_2 = "yyyy年M月d日 HH:mm:ss"; + public static final String MMdd = "MM-dd"; + public static final String MMdd2 = "MM月dd日"; + public static final String MMdd3 = "MMdd"; + public static final String HHmm = "HH:mm"; + public static final String HHmm2 = "HHmm"; + public static final String HHmmss = "HH:mm:ss"; + public static final String HHmmss2 = "HHmmss"; + public static final String hhmmMMDDyyyy = "hh:mm M月d日 yyyy"; + public static final String hhmmssMMDDyyyy = "hh:mm:ss M月d日 yyyy"; + + // 一分钟 60 秒 + public static final int MINUTE_S = 60; + // 一小时 60 * 60 秒 + public static final int HOUR_S = 3600; + // 一天 24 * 60 * 60 秒 + public static final int DAY_S = 86400; + + // 秒与毫秒的倍数 + public static final long SEC = 1000; + // 分与毫秒的倍数 + public static final long MIN = SEC * 60; + // 时与毫秒的倍数 + public static final long HOUR = MIN * 60; + // 天与毫秒的倍数 + public static final long DAY = HOUR * 24; + // 周与毫秒的倍数 + public static final long WEEK = DAY * 7; + // 月与毫秒的倍数 + public static final long MONTH = DAY * 30; + // 年与毫秒的倍数 + public static final long YEAR = DAY * 365; + + /** + * 获取当前日期的字符串 - yyyy-MM-dd HH:mm:ss + * @return 当前日期 yyyy-MM-dd HH:mm:ss 格式字符串 + */ + public static String getDateNow() { + return getDateNow(yyyyMMddHHmmss); + } + + /** + * 获取当前日期的字符串 + * @param format 日期格式, 如: yyyy-MM-dd HH:mm:ss + * @return 当前日期指定格式字符串 + */ + public static String getDateNow(final String format) { + if (format == null) return null; + try { + return new SimpleDateFormat(format).format(Calendar.getInstance().getTime()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDateNow"); + } + return null; + } + + // = + + /** + * 将时间戳转换日期字符串 + * @param time 时间戳 + * @param format 日期格式 + * @return 按照指定格式的日期字符串 + */ + public static String formatTime(final long time, final String format) { + if (format == null) return null; + try { + return new SimpleDateFormat(format).format(time); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "formatTime"); + } + return null; + } + + /** + * 将 Date 转换日期字符串 + * @param date 日期 + * @param format 日期格式 + * @return 按照指定格式的日期字符串 + */ + public static String formatDate(final Date date, final String format) { + if (date == null || format == null) return null; + try { + return new SimpleDateFormat(format).format(date); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "formatDate"); + } + return null; + } + + // = + + /** + * 将时间戳转换成 Date + * @param time 时间戳 + * @return {@link Date} + */ + public static Date parseDate(final long time) { + try { + return new Date(time); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseDate"); + } + return null; + } + + /** + * 将日期字符串转换为 Date - 默认表示 time 属于 yyyy-MM-dd HH:mm:ss 格式 + * @param time 时间 + * @return {@link Date} + */ + public static Date parseDate(final String time) { + return parseDate(time, yyyyMMddHHmmss); + } + + /** + * 将日期字符串转换为 Date + * @param time 时间 + * @param format 日期格式 + * @return {@link Date} + */ + public static Date parseDate(final String time, final String format) { + if (time == null || format == null) return null; + try { + return new SimpleDateFormat(format).parse(time); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseDate"); + } + return null; + } + + // = + + /** + * 解析时间字符串转换为 long 毫秒 - 默认表示 time 属于 yyyy-MM-dd HH:mm:ss 格式 + * @param time 时间 + * @return 毫秒时间 + */ + public static long parseLong(final String time) { + return parseLong(time, yyyyMMddHHmmss); + } + + /** + * 解析时间字符串转换为 long 毫秒 + * @param time 时间 + * @param format 时间的格式 + * @return 毫秒时间 + */ + public static long parseLong(final String time, final String format) { + if (time == null || format == null) return 0L; + try { + // 按规定的时间格式, 进行格式化时间, 并且获取 long 时间毫秒, 返回毫秒时间 + return new SimpleDateFormat(format).parse(time).getTime(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseLong"); + } + return 0L; + } + + /** + * 转换时间为指定字符串 + * @param time 需要转换的时间 + * @param timeFormat time 的时间格式 + * @param format 把 time 转换成需要的格式 + * @return 转换指定格式的时间字符串 + */ + public static String parseToString(final String time, final String timeFormat, final String format) { + if (time != null && timeFormat != null && format != null) { + try { + Date date = parseDate(time, timeFormat); + // 把时间转为所需格式字符串 + return formatDate(date, format); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "parseToString"); + } + } + return null; + } + + // = + + /** + * 获取时间差 - 分钟 + * @param time 毫秒 + * @return 分钟 + */ + public static int getTimeDiffMinute(final long time) { + return (int) (time / 60000); // 60 * 1000 + } + + /** + * 获取时间差 - 小时 + * @param time 毫秒 + * @return 小时 + */ + public static int getTimeDiffHour(final long time) { + return (int) (time / 3600000); // 60 * 1000 * 60 + } + + /** + * 获取时间差 - 天 + * @param time 毫秒 + * @return 天 + */ + public static int getTimeDiffDay(final long time) { + return (int) (time / 86400000); // 60 * 1000 * 60 * 24 + } + + /** + * 获取时间差 - ( 传入时间 - 当前时间 ) + * @param time 毫秒 + * @return 与当前时间的时间差 ( 毫秒 ) + */ + public static long getTimeDiff(final long time) { + return time - System.currentTimeMillis(); + } + + /** + * 获取时间差 + * @param time1 时间 yyyy-MM-dd HH:mm:ss 格式 + * @param time2 对比时间 yyyy-MM-dd HH:mm:ss 格式 + * @return 时间差 ( 毫秒 ) + */ + public static long getTimeDiff(final String time1, final String time2) { + long timeLong1 = parseLong(time1); + long timeLong2 = parseLong(time2); + if (timeLong1 > 1L && timeLong2 > 1L) { + return timeLong1 - timeLong2; + } + return -2L; + } + + /** + * 获取时间差 + * @param time1 时间 + * @param timeFormat1 时间格式 + * @param time2 对比时间 + * @param timeFormat2 对比时间格式 + * @return 时间差 ( 毫秒 ) + */ + public static long getTimeDiff(final String time1, final String timeFormat1, final String time2, final String timeFormat2) { + long timeLong1 = parseLong(time1, timeFormat1); + long timeLong2 = parseLong(time2, timeFormat2); + if (timeLong1 > 1L && timeLong2 > 1L) { + return timeLong1 - timeLong2; + } + return -2L; + } + + // ============ + // = 获取时间 = + // ============ + + /** + * 获取年 + * @param date 日期 + * @return 年 + */ + public static int getYear(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.YEAR); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getYear"); + } + return -1; + } + + /** + * 获取月 (0 - 11) + 1 + * @param date 日期 + * @return 月 + */ + public static int getMonth(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.MONTH) + 1; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getMonth"); + } + return -1; + } + + /** + * 获取日 + * @param date 日期 + * @return 日 + */ + public static int getDay(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.DAY_OF_MONTH); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDay"); + } + return -1; + } + + /** + * 获取日期是星期几 + * @param date 日期 + * @return 日 + */ + public static int getWeek(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int week = calendar.get(Calendar.DAY_OF_WEEK); + return week; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getWeek"); + } + return -1; + } + + /** + * 获取时 - 24 + * @param date 日期 + * @return 时 + */ + public static int get24Hour(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.HOUR_OF_DAY); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get24Hour"); + } + return -1; + } + + /** + * 获取时 - 12 + * @param date 日期 + * @return 时 + */ + public static int get12Hour(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.HOUR); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get12Hour"); + } + return -1; + } + + /** + * 获取分 + * @param date 日期 + * @return 分 + */ + public static int getMinute(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.MINUTE); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getMinute"); + } + return -1; + } + + /** + * 获取秒 + * @param date 日期 + * @return 秒 + */ + public static int getSecond(final Date date) { + if (date == null) return -1; + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.SECOND); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getSecond"); + } + return -1; + } + + /** + * 转换时间处理, 小于 10, 则自动补充 0x + * @param time 待处理时间 + * @return 自动补 0 时间字符串 + */ + public static String convertTime(final int time) { + return convertTime(time, true); + } + + /** + * 转换时间处理, 小于 10, 则自动补充 0x + * @param time 待处理时间 + * @param append 判断是否需要自动补 0 + * @return 自动补 0 时间字符串 + */ + public static String convertTime(final int time, final boolean append) { + if (append) { + int timeTmpe = time; + // 防止出现负数 + timeTmpe = Math.max(0, timeTmpe); + return timeTmpe >= 10 ? timeTmpe + "" : "0" + timeTmpe; + } + return time + ""; + } + + // = + + /** + * 获取年 + * @return 年 + */ + public static int getYear() { + return Calendar.getInstance().get(Calendar.YEAR); + } + + /** + * 获取月 (0 - 11) + 1 + * @return 月 + */ + public static int getMonth() { + int month = Calendar.getInstance().get(Calendar.MONTH) + 1; + return month; + } + + /** + * 获取日 + * @return 日 + */ + public static int getDay() { + return Calendar.getInstance().get(Calendar.DATE); + } + + /** + * 获取星期数 + * @return 星期 + */ + public static int getWeek() { + return Calendar.getInstance().get(Calendar.DAY_OF_WEEK); + } + + /** + * 获取时 - 24 + * @return 24 小时制小时 + */ + public static int get24Hour() { + return Calendar.getInstance().get(Calendar.HOUR_OF_DAY); + } + + /** + * 获取时 - 12 + * @return 12 小时制小时 + */ + public static int get12Hour() { + return Calendar.getInstance().get(Calendar.HOUR); + } + + /** + * 获取分 + * @return 分钟 + */ + public static int getMinute() { + return Calendar.getInstance().get(Calendar.MINUTE); + } + + /** + * 获取秒 + * @return 秒数 + */ + public static int getSecond() { + return Calendar.getInstance().get(Calendar.SECOND); + } + + // = + + /** + * 判断是否闰年 + * @param year 年数 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLeapYear(final int year) { + // 判断是否闰年 + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; + } + + /** + * 根据年份、月份, 获取对应的天数 ( 完整天数, 无判断是否属于未来日期 ) + * @param year 年数 + * @param month 月份 + * @return 指定年份所属的月份的天数 + */ + public static int getMonthDayNumberAll(final int year, final int month) { + int number = 31; + // 判断返回的标识数字 + switch (month) { + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + number = 31; + break; + case 2: + if (isLeapYear(year)) { + number = 29; + } else { + number = 28; + } + break; + case 4: + case 6: + case 9: + case 11: + number = 30; + break; + } + return number; + } + + /** + * 根据年份, 获取对应的月份 + * @param year 年份 + * @return 内部判断是否相同一年, 不能超过限制未来的月份 + */ + public static int getYearMonthNumber(final int year) { + // 如: 当前 2019-01, 传入 2019 则返回 1 + // 传入 2018 返回 12, 不会返回未来的月份 + if (year == getYear()) { + return getMonth(); + } + return 12; + } + + /** + * 根据年份、月份, 获取对应的天数 + * @param year 年份 + * @param month 月份 + * @return 内部判断是否相同一年、月份, 不能超过限制未来的天数 + */ + public static int getMonthDayNumber(final int year, final int month) { + // 判断年份, 相同则判断月份 + if (year == getYear()) { + // 判断月份, 先同则返回天数 + if (getYearMonthNumber(year) == month) { + return getDay(); + } + } + return getMonthDayNumberAll(year, month); + } + + // = + + /** + * 生成 HH 按时间排序数组 + * @return 按小时排序的数组 + */ + public static String[] getArrayToHH() { + List lists = getListToHH(); + return lists.toArray(new String[lists.size()]); + } + + /** + * 生成 HH 按时间排序集合 + * @return 按小时排序的集合 + */ + public static List getListToHH() { + List lists = new ArrayList<>(); + for (int i = 0; i < 24; i++) { + lists.add(convertTime(i, true)); + } + return lists; + } + + /** + * 生成 MM 按时间排序数组 + * @return 按分钟排序的数组 + */ + public static String[] getArrayToMM() { + List lists = getListToMM(); + return lists.toArray(new String[lists.size()]); + } + + /** + * 生成 MM 按时间排序集合 + * @return 按分钟排序的集合 + */ + public static List getListToMM() { + List lists = new ArrayList<>(); + for (int i = 0; i < 60; i++) { + lists.add(convertTime(i, true)); + } + return lists; + } + + /** + * 生成 HH:mm 按间隔时间排序数组 + * @param type 0 = 00:00 - 23:00 = 每 1 小时间隔 + * 1 = 00:00 - 23:45 = 每 15 分钟间隔 + * 2 = 00:00 - 23:30 = 每 30 分钟间隔 + * @return 指定格式的数组 + */ + public static String[] getArrayToHHMM(final int type) { + List lists = getListToHHMM(type); + return lists.toArray(new String[lists.size()]); + } + + /** + * 生成 HH:mm 按间隔时间排序集合 + * @param type 0 = 00:00 - 23:00 = 每 1 小时间隔 + * 1 = 00:00 - 23:45 = 每 15 分钟间隔 + * 2 = 00:00 - 23:30 = 每 30 分钟间隔 + * @return 指定格式的集合 + */ + public static List getListToHHMM(final int type) { + List lists = new ArrayList<>(); + switch (type) { + case 0: + for (int i = 0; i < 24; i++) { + lists.add(convertTime(i, true) + ":00"); + } + break; + case 1: + for (int i = 0; i < 96; i++) { // 00 15 30 45 = 4 (24 * 4) + if (i % 2 == 0) { // 判断是否偶数 00、30 + // 小时数 + String hour = convertTime(i / 4, true); + // 分钟数 + String minute = i % 4 == 0 ? "00" : "30"; + // 累加时间 + lists.add(hour + ":" + minute); + } else { // 15、45 + // 小时数 + String hour = convertTime(i / 4, true); + // 分钟数 + String minute = (i - 1) % 4 == 0 ? "15" : "45"; + // 累加时间 + lists.add(hour + ":" + minute); + } + } + break; + case 2: + for (int i = 0; i < 48; i++) { // 00 30 = 2 (24 * 2) + // 小时处理 + int hour = i / 2; + // 属于偶数 + if (i % 2 == 0) { + lists.add(convertTime(hour, true) + ":00"); + } else { + lists.add(convertTime(hour, true) + ":30"); + } + } + break; + } + return lists; + } + + /** + * 获取 HH:mm 按间隔时间排序的集合中, 指定时间所在索引 + * @param time HH:mm 格式 + * @param type 0 = 00:00 - 23:00 = 每 1 小时间隔 + * 1 = 00:00 - 23:45 = 每 15 分钟间隔 + * 2 = 00:00 - 23:30 = 每 30 分钟间隔 + * @return 指定数据, 在对应格式类型内的索引 + */ + public static int getListToHHMMPosition(final String time, final int type) { + if (time != null && time.length() != 0) { + // 进行拆分 + String[] timeSplit = time.split(":"); + if (timeSplit != null && timeSplit.length == 2) { + // 转换小时 + int hour = toInt(timeSplit[0], -1); + // 判断是否小于 0 + if (hour < 0) { + return -1; + } else if (hour > 24) { + return -1; + } + + // 判断格式, 进行格式处理 + switch (type) { + case 0: + return hour; + case 1: + case 2: + // 转换分钟 + int minute = toInt(timeSplit[1], -1); + // 判断是否小于 0 + if (minute < 0) { + return -1; + } else if (minute > 59) { + return -1; + } + // 判断间隔 + if (type == 1) { + if (minute >= 0 && minute < 15) { + return hour * 4; + } else if (minute >= 15 && minute < 30) { + return hour * 4 + 1; + } else if (minute >= 30 && minute < 45) { + return hour * 4 + 2; + } else if (minute >= 30 && minute < 60) { + return hour * 4 + 3; + } + } else if (type == 2) { // 30 分钟一个间隔 + // 大于等于 30, 表示属于基数 + if (minute >= 30) { // 属于奇数 (30), 需要加 1 + return hour * 2 + 1; + } else { + return hour * 2; + } + } + break; + } + } + } + return -1; + } + + // = + + /** + * 传入时间, 获取时间 (00:00:00 格式 ) - 不处理大于一天 + * @param time 时间 ( 秒为单位 ) + * @return 转换 (00:00:00 格式 ) 时间字符串 + */ + public static String secToTimeRetain(final int time) { + return secToTimeRetain(time, false); + } + + /** + * 传入时间, 获取时间 (00:00:00 格式 ) + * @param time 时间 ( 秒为单位 ) + * @param isHandlerMDay 是否处理大于一天的时间 + * @return 转换 (00:00:00 格式 ) 时间字符串 + */ + public static String secToTimeRetain(final int time, final boolean isHandlerMDay) { + try { + if (time <= 0) { + return "00:00:00"; + } else { + // 取模 + int rSecond; + int rMinute; + // 差数 + int dSecond; + int dMinute; + int dHour; + // 转换时间格式 + if (time < MINUTE_S) { // 小于一分钟 + return "00:00:" + ((time >= 10) ? time : ("0" + time)); + } else if (time >= MINUTE_S && time < HOUR_S) { // 小于一小时 + dSecond = time % MINUTE_S; // 取模分钟, 获取多出的秒数 + dMinute = (time - dSecond) / MINUTE_S; + return "00:" + ((dMinute >= 10) ? dMinute : ("0" + dMinute)) + ":" + ((dSecond >= 10) ? dSecond : ("0" + dSecond)); + } else if (time >= HOUR_S && time < DAY_S) { // 小于等于一天 + rMinute = time % HOUR_S; // 取模小时, 获取多出的分钟 + dHour = (time - rMinute) / HOUR_S; // 获取小时 + dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 + dMinute = dSecond / MINUTE_S; // 获取多出的分钟 + rSecond = dSecond % MINUTE_S; // 取模分钟, 获取多余的秒数 + return ((dHour >= 10) ? dHour : ("0" + dHour)) + ":" + ((dMinute >= 10) ? dMinute : ("0" + dMinute)) + ":" + ((rSecond >= 10) ? rSecond : "0" + rSecond); + } else { // 多余的时间, 直接格式化 + // 大于一天的情况 + if (isHandlerMDay) { + rMinute = time % HOUR_S; // 取模小时, 获取多出的分钟 + dHour = (time - rMinute) / HOUR_S; // 获取小时 + dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 + dMinute = dSecond / MINUTE_S; // 获取多出的分钟 + rSecond = dSecond % MINUTE_S; // 取模分钟, 获取多余的秒数 + return ((dHour >= 10) ? dHour : ("0" + dHour)) + ":" + ((dMinute >= 10) ? dMinute : ("0" + dMinute)) + ":" + ((rSecond >= 10) ? rSecond : "0" + rSecond); + } + } + } + } catch (Exception e) { + } + return null; + } + + /** + * 传入时间, 时间参数 ( 小时、分钟、秒 ) + * @param time 时间 ( 秒为单位 ) + * @return int[] { 小时、分钟、秒 } + */ + public static int[] convertTimeArys(final int time) { + try { + if (time <= 0) { + return new int[]{0, 0, 0}; + } else { + // 取模 + int rSecond; + int rMinute; + // 差数 + int dSecond; + int dMinute; + int dHour; + // 转换时间格式 + if (time < MINUTE_S) { // 小于一分钟 + return new int[]{0, 0, time}; + } else if (time >= MINUTE_S && time < HOUR_S) { // 小于一小时 + dSecond = time % MINUTE_S; // 取模分钟, 获取多出的秒数 + dMinute = (time - dSecond) / MINUTE_S; + return new int[]{0, dMinute, dSecond}; + } else if (time >= HOUR_S && time < DAY_S) { // 小于等于一天 + rMinute = time % HOUR_S; // 取模小时, 获取多出的分钟 + dHour = (time - rMinute) / HOUR_S; // 获取小时 + dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 + dMinute = dSecond / MINUTE_S; // 获取多出的分钟 + rSecond = dSecond % MINUTE_S; // 取模分钟, 获取多余的秒数 + return new int[]{dHour, dMinute, rSecond}; + } else { // 多余的时间, 直接格式化 + // 大于一天的情况 + rMinute = time % HOUR_S; // 取模小时, 获取多出的分钟 + dHour = (time - rMinute) / HOUR_S; // 获取小时 + dSecond = (time - dHour * HOUR_S); // 获取多出的秒数 + dMinute = dSecond / MINUTE_S; // 获取多出的分钟 + rSecond = dSecond % MINUTE_S; // 取模分钟, 获取多余的秒数 + return new int[]{dHour, dMinute, rSecond}; + } + } + } catch (Exception e) { + } + return null; + } + + /** + * 转换时间 + * @param millis 时间毫秒 + * @param precision precision = 0, return null + * precision = 1, return 天 + * precision = 2, return 天, 小时 + * precision = 3, return 天, 小时, 分钟 + * precision = 4, return 天, 小时, 分钟, 秒 + * precision = 5, return 天, 小时, 分钟, 秒, 毫秒 + * @return 转换指定格式的时间字符串 + */ + public static String millisToFitTimeSpan(final long millis, final int precision) { + if (millis <= 0 || precision <= 0) return null; + + long millisTime = millis; + int precisionFormat = precision; + + StringBuilder builder = new StringBuilder(); + String[] units = {"天", "小时", "分钟", "秒", "毫秒"}; + int[] unitLen = {86400000, 3600000, 60000, 1000, 1}; + precisionFormat = Math.min(precisionFormat, 5); + for (int i = 0; i < precisionFormat; i++) { + if (millisTime >= unitLen[i]) { + long mode = millisTime / unitLen[i]; + millisTime -= mode * unitLen[i]; + builder.append(mode).append(units[i]); + } + } + return builder.toString(); + } + + /** + * 转换时间为数组 + * @param millis 时间毫秒 + * @return int[5] { 天, 小时, 分钟, 秒, 毫秒 } + */ + public static int[] millisToTimeArys(final long millis) { + if (millis <= 0) return null; + int[] timeArys = new int[5]; + int[] unitLen = {86400000, 3600000, 60000, 1000, 1}; + long millisTime = millis; + for (int i = 0; i < 5; i++) { + if (millisTime >= unitLen[i]) { + long mode = millisTime / unitLen[i]; + millisTime -= mode * unitLen[i]; + timeArys[i] = (int) mode; + } + } + return timeArys; + } + + // ====================== + // = 判断是否在区间范围 = + // ====================== + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTimeHHmm(final String startTime, final String endTime) { + return isInTime(formatTime(System.currentTimeMillis(), HHmm), startTime, endTime, HHmm); + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTimeHHmm(final String nowTime, final String startTime, final String endTime) { + return isInTime(nowTime, startTime, endTime, HHmm); + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTimeHHmmss(final String startTime, final String endTime) { + return isInTime(formatTime(System.currentTimeMillis(), HHmmss), startTime, endTime, HHmmss); + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTimeHHmmss(final String nowTime, final String startTime, final String endTime) { + return isInTime(nowTime, startTime, endTime, HHmmss); + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param format 时间格式 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTime(final String nowTime, final String startTime, final String endTime, final String format) { + if (nowTime == null || startTime == null || endTime == null || format == null) return false; + try { + // 格式化日期 + SimpleDateFormat sdf = new SimpleDateFormat(format); + // 当前时间转换 + long now = sdf.parse(nowTime).getTime(); + // 开始时间转换 + long start = sdf.parse(startTime).getTime(); + // 结束时间转换 + long end = sdf.parse(endTime).getTime(); + // 判断结束时间是否小于开始时间 + if (end < start) { // 结束属于第二天区域 + if (now >= start || now <= end) { + return true; + } + } else { + if (now >= start && now <= end) { + return true; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isInTime"); + } + return false; + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInTime(final long nowTime, final long startTime, final long endTime) { + return isInDate(new Date(nowTime), new Date(startTime), new Date(endTime)); + } + + /** + * 判断时间是否在 [startTime, endTime] 区间, 注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return {@code true} yes, {@code false} no + */ + public static boolean isInDate(final Date nowTime, final Date startTime, final Date endTime) { + if (nowTime == null || startTime == null || endTime == null) { + return false; + } else if (nowTime.getTime() == startTime.getTime() || nowTime.getTime() == endTime.getTime()) { + return true; + } + // 当前时间 + Calendar now = Calendar.getInstance(); + now.setTime(nowTime); + // 开始时间 + Calendar begin = Calendar.getInstance(); + begin.setTime(startTime); + // 结束时间 + Calendar end = Calendar.getInstance(); + end.setTime(endTime); + // 判断是否在 begin 之后的时间, 并且在 end 之前的时间 + return now.after(begin) && now.before(end); + } + + // = + + /** + * 获取指定时间距离该时间第二天的指定时段的时间 ( 判断凌晨情况 ) + * @param endTime 结束时间 HH:mm + * @return 距离指定结束时间还有多少毫秒 + */ + public static long getEndTimeDiffHHmm(final String endTime) { + return getEndTimeDiff(System.currentTimeMillis(), endTime, HHmm); + } + + /** + * 获取指定时间距离该时间第二天的指定时段的时间 ( 判断凌晨情况 ) + * @param startTime 开始时间 + * @param endTime 结束时间 HH:mm + * @return 距离指定结束时间还有多少毫秒 + */ + public static long getEndTimeDiffHHmm(final long startTime, final String endTime) { + return getEndTimeDiff(startTime, endTime, HHmm); + } + + /** + * 获取指定时间距离该时间第二天的指定时段的时间差 ( 判断凌晨情况 ) + * @param endTime 结束时间 + * @param format 格式 如: HH:mm + * @return 距离指定结束时间还有多少毫秒 + */ + public static long getEndTimeDiff(final String endTime, final String format) { + return getEndTimeDiff(System.currentTimeMillis(), endTime, format); + } + + /** + * 获取指定时间距离该时间第二天的指定时段的时间差 ( 判断凌晨情况 ) + *
+     *     如当前时间 2018-12-07 15:27:23, 判断距离 14:39:20 (endTime) 有多久
+     *     如果过了这个时间段, 则返回 2018-12-08 14:39:20 ( 明天的这个时间段时间 )
+     *     如果没有过这个时间段 ( 如: 17:39:20) 则返回当天时间段 2018-12-07 17:39:20 (2018-12-07 + endTime)
+     * 
+ * @param startTime 开始时间 + * @param endTime 结束时间 + * @param format 格式 如: HH:mm + * @return 距离指定结束时间还有多少毫秒 + */ + public static long getEndTimeDiff(final long startTime, final String endTime, final String format) { + if (startTime < 1 || endTime == null || format == null) return -1; + try { + // 判断格式是否加了秒 + boolean isSecond = format.endsWith(":ss"); + // 获取开始时间 + String start = formatTime(startTime, format); + // 转换时间 + int startNumber = Integer.parseInt(start.replace(":", "")); + // 获取结束时间转换 + int endNumber = Integer.parseInt(endTime.replace(":", "")); + // 时间处理 + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date(startTime)); // 设置当前时间 + // 如果当前时间大于结束时间, 表示非第二天 + if (startNumber > endNumber) { + // 时间累加一天 + calendar.add(Calendar.DATE, 1); // 当前日期加一天 + } + // 获取天数时间 + String yyyyMMddDate = formatDate(calendar.getTime(), yyyyMMdd); + // 累加时间 + String yyyyMMddHHmmssDate = yyyyMMddDate + " " + endTime + (isSecond ? "" : ":00"); + // 返回转换后的时间 + return parseLong(yyyyMMddHHmmssDate, yyyyMMddHHmmss); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getEndTimeDiff"); + } + return -1; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + /** + * 字符串 转 int + * @param str String + * @param defaultValue 默认值 + * @return int 如果转换失败, 则返回 defaultValue + */ + private static int toInt(final String str, final int defaultValue) { + if (str == null) return defaultValue; + try { + return Integer.parseInt(str); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toInt"); + } + return defaultValue; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/DevCommonUtils.java b/lib/DevJava/src/main/java/dev/utils/common/DevCommonUtils.java new file mode 100644 index 0000000000..d2d7e7f0f0 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/DevCommonUtils.java @@ -0,0 +1,2162 @@ +package dev.utils.common; + +import java.lang.reflect.Array; +import java.security.MessageDigest; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Map; +import java.util.Random; +import java.util.UUID; + +import dev.utils.JCLogUtils; + +/** + * detail: 开发常用方法工具类 + * @author Ttt + */ +public final class DevCommonUtils { + + private DevCommonUtils() { + } + + // 日志 TAG + private static final String TAG = DevCommonUtils.class.getSimpleName(); + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + + // ================ + // = 计时相关处理 = + // ================ + + /** + * 耗时时间记录 + * @param builder 拼接 Builder + * @param startTime 开始时间 + * @param endTime 结束时间 + */ + public static void timeRecord(final StringBuilder builder, final long startTime, final long endTime) { + timeRecord(builder, null, startTime, endTime); + } + + /** + * 耗时时间记录 + * @param builder 拼接 Builder + * @param title 标题 + * @param startTime 开始时间 + * @param endTime 结束时间 + */ + public static void timeRecord(final StringBuilder builder, final String title, final long startTime, final long endTime) { + if (builder == null) return; + // 使用时间 + long diffTime = endTime - startTime; + // 计算时间 + if (!isEmpty(title)) { + builder.append(NEW_LINE_STR); + builder.append(title); + } + // 计算时间 + builder.append(NEW_LINE_STR + "开始时间: " + formatTime(startTime, yyyyMMddHHmmss)); + builder.append(NEW_LINE_STR + "结束时间: " + formatTime(endTime, yyyyMMddHHmmss)); + builder.append(NEW_LINE_STR + "所用时间(毫秒): " + diffTime); + builder.append(NEW_LINE_STR + "所用时间(秒): " + (diffTime / 1000)); + } + + // = + + /** + * 获取操作时间 + * @param operateTime 操作时间 ( 毫秒 ) + * @return 操作时间 + */ + public static long getOperateTime(final long operateTime) { + return getOperateTime(operateTime, -1); + } + + /** + * 获取操作时间 + * @param operateTime 操作时间 ( 毫秒 ) + * @param randomTime 随机时间范围 ( 毫秒 ) + * @return 操作时间 + */ + public static long getOperateTime(final long operateTime, final int randomTime) { + int random = 0; + // 大于 2 才处理 + if (randomTime >= 2) { + // 随机时间 + random = getRandom(randomTime); + } + // 返回操作时间 + return Math.max(0, operateTime) + random; + } + + /** + * 堵塞操作 + * @param sleepTime 堵塞时间 ( 毫秒 ) + */ + public static void sleepOperate(final long sleepTime) { + sleepOperate(sleepTime, -1); + } + + /** + * 堵塞操作 + * @param sleepTime 堵塞时间 ( 毫秒 ) + * @param randomTime 随机时间范围 ( 毫秒 ) + */ + public static void sleepOperate(final long sleepTime, final int randomTime) { + long time = getOperateTime(sleepTime, randomTime); + if (time > 0) { + try { + Thread.sleep(time); + } catch (Throwable e1) { + JCLogUtils.eTag(TAG, e1, "sleepOperate"); + } + } + } + + // = + + /** + * 判断是否网络资源 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isHttpRes(final String str) { + if (!isEmpty(str)) { + // 属于第一位开始, 才是属于网络资源 + return str.toLowerCase().startsWith("http:") || + str.toLowerCase().startsWith("https:"); + } + return false; + } + + /** + * 循环 MD5 加密处理 + * @param str 待处理数据 + * @param number MD5 加密次数 + * @param isUppercase 是否大写处理 + * @param salts 特殊 salt 拼接 + * @return 循环加密后的字符串 + */ + public static String whileMD5(final String str, final int number, final boolean isUppercase, final String... salts) { + if (str != null && number >= 1) { + int saltLen = (salts != null) ? salts.length : 0; + // 临时字符串 + String dataTemp = str; + // 判断是否大写 + if (isUppercase) { + // 循环加密 + for (int i = 0; i < number; i++) { + if (saltLen >= i) { + String salt = salts[i]; + if (salt != null) { + dataTemp = md5Upper(dataTemp + salt); + } else { + dataTemp = md5Upper(dataTemp); + } + } else { + dataTemp = md5Upper(dataTemp); + } + } + } else { + // 循环加密 + for (int i = 0; i < number; i++) { + if (saltLen >= i) { + String salt = salts[i]; + if (salt != null) { + dataTemp = md5(dataTemp + salt); + } else { + dataTemp = md5(dataTemp); + } + } else { + dataTemp = md5(dataTemp); + } + } + } + } + return str; + } + + // ============== + // = 获取唯一数 = + // ============== + + /** + * 获取随机唯一数 + * @return {@link UUID} + */ + public static UUID randomUUID() { + return UUID.randomUUID(); + } + + /** + * 获取随机唯一数 HashCode + * @return 随机 UUID hashCode + */ + public static int randomUUIDToHashCode() { + return UUID.randomUUID().hashCode(); + } + + /** + * 获取随机唯一数 HashCode + * @param uuid {@link UUID} + * @return 随机 UUID hashCode + */ + public static int randomUUIDToHashCode(final UUID uuid) { + return (uuid != null) ? uuid.hashCode() : 0; + } + + /** + * 获取随机规则生成 UUID + * @return 随机规则生成 UUID + */ + public static UUID getRandomUUID() { + // 获取随机数 + String random1 = (900000 + new Random().nextInt(10000)) + ""; + // 获取随机数 + String random2 = (900000 + new Random().nextInt(10000)) + ""; + // 获取当前时间 + String time = System.currentTimeMillis() + random1 + random2; + // 生成唯一随机 uuid time.hashCode(), random1.hashCode() | random2.hashCode() + UUID randomUUID = new UUID(time.hashCode(), ((long) random1.hashCode() << 32) | random2.hashCode()); + return randomUUID; + } + + /** + * 获取随机规则生成 UUID 字符串 + * @return 随机规则生成 UUID 字符串 + */ + public static String getRandomUUIDToString() { + return getRandomUUID().toString(); + } + + // ============== + // = 字符串处理 = + // ============== + + /** + * 转换手机号 + * @param phone 待处理字符串 + * @return 处理后的字符串 + */ + public static String converHideMobile(final String phone) { + return converHideMobile(phone, "*"); + } + + /** + * 转换手机号 + * @param phone 待处理字符串 + * @param symbol 转换符号 + * @return 处理后的字符串 + */ + public static String converHideMobile(final String phone, final String symbol) { + return converSymbolHide(3, phone, symbol); + } + + /** + * 转换符号处理 + * @param start 开始位置 + * @param str 待处理字符串 + * @param symbol 转换符号 + * @return 处理后的字符串 + */ + public static String converSymbolHide(final int start, final String str, final String symbol) { + if (!isEmpty(str)) { + if (start <= 0) { + return str; + } + // 获取数据长度 + int length = str.length(); + // 如果数据小于 start 位则直接返回 + if (length <= start) { + return str; + } else { // 大于 start 位 + StringBuilder builder = new StringBuilder(); + builder.append(str.substring(0, start)); + int len = length - start; + // 进行平分 + len /= 2; + // 进行遍历保存 + for (int i = 0; i < len; i++) { + builder.append(symbol); + } + builder.append(str.substring(start + len, length)); + return builder.toString(); + } + } + return ""; + } + + // = + + /** + * 裁剪超出的内容, 并且追加符号 ( 如 ...) + * @param maxLength 允许最大的长度 + * @param str 待处理字符串 + * @param symbol 转换符号 + * @return 处理后的字符串 + */ + public static String subEllipsize(final int maxLength, final String str, final String symbol) { + if (maxLength >= 1) { + // 获取内容长度 + int strLength = length(str); + // 防止为不存在数据 + if (strLength != 0) { + if (maxLength >= strLength) { + return str; + } + return str.substring(0, maxLength) + toCheckValue(symbol); + } + } + return ""; + } + + /** + * 裁剪符号处理 + * @param start 开始位置 + * @param symbolNumber 转换数量 + * @param str 待处理字符串 + * @param symbol 转换符号 + * @return 处理后的字符串 + */ + public static String subSymbolHide(final int start, final int symbolNumber, final String str, final String symbol) { + if (!isEmpty(str)) { + if (start <= 0 || symbolNumber <= 0) { + return str; + } + // 获取数据长度 + int length = str.length(); + // 如果数据小于 start 位则直接返回 + if (length <= start) { + return str; + } else { // 大于 start 位 + StringBuilder builder = new StringBuilder(); + builder.append(str.substring(0, start)); + int len = length - start - symbolNumber; + // 如果超出总长度, 则进行控制 + if (len <= 0) { // 表示后面的全部转换 + len = length - start; + } else { // 需要裁剪的数量 + len = symbolNumber; + } + // 进行遍历保存 + for (int i = 0; i < len; i++) { + builder.append(symbol); + } + builder.append(str.substring(start + len, length)); + return builder.toString(); + } + } + return ""; + } + + /** + * 裁剪内容, 设置符号处理 + * @param str 待处理字符串 + * @param frontRetainLength 前面保留的长度 + * @param rearRetainLength 后面保留的长度 + * @param symbol 转换符号 + * @return 处理后的字符串 + */ + public static String subSetSymbol(final String str, final int frontRetainLength, final int rearRetainLength, final String symbol) { + if (str != null) { + try { + + // 截取前面需保留的内容 + String startStr = str.substring(0, frontRetainLength); + // 截取后面需保留的内容 + String endStr = str.substring(str.length() - rearRetainLength); + // 特殊符号长度 + int symbolLength = str.length() - (frontRetainLength + rearRetainLength); + if (symbolLength >= 1) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < symbolLength; i++) { + builder.append(symbol); + } + return startStr + builder.toString() + endStr; + } + return startStr + endStr; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "subSetSymbol"); + } + } + return null; + } + + // ================== + // = 替换、截取操作 = + // ================== + + /** + * 裁剪字符串 + * @param str 需要裁剪的字符串 + * @param endIndex 结束裁剪的位置 + * @return 裁剪后的字符串 + */ + public static String substring(final String str, final int endIndex) { + return substring(str, 0, endIndex, true); + } + + /** + * 裁剪字符串 + * @param str 需要裁剪的字符串 + * @param endIndex 结束裁剪的位置 + * @param isReturn 开始位置超过限制是否返回内容 + * @return 裁剪后的字符串 + */ + public static String substring(final String str, final int endIndex, final boolean isReturn) { + return substring(str, 0, endIndex, isReturn); + } + + /** + * 裁剪字符串 + * @param str 需要裁剪的字符串 + * @param beginIndex 开始裁剪的位置 + * @param endIndex 结束裁剪的位置 + * @param isReturn 开始位置超过限制是否返回内容 + * @return 裁剪后的字符串 + */ + public static String substring(final String str, final int beginIndex, final int endIndex, final boolean isReturn) { + if (!isEmpty(str) && beginIndex >= 0 && endIndex >= 0 && endIndex >= beginIndex) { + // 获取数据长度 + int len = length(str); + // 防止超过限制 + if (beginIndex > len) { + return isReturn ? str : ""; + } + // 防止超过限制 + return str.substring(beginIndex, (endIndex >= len) ? len : endIndex); + } + return isReturn ? str : ""; + } + + // = + + /** + * 替换 ( 删除 - 替换成 "") 字符串中符合 特定标记字符的 startsWith - endsWith + * * 如 _____a_a_a_a________ 传入 _ 等于 ____a_a_a_a____ + * @param str 待处理字符串 + * @param suffix 替换符号字符串 + * @return 处理后的字符串 + */ + public static String toReplaceSEWith(final String str, final String suffix) { + return toReplaceSEWith(str, suffix, ""); + } + + /** + * 替换字符串中符合 特定标记字符的 startsWith - endsWith + * 如 _____a_a_a_a________ 传入 _, c 等于 c____a_a_a_a____c + * @param str 待处理字符串 + * @param suffix 替换匹配内容 + * @param value 需要替换的内容 + * @return 处理后的字符串 + */ + public static String toReplaceSEWith(final String str, final String suffix, final String value) { + try { + if (isEmpty(str) || isEmpty(suffix) || isEmpty(value) || suffix.equals(value)) + return str; + // 获取编辑内容长度 + int kLength = suffix.length(); + // 保存新的 Builder 中, 减少内存开销 + StringBuilder builder = new StringBuilder(str); + // 判断是否在最头部 + if (builder.indexOf(suffix) == 0) { + builder.delete(0, kLength); + // 追加内容 + builder.insert(0, value); + } + // 获取尾部的位置 + int lastIndexOf = -1; + // 数据长度 + int bufLength = -1; + // 判断是否在最尾部 + if ((lastIndexOf = builder.lastIndexOf(suffix)) == ((bufLength = builder.length()) - kLength)) { + builder.delete(lastIndexOf, bufLength); + // 追加内容 + builder.insert(lastIndexOf, value); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toReplaceSEWith"); + } + return str; + } + + // = + + /** + * 替换开头字符串 + * @param str 待处理字符串 + * @param prefix 开头匹配字符串 + * @return 处理后的字符串 + */ + public static String toReplaceStartsWith(final String str, final String prefix) { + return toReplaceStartsWith(str, prefix, ""); + } + + /** + * 替换开头字符串 + * @param str 待处理字符串 + * @param prefix 开头匹配字符串 + * @param startAppend 开头追加的内容 + * @return 处理后的字符串 + */ + public static String toReplaceStartsWith(final String str, final String prefix, final String startAppend) { + if (!isEmpty(str) && !isEmpty(prefix)) { + try { + if (str.startsWith(prefix)) { + return toCheckValue(startAppend) + str.substring(prefix.length()); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toReplaceStartsWith"); + } + } + return str; + } + + /** + * 替换结尾字符串 + * @param str 待处理字符串 + * @param suffix 结尾匹配字符串 + * @return 处理后的字符串 + */ + public static String toReplaceEndsWith(final String str, final String suffix) { + return toReplaceEndsWith(str, suffix, ""); + } + + /** + * 替换结尾字符串 + * @param str 待处理字符串 + * @param suffix 结尾匹配字符串 + * @param value 需要替换的内容 + * @return 处理后的字符串 + */ + public static String toReplaceEndsWith(final String str, final String suffix, final String value) { + if (!isEmpty(str) && !isEmpty(suffix)) { + try { + if (str.endsWith(suffix)) { + return str.substring(0, str.length() - suffix.length()) + value; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toReplaceEndsWith"); + } + } + return str; + } + + // = + + /** + * 这个方法功能主要把字符符合标记的 头部和尾部都替换成 "" + * 如 _____a_a_a_a________ 传入 _ 等于 a_a_a_a + * 替换字符串中符合 特定标记字符的 startsWith(indexOf) - endsWith(lastIndexOf) + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @return 处理后的字符串 + */ + public static String toClearSEWiths(final String str, final String suffix) { + if (isEmpty(str) || isEmpty(suffix)) return str; + try { + // 获取编辑内容长度 + int kLength = suffix.length(); + // 保存新的 Builder 中, 减少内存开销 + StringBuilder builder = new StringBuilder(str); + // 进行循环判断 - 属于最前面的, 才进行处理 + while (builder.indexOf(suffix) == 0) { + builder.delete(0, kLength); + } + // 获取尾部的位置 + int lastIndexOf = -1; + // 数据长度 + int bufLength = -1; + // 进行循环判断 - 属于最后面的, 才进行处理 + while ((lastIndexOf = builder.lastIndexOf(suffix)) == ((bufLength = builder.length()) - kLength)) { + builder.delete(lastIndexOf, bufLength); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toClearSEWiths"); + } + return str; + } + + /** + * 清空属于特定字符串开头的字段 + * 如 _____a_a_a_a________ 传入 _ 等于 a_a_a_a_____ + * 替换字符串中符合 特定标记字符的 endsWith(lastIndexOf) + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @return 处理后的字符串 + */ + public static String toClearStartsWith(final String str, final String suffix) { + if (isEmpty(str) || isEmpty(suffix)) return str; + try { + // 获取编辑内容长度 + int kLength = suffix.length(); + // 保存新的 Builder 中, 减少内存开销 + StringBuilder builder = new StringBuilder(str); + // 进行循环判断 - 属于最前面的, 才进行处理 + while (builder.indexOf(suffix) == 0) { + builder.delete(0, kLength); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toClearStartsWith"); + } + return str; + } + + /** + * 清空属于特定字符串结尾的字段 + * 如 _____a_a_a_a________ 传入 _ 等于 _____a_a_a_a + * 替换字符串中符合 特定标记字符的 endsWith(lastIndexOf) + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @return 处理后的字符串 + */ + public static String toClearEndsWith(final String str, final String suffix) { + if (isEmpty(str) || isEmpty(suffix)) return str; + try { + // 获取编辑内容长度 + int kLength = suffix.length(); + // 保存新的 Builder 中, 减少内存开销 + StringBuilder builder = new StringBuilder(str); + // 获取最后一位位置 + int sLength = 0; + // 进行循环判断 - 属于最前面的, 才进行处理 + while (builder.lastIndexOf(suffix) == ((sLength = builder.length()) - kLength)) { + builder.delete(sLength - kLength, sLength); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toClearEndsWith"); + } + return str; + } + + // = + + /** + * 替换字符串 + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @param replace 替换的内容 + * @return 处理后的字符串 + */ + public static String replaceStr(final String str, final String suffix, final String replace) { + // 如果替换的内容或者判断的字符串为 null, 则直接跳过 + if (!isEmpty(str) && !isEmpty(suffix) && replace != null && !suffix.equals(replace)) { + try { + return str.replaceAll(suffix, replace); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "replaceStr"); + } + } + return str; + } + + /** + * 替换字符串 + * @param str 待处理字符串 + * @param suffix 匹配判断字符串 + * @param replace 替换的内容 + * @return 处理后的字符串, 替换失败则返回 null + */ + public static String replaceStrToNull(final String str, final String suffix, final String replace) { + // 如果替换的内容或者判断的字符串为 null, 则直接跳过 + if (!isEmpty(str) && !isEmpty(suffix) && replace != null && !suffix.equals(replace)) { + try { + return str.replaceAll(suffix, replace); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "replaceStrToNull"); + } + } + return null; + } + + /** + * 替换字符串 + * @param str 内容 + * @param suffixArys 匹配判断字符串数组 + * @param replaceArys 准备替换的字符串数组 + * @return 处理后的字符串 + */ + public static String replaceStrs(final String str, final String[] suffixArys, final String[] replaceArys) { + // 防止数据为 null + if (str != null && suffixArys != null && replaceArys != null) { + String cStr = str; + // 替换的特殊字符串长度 + int spCount = suffixArys.length; + // 替换的内容长度 + int reCount = replaceArys.length; + // 相同才进行处理 + if (spCount == reCount) { + // 遍历进行判断 + for (int i = 0; i < spCount; i++) { + // 进行替换字符串 + cStr = replaceStr(cStr, suffixArys[i], replaceArys[i]); + } + // 最终不为 null, 则进行返回 + return cStr; + } + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = RandomUtils = + // =============== + + /** + * 获取 0 - 最大随机数之间的随机数 + * @param max 最大随机数 + * @return 随机介于 [0, max) 的区间值 + */ + private static int getRandom(final int max) { + return getRandom(0, max); + } + + /** + * 获取两个数之间的随机数 ( 不含最大随机数, 需要 + 1) + * @param min 最小随机数 + * @param max 最大随机数 + * @return 随机介于 [min, max) 的区间值 + */ + private static int getRandom(final int min, final int max) { + if (min > max) { + return 0; + } else if (min == max) { + return min; + } + return min + new Random().nextInt(max - min); + } + + // ============= + // = DateUtils = + // ============= + + private static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; + + /** + * 将时间戳转换日期字符串 + * @param time 时间戳 + * @param format 日期格式 + * @return 按照指定格式的日期字符串 + */ + private static String formatTime(final long time, final String format) { + if (format == null) return null; + try { + return new SimpleDateFormat(format).format(time); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "formatTime"); + } + return null; + } + + // ============ + // = MD5Utils = + // ============ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + // 用于建立十六进制字符的输出的大写字符数组 + private static final char[] HEX_DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * 加密内容 (32 位小写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + private static String md5(final String data) { + if (data == null) return null; + try { + return md5(data.getBytes()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5"); + } + return null; + } + + /** + * 加密内容 (32 位小写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + private static String md5(final byte[] data) { + if (data == null) return null; + try { + // 获取 MD5 摘要算法的 MessageDigest 对象 + MessageDigest digest = MessageDigest.getInstance("MD5"); + // 使用指定的字节更新摘要 + digest.update(data); + // 获取密文 + return toHexString(digest.digest(), HEX_DIGITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5"); + } + return null; + } + + /** + * 加密内容 (32 位大写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + private static String md5Upper(final String data) { + if (data == null) return null; + try { + return md5Upper(data.getBytes()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5Upper"); + } + return null; + } + + /** + * 加密内容 (32 位大写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + private static String md5Upper(final byte[] data) { + if (data == null) return null; + try { + // 获取 MD5 摘要算法的 MessageDigest 对象 + MessageDigest digest = MessageDigest.getInstance("MD5"); + // 使用指定的字节更新摘要 + digest.update(data); + // 获取密文 + return toHexString(digest.digest(), HEX_DIGITS_UPPER); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5Upper"); + } + return null; + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS}、{@link #HEX_DIGITS_UPPER} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // ============== + // = ArrayUtils = + // ============== + + /** + * 判断数组是否为 null + * @param objects object[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Object[] objects) { + return objects == null || objects.length == 0; + } + + /** + * 判断数组是否为 null + * @param ints int[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final int[] ints) { + return ints == null || ints.length == 0; + } + + /** + * 判断数组是否为 null + * @param bytes byte[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final byte[] bytes) { + return bytes == null || bytes.length == 0; + } + + /** + * 判断数组是否为 null + * @param chars char[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final char[] chars) { + return chars == null || chars.length == 0; + } + + /** + * 判断数组是否为 null + * @param shorts short[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final short[] shorts) { + return shorts == null || shorts.length == 0; + } + + /** + * 判断数组是否为 null + * @param longs long[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final long[] longs) { + return longs == null || longs.length == 0; + } + + /** + * 判断数组是否为 null + * @param floats float[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final float[] floats) { + return floats == null || floats.length == 0; + } + + /** + * 判断数组是否为 null + * @param doubles double[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final double[] doubles) { + return doubles == null || doubles.length == 0; + } + + /** + * 判断数组是否为 null + * @param booleans boolean[] + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final boolean[] booleans) { + return booleans == null || booleans.length == 0; + } + + // = + + /** + * 判断数组是否不为 null + * @param objects object[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Object[] objects) { + return objects != null && objects.length != 0; + } + + /** + * 判断数组是否不为 null + * @param ints int[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final int[] ints) { + return ints != null && ints.length != 0; + } + + /** + * 判断数组是否不为 null + * @param bytes byte[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final byte[] bytes) { + return bytes != null && bytes.length != 0; + } + + /** + * 判断数组是否不为 null + * @param chars char[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final char[] chars) { + return chars != null && chars.length != 0; + } + + /** + * 判断数组是否不为 null + * @param shorts short[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final short[] shorts) { + return shorts != null && shorts.length != 0; + } + + /** + * 判断数组是否不为 null + * @param longs long[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final long[] longs) { + return longs != null && longs.length != 0; + } + + /** + * 判断数组是否不为 null + * @param floats float[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final float[] floats) { + return floats != null && floats.length != 0; + } + + /** + * 判断数组是否不为 null + * @param doubles double[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final double[] doubles) { + return doubles != null && doubles.length != 0; + } + + /** + * 判断数组是否不为 null + * @param booleans boolean[] + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final boolean[] booleans) { + return booleans != null && booleans.length != 0; + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取数组长度 + * @param objects object[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final Object[] objects) { + return length(objects, 0); + } + + /** + * 获取数组长度 + * @param ints int[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final int[] ints) { + return length(ints, 0); + } + + /** + * 获取数组长度 + * @param bytes byte[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final byte[] bytes) { + return length(bytes, 0); + } + + /** + * 获取数组长度 + * @param chars char[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final char[] chars) { + return length(chars, 0); + } + + /** + * 获取数组长度 + * @param shorts short[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final short[] shorts) { + return length(shorts, 0); + } + + /** + * 获取数组长度 + * @param longs long[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final long[] longs) { + return length(longs, 0); + } + + /** + * 获取数组长度 + * @param floats float[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final float[] floats) { + return length(floats, 0); + } + + /** + * 获取数组长度 + * @param doubles double[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final double[] doubles) { + return length(doubles, 0); + } + + /** + * 获取数组长度 + * @param booleans boolean[] + * @return 如果数据为 null, 则返回默认长度, 如果不为 null, 则返回 array[].length + */ + public static int length(final boolean[] booleans) { + return length(booleans, 0); + } + + // = + + /** + * 获取数组长度 + * @param objects object[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final Object[] objects, final int defaultLength) { + return objects != null ? objects.length : defaultLength; + } + + /** + * 获取数组长度 + * @param ints int[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final int[] ints, final int defaultLength) { + return ints != null ? ints.length : defaultLength; + } + + /** + * 获取数组长度 + * @param bytes byte[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final byte[] bytes, final int defaultLength) { + return bytes != null ? bytes.length : defaultLength; + } + + /** + * 获取数组长度 + * @param chars char[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final char[] chars, final int defaultLength) { + return chars != null ? chars.length : defaultLength; + } + + /** + * 获取数组长度 + * @param shorts short[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final short[] shorts, final int defaultLength) { + return shorts != null ? shorts.length : defaultLength; + } + + /** + * 获取数组长度 + * @param longs long[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final long[] longs, final int defaultLength) { + return longs != null ? longs.length : defaultLength; + } + + /** + * 获取数组长度 + * @param floats float[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final float[] floats, final int defaultLength) { + return floats != null ? floats.length : defaultLength; + } + + /** + * 获取数组长度 + * @param doubles double[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final double[] doubles, final int defaultLength) { + return doubles != null ? doubles.length : defaultLength; + } + + /** + * 获取数组长度 + * @param booleans boolean[] + * @param defaultLength 数组为 null 时, 返回的长度 + * @return 如果数据为 null, 则返回 defaultLength, 如果不为 null, 则返回 array[].length + */ + public static int length(final boolean[] booleans, final int defaultLength) { + return booleans != null ? booleans.length : defaultLength; + } + + // = + + /** + * 判断数组长度是否等于期望长度 + * @param objects object[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Object[] objects, final int length) { + return objects != null && objects.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param ints int[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final int[] ints, final int length) { + return ints != null && ints.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param bytes byte[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final byte[] bytes, final int length) { + return bytes != null && bytes.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param chars char[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final char[] chars, final int length) { + return chars != null && chars.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param shorts short[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final short[] shorts, final int length) { + return shorts != null && shorts.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param longs long[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final long[] longs, final int length) { + return longs != null && longs.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param floats float[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final float[] floats, final int length) { + return floats != null && floats.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param doubles double[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final double[] doubles, final int length) { + return doubles != null && doubles.length == length; + } + + /** + * 判断数组长度是否等于期望长度 + * @param booleans boolean[] + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final boolean[] booleans, final int length) { + return booleans != null && booleans.length == length; + } + + // =================== + // = CollectionUtils = + // =================== + + /** + * 判断 Collection 是否为 null + * @param collection {@link Collection} + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Collection collection) { + return (collection == null || collection.size() == 0); + } + + /** + * 判断 Collection 是否不为 null + * @param collection {@link Collection} + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Collection collection) { + return (collection != null && collection.size() != 0); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取 Collection 长度 + * @param collection {@link Collection} + * @return 如果 Collection 为 null, 则返回默认长度, 如果不为 null, 则返回 collection.size() + */ + public static int length(final Collection collection) { + return length(collection, 0); + } + + /** + * 获取 Collection 长度 + * @param collection {@link Collection} + * @param defaultLength 集合为 null 默认长度 + * @return 如果 Collection 为 null, 则返回 defaultLength, 如果不为 null, 则返回 collection.size() + */ + public static int length(final Collection collection, final int defaultLength) { + return collection != null ? collection.size() : defaultLength; + } + + // = + + /** + * 获取长度 to Collection 是否等于期望长度 + * @param collection {@link Collection} + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Collection collection, final int length) { + return collection != null && collection.size() == length; + } + + // ============ + // = MapUtils = + // ============ + + /** + * 判断 Map 是否为 null + * @param map {@link Map} + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Map map) { + return (map == null || map.size() == 0); + } + + /** + * 判断 Map 是否不为 null + * @param map {@link Map} + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Map map) { + return (map != null && map.size() != 0); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取 Map 长度 + * @param map {@link Map} + * @return 如果 Map 为 null, 则返回默认长度, 如果不为 null, 则返回 map.size() + */ + public static int length(final Map map) { + return length(map, 0); + } + + /** + * 获取 Map 长度 + * @param map {@link Map} + * @param defaultLength 集合为 null 默认长度 + * @return 如果 Map 为 null, 则返回 defaultLength, 如果不为 null, 则返回 map.size() + */ + public static int length(final Map map, final int defaultLength) { + return map != null ? map.size() : defaultLength; + } + + // = + + /** + * 获取长度 to Map 是否等于期望长度 + * @param map {@link Map} + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Map map, final int length) { + return map != null && map.size() == length; + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @param isTrim 是否调用 trim() + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String str, final boolean isTrim) { + if (str != null) { + return isEmpty(isTrim ? str.trim() : str); + } + return false; + } + + /** + * 判断多个字符串是否存在为 null 的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + if (isEmpty(strs[i])) { + return true; + } + } + return false; + } + // 默认表示属于 null + return true; + } + + // = + + /** + * 判断字符串是否不为 null + * @param str 待校验的字符串 + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final String str) { + return (str != null && str.length() != 0); + } + + /** + * 判断字符串是否不为 null + * @param str 待校验的字符串 + * @param isTrim 是否调用 trim() + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final String str, final boolean isTrim) { + return isNotEmpty(isTrim ? str.trim() : str); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取字符串长度 + * @param str 待校验的字符串 + * @return 字符串长度, 如果字符串为 null, 则返回 0 + */ + public static int length(final String str) { + return str == null ? 0 : str.length(); + } + + /** + * 获取字符串长度 + * @param str 待校验的字符串 + * @param defaultLength 字符串为 null 时, 返回的长度 + * @return 字符串长度, 如果字符串为 null, 则返回 defaultLength + */ + public static int length(final String str, final int defaultLength) { + return str != null ? str.length() : defaultLength; + } + + // = + + /** + * 获取字符串长度 是否等于期望长度 + * @param str 待校验的字符串 + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final String str, final int length) { + return str != null && str.length() == length; + } + + // ============ + // = 对比判断 = + // ============ + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + /** + * 判断多个字符串是否相等, 只有全相等才返回 true - 对比大小写 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEquals(final String... strs) { + return isEquals(false, strs); + } + + /** + * 判断多个字符串是否相等, 只有全相等才返回 true + * @param isIgnore 是否忽略大小写 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEquals(final boolean isIgnore, final String... strs) { + if (strs != null) { + String last = null; + // 获取数据长度 + int len = strs.length; + // 如果最多只有一个数据判断, 则直接跳过 + if (len <= 1) { + return false; + } + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 如果等于 null, 则跳过 + if (val == null) { + return false; + } + if (last != null) { + if (isIgnore) { + if (!val.equalsIgnoreCase(last)) { + return false; + } + } else { + if (!val.equals(last)) { + return false; + } + } + } + last = val; + } + return true; + } + return false; + } + + /** + * 判断多个字符串, 只要有一个符合条件则通过 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isOrEquals(final String str, final String... strs) { + return isOrEquals(false, str, strs); + } + + /** + * 判断多个字符串, 只要有一个符合条件则通过 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isOrEquals(final boolean isIgnore, final String str, final String... strs) { + if (str != null && strs != null && strs.length != 0) { + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 如果等于 null, 则跳过 + if (val == null) { + continue; + } else { + if (isIgnore) { + if (val.equalsIgnoreCase(str)) { + return true; + } + } else { + if (val.equals(str)) { + return true; + } + } + } + } + } + return false; + } + + /** + * 判断一堆值中, 是否存在符合该条件的 ( 包含 ) + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContains(final String str, final String... strs) { + return isContains(false, str, strs); + } + + /** + * 判断一堆值中, 是否存在符合该条件的 ( 包含 ) + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContains(final boolean isIgnore, final String str, final String... strs) { + if (str != null && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取内容长度 + int cLength = strTemp.length(); + // 遍历判断 + for (int i = 0, len = strs.length; i < len; i++) { + // 获取参数 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val) && cLength != 0) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否包含 + if (valIgnore.indexOf(strTemp) != -1) { + return true; + } + } else { + // 判断是否包含 + if (val.indexOf(strTemp) != -1) { + return true; + } + } + } else { + // 下面这一串可以不要, 因为判断字符串是否包含 + // 已经处理了值不为 null, 并且需要判断的值长度不能为 0, 下面则不需要加上 + if (strTemp.equals(val)) { + return true; + } + } + } + } + return false; + } + + /** + * 判断内容, 是否属于特定字符串开头 - 对比大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isStartsWith(final String str, final String... strs) { + return isStartsWith(false, str, strs); + } + + /** + * 判断内容, 是否属于特定字符串开头 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isStartsWith(final boolean isIgnore, final String str, final String... strs) { + if (!isEmpty(str) && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val)) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否属于 val 开头 + if (strTemp.startsWith(valIgnore)) { + return true; + } + } else { + // 判断是否属于 val 开头 + if (strTemp.startsWith(val)) { + return true; + } + } + } + } + } + return false; + } + + /** + * 判断内容, 是否属于特定字符串结尾 - 对比大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEndsWith(final String str, final String... strs) { + return isEndsWith(false, str, strs); + } + + /** + * 判断内容, 是否属于特定字符串结尾 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEndsWith(final boolean isIgnore, final String str, final String... strs) { + if (!isEmpty(str) && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val)) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否属于 val 结尾 + if (strTemp.endsWith(valIgnore)) { + return true; + } + } else { + // 判断是否属于 val 结尾 + if (strTemp.endsWith(val)) { + return true; + } + } + } + } + } + return false; + } + + // ============ + // = 其他处理 = + // ============ + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + * 清空字符串全部空格 + * @param str 待处理字符串 + * @return 处理后的字符串 + */ + public static String toClearSpace(final String str) { + if (isEmpty(str)) return str; + return str.replaceAll(" ", ""); + } + + /** + * 清空字符串前后所有空格 + * @param str 待处理字符串 + * @return 处理后的字符串 + */ + public static String toClearSpaceTrim(final String str) { + if (isEmpty(str)) return str; + String strTemp = str; + // 如果前面或者后面都是空格开头, 就一直进行处理 + while (strTemp.startsWith(" ") || strTemp.endsWith(" ")) { + strTemp = strTemp.trim(); + } + return strTemp; + } + + // = + + /** + * 追加空格 + * @param number 空格数量 + * @return 指定数量的空格字符串 + */ + public static String appendSpace(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append(" "); + } + } + return builder.toString(); + } + + /** + * 追加 Tab + * @param number tab 键数量 + * @return 指定数量的 Tab 字符串 + */ + public static String appendTab(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append("\t"); + } + } + return builder.toString(); + } + + /** + * 追加换行 + * @param number 换行数量 + * @return 指定数量的换行字符串 + */ + public static String appendLine(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append(NEW_LINE_STR); + } + } + return builder.toString(); + } + + // = + + /** + * 检查字符串 + * @param str 待校验字符串 + * @return 如果待校验字符串为 null, 则返回默认字符串, 如果不为 null, 则返回该字符串 + */ + public static String toCheckValue(final String str) { + return toCheckValue("", str); + } + + /** + * 检查字符串 + * @param defaultStr 默认字符串 + * @param str 待校验字符串 + * @return 如果待校验字符串为 null, 则返回 defaultStr, 如果不为 null, 则返回该字符串 + */ + public static String toCheckValue(final String defaultStr, final String str) { + return isEmpty(str) ? defaultStr : str; + } + + /** + * 检查字符串 - 单独检查两个值 + * @param defaultStr 默认字符串 + * @param value1 第一个待校验字符串 + * @param value2 第二个待校验字符串 + * @return 两个待校验字符串中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValue(final String defaultStr, final String value1, final String value2) { + if (isEmpty(value1)) { + if (isEmpty(value2)) { + return defaultStr; + } else { + return value2; + } + } else { + return value1; + } + } + + /** + * 检查字符串 - 多个值 + * @param defaultStr 默认字符串 + * @param strs 待校验字符串数组 + * @return 字符串数组中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValues(final String defaultStr, final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + String val = strs[i]; + if (isEmpty(val)) { + if (i == len - 1) { + return defaultStr; // 属于最后一个, 则返回默认值 + } else { + continue; // 不属于最后一个则跳过 + } + } else { + return val; + } + } + } + return defaultStr; + } + + /** + * 检查字符串 - 多个值 ( 删除前后空格对比判断 ) + * @param defaultStr 默认字符串 + * @param strs 待校验字符串数组 + * @return 字符串数组中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValuesSpace(final String defaultStr, final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + // 删除前后空格处理后, 进行返回 + String val = toClearSpaceTrim(strs[i]); + if (isEmpty(val)) { + if (i == len - 1) { + return defaultStr; // 属于最后一个, 则返回默认值 + } else { + continue; // 不属于最后一个则跳过 + } + } else { + return val; + } + } + } + return defaultStr; + } + + // ================== + // = 数据格式化处理 = + // ================== + + /** + * 获取格式化后的字符串 + * @param format 待格式化字符串 + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getFormatString(final String format, final Object... args) { + if (format == null) return null; + try { + if (args != null && args.length != 0) { + return String.format(format, args); + } else { + return format; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFormatString"); + } + return null; + } + + // = + + /** + * 获取自动数量格式化后的字符串 ( 可变参数 ) + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getAutoFormatString(final Object... args) { + if (args != null && args.length != 0) { + try { + int length = args.length; + StringBuilder builder = new StringBuilder(); + builder.append("%s"); + if (length > 1) { + for (int i = 1; i < length; i++) { + builder.append(" %s"); + } + } + return String.format(builder.toString(), args); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAutoFormatString"); + } + } + return null; + } + + /** + * 获取自动数量格式化后的字符串 ( 可变参数 ) + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getAutoFormatString2(final Object... args) { + if (args != null && args.length != 0) { + try { + int length = args.length; + StringBuilder builder = new StringBuilder(); + builder.append("【%s】"); + if (length > 1) { + for (int i = 1; i < length; i++) { + builder.append(" %s"); + } + } + return String.format(builder.toString(), args); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAutoFormatString2"); + } + } + return null; + } + + // = + + /** + * StringBuilder 拼接处理 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final Object... args) { + return appends(new StringBuilder(), null, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param split 追加间隔 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final String split, final Object... args) { + return appends(new StringBuilder(), split, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param builder 拼接 Builder + * @param split 追加间隔 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final StringBuilder builder, final String split, final Object... args) { + return appends(builder, split, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param builder 拼接 Builder + * @param split 追加间隔 + * @param end 结尾是否追加 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final StringBuilder builder, final String split, final boolean end, final Object... args) { + if (builder != null && args != null) { + // 获取间隔字符串, 优化循环判断 + String str = isEmpty(split) ? "" : split; + // 循环处理 + for (int i = 0, len = args.length; i < len; i++) { + builder.append(args[i]); // 拼接数据 + // 判断是否结尾 + if (len - i == 1) { + // 判断结尾是否追加 + if (end) builder.append(str); // 间隔追加 + } else { + builder.append(str); // 间隔追加 + } + } + return builder; + } + return builder; + } + + // =============== + // = ObjectUtils = + // =============== + + /** + * 判断对象是否为空 + * @param object 对象 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Object object) { + if (object == null) { + return true; + } + try { + if (object.getClass().isArray() && Array.getLength(object) == 0) { + return true; + } + if (object instanceof CharSequence && object.toString().length() == 0) { + return true; + } + if (object instanceof Collection && ((Collection) object).isEmpty()) { + return true; + } + if (object instanceof Map && ((Map) object).isEmpty()) { + return true; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isEmpty"); + } + return false; + } + + /** + * 判断对象是否非空 + * @param object 对象 + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Object object) { + return !isEmpty(object); + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/EncodeUtils.java b/lib/DevJava/src/main/java/dev/utils/common/EncodeUtils.java new file mode 100644 index 0000000000..1c62a5781f --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/EncodeUtils.java @@ -0,0 +1,293 @@ +package dev.utils.common; + +import dev.utils.JCLogUtils; +import dev.utils.common.cipher.Base64; + +/** + * detail: 编码工具类 + * @author Ttt + *
+ *     Base64 flags
+ *     

+ * CRLF 这个参数看起来比较眼熟, 它就是 Win 风格的换行符, 意思就是使用 CR LF 这一对作为一行的结尾而不是 Unix 风格的 LF + * DEFAULT 这个参数是默认, 使用默认的方法来加密 + * NO_PADDING 这个参数是略去加密字符串最后的 = + * NO_WRAP 这个参数意思是略去所有的换行符 ( 设置后 CRLF 就没用了 ) + * URL_SAFE 这个参数意思是加密时不使用对 URL 和文件名有特殊意义的字符来作为加密字符, 具体就是以 - 和 _ 取代 + 和 / + *
+ */ +public final class EncodeUtils { + + private EncodeUtils() { + } + + // 日志 TAG + private static final String TAG = EncodeUtils.class.getSimpleName(); + + // ========== + // = Base64 = + // ========== + + // =============== + // = Base64 编码 = + // =============== + + /** + * Base64 编码 + * @param input 待处理数据 + * @return Base64 编码后的 byte[] + */ + public static byte[] base64Encode(final String input) { + return base64Encode(input, Base64.NO_WRAP); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 编码后的 byte[] + */ + public static byte[] base64Encode(final String input, final int flags) { + return base64Encode(toBytes(input), flags); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @return Base64 编码后的 byte[] + */ + public static byte[] base64Encode(final byte[] input) { + return base64Encode(input, Base64.NO_WRAP); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 编码后的 byte[] + */ + public static byte[] base64Encode(final byte[] input, final int flags) { + if (input == null) return null; + return Base64.encode(input, flags); + } + + // = + + /** + * Base64 编码 + * @param input 待处理数据 + * @return Base64 编码后的 byte[] 转 String + */ + public static String base64EncodeToString(final String input) { + return base64EncodeToString(input, Base64.NO_WRAP); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 编码后的 byte[] 转 String + */ + public static String base64EncodeToString(final String input, final int flags) { + return base64EncodeToString(toBytes(input), flags); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @return Base64 编码后的 byte[] 转 String + */ + public static String base64EncodeToString(final byte[] input) { + return base64EncodeToString(input, Base64.NO_WRAP); + } + + /** + * Base64 编码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 编码后的 byte[] 转 String + */ + public static String base64EncodeToString(final byte[] input, final int flags) { + if (input == null) return null; + return toString(Base64.encode(input, flags)); + } + + // =============== + // = Base64 解码 = + // =============== + + /** + * Base64 解码 + * @param input 待处理数据 + * @return Base64 解码后的 byte[] + */ + public static byte[] base64Decode(final String input) { + return base64Decode(input, Base64.NO_WRAP); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 解码后的 byte[] + */ + public static byte[] base64Decode(final String input, final int flags) { + return base64Decode(toBytes(input), flags); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @return Base64 解码后的 byte[] + */ + public static byte[] base64Decode(final byte[] input) { + return base64Decode(input, Base64.NO_WRAP); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 解码后的 byte[] + */ + public static byte[] base64Decode(final byte[] input, final int flags) { + if (input == null) return null; + return Base64.decode(input, flags); + } + + // = + + /** + * Base64 解码 + * @param input 待处理数据 + * @return Base64 解码后的 byte[] 转 String + */ + public static String base64DecodeToString(final String input) { + return base64DecodeToString(input, Base64.NO_WRAP); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 解码后的 byte[] 转 String + */ + public static String base64DecodeToString(final String input, final int flags) { + return base64DecodeToString(toBytes(input), flags); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @return Base64 解码后的 byte[] 转 String + */ + public static String base64DecodeToString(final byte[] input) { + return base64DecodeToString(input, Base64.NO_WRAP); + } + + /** + * Base64 解码 + * @param input 待处理数据 + * @param flags Base64 编解码 flags + * @return Base64 解码后的 byte[] 转 String + */ + public static String base64DecodeToString(final byte[] input, final int flags) { + if (input == null) return null; + return toString(Base64.decode(input, flags)); + } + + // ======== + // = Html = + // ======== + + /** + * Html 字符串编码 + *
+     *     TextUtils.htmlEncode()
+     * 
+ * @param input 待处理数据 + * @return Html 编码后的数据 + */ + public static String htmlEncode(final CharSequence input) { + if (input == null) return null; + StringBuilder builder = new StringBuilder(); + char ch; + for (int i = 0, len = input.length(); i < len; i++) { + ch = input.charAt(i); + switch (ch) { + case '<': + builder.append("<"); //$NON-NLS-1$ + break; + case '>': + builder.append(">"); //$NON-NLS-1$ + break; + case '&': + builder.append("&"); //$NON-NLS-1$ + break; + case '\'': + //http://www.w3.org/TR/xhtml1 + // The named character reference ' (the apostrophe, U+0027) was introduced in + // XML 1.0 but does not appear in HTML. Authors should therefore use ' instead + // of ' to work as expected in HTML 4 user agents. + builder.append("'"); //$NON-NLS-1$ + break; + case '"': + builder.append("""); //$NON-NLS-1$ + break; + default: + builder.append(ch); + } + } + return builder.toString(); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + /** + * byte[] 转 String + * @param data byte[] + * @return {@link String} + */ + private static String toString(final byte[] data) { + return toString(data, null); + } + + /** + * byte[] 转 String + * @param data byte[] + * @param defaultStr 默认字符串 + * @return {@link String} 如果转换失败, 则返回 defaultStr + */ + private static String toString(final byte[] data, final String defaultStr) { + if (data != null) { + try { + return new String(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toString"); + } + } + return defaultStr; + } + + /** + * 字符串 获取 byte[] + * @param str String + * @return byte[] + */ + private static byte[] toBytes(final String str) { + if (str != null) { + try { + return str.getBytes(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toBytes"); + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/FieldUtils.java b/lib/DevJava/src/main/java/dev/utils/common/FieldUtils.java new file mode 100644 index 0000000000..e77099757c --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/FieldUtils.java @@ -0,0 +1,201 @@ +package dev.utils.common; + +import java.io.Serializable; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.LinkedList; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 变量字段工具类 + * @author Ttt + */ +public final class FieldUtils { + + private FieldUtils() { + } + + // 日志 TAG + private static final String TAG = FieldUtils.class.getSimpleName(); + + /** + * 判断是否序列化 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isSerializable(final Field field) { + if (field == null) return false; + try { + Class[] clazzs = field.getType().getInterfaces(); + for (Class clazz : clazzs) { + if (Serializable.class == clazz) { + return true; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isSerializable"); + } + return false; + } + + /** + * 设置字段的值 + * @param field {@link Field} + * @param object Object + * @param value Object-Value + * @return 对应的 Object + */ + public static Object set(final Field field, final Object object, final Object value) { + if (field == null || object == null) return null; + try { + field.setAccessible(true); + field.set(object, value); + return field.get(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "set"); + } + return null; + } + + /** + * 获取字段的值 + * @param field {@link Field} + * @param object Object + * @return 对应的 Object + */ + public static Object get(final Field field, final Object object) { + if (field == null || object == null) return null; + try { + field.setAccessible(true); + return field.get(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + return null; + } + + // = + + /** + * 是否 long/Long 类型 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isLong(final Field field) { + return field != null && (field.getType() == long.class || field.getType() == Long.class); + } + + /** + * 是否 float/Float 类型 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isFloat(final Field field) { + return field != null && (field.getType() == float.class || field.getType() == Float.class); + } + + /** + * 是否 double/Double 类型 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isDouble(final Field field) { + return field != null && (field.getType() == double.class || field.getType() == Double.class); + } + + /** + * 是否 int/Integer 类型 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isInteger(final Field field) { + return field != null && (field.getType() == int.class || field.getType() == Integer.class); + } + + /** + * 是否 String 类型 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isString(final Field field) { + return field != null && (field.getType() == String.class); + } + + // = + + /** + * 获取字段的泛型类型, 如果不带泛型返回 null + * @param field {@link Field} + * @param 未知类型 + * @return 泛型类型 + */ + public static Class getGenericType(final Field field) { + if (field == null) return null; + try { + Type type = field.getGenericType(); + if (type instanceof ParameterizedType) { + type = ((ParameterizedType) type).getActualTypeArguments()[0]; + if (type instanceof Class) { + return (Class) type; + } + } else if (type instanceof Class) { + return (Class) type; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getGenericType"); + } + return null; + } + + /** + * 获取数组的类型 + * @param field {@link Field} + * @param 未知类型 + * @return 数组类型 + */ + public static Class getComponentType(final Field field) { + if (field == null || field.getType() == null) return null; + return field.getType().getComponentType(); + } + + /** + * 获取全部 Field, 包括父类 + * @param clazz {@link Class} + * @return {@link List} + */ + public static List getAllDeclaredFields(final Class clazz) { + if (clazz == null) return null; + try { + Class clazzTemp = clazz; + // find all field. + LinkedList fieldList = new LinkedList<>(); + while (clazzTemp != null && clazzTemp != Object.class) { + Field[] fs = clazzTemp.getDeclaredFields(); + for (int i = 0; i < fs.length; i++) { + Field f = fs[i]; + if (!isInvalid(f)) { + fieldList.addLast(f); + } + } + clazzTemp = clazzTemp.getSuperclass(); + } + return fieldList; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAllDeclaredFields"); + } + return null; + } + + /** + * 是静态常量或者内部结构属性 + * @param field {@link Field} + * @return {@code true} yes, {@code false} no + */ + public static boolean isInvalid(final Field field) { + return field != null && ((Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) || field.isSynthetic()); + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/FileIOUtils.java b/lib/DevJava/src/main/java/dev/utils/common/FileIOUtils.java new file mode 100644 index 0000000000..8948b6e1d5 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/FileIOUtils.java @@ -0,0 +1,706 @@ +package dev.utils.common; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 文件 (IO 流 ) 工具类 + * @author Ttt + */ +public final class FileIOUtils { + + private FileIOUtils() { + } + + // 日志 TAG + private static final String TAG = FileIOUtils.class.getSimpleName(); + // 换行符 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + // 缓存大小 + private static int sBufferSize = 8192; + + /** + * 设置缓冲区的大小, 默认大小等于 8192 字节 + * @param bufferSize 缓冲 Buffer 大小 + */ + public static void setBufferSize(final int bufferSize) { + sBufferSize = bufferSize; + } + + /** + * 通过输入流写入文件 + * @param filePath 文件路径 + * @param inputStream {@link InputStream} + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromIS(final String filePath, final InputStream inputStream) { + return writeFileFromIS(getFileByPath(filePath), inputStream, false); + } + + /** + * 通过输入流写入文件 + * @param filePath 文件路径 + * @param inputStream {@link InputStream} + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromIS(final String filePath, final InputStream inputStream, final boolean append) { + return writeFileFromIS(getFileByPath(filePath), inputStream, append); + } + + /** + * 通过输入流写入文件 + * @param file 文件 + * @param inputStream {@link InputStream} + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromIS(final File file, final InputStream inputStream) { + return writeFileFromIS(file, inputStream, false); + } + + /** + * 通过输入流写入文件 + * @param file 文件 + * @param inputStream {@link InputStream} + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromIS(final File file, final InputStream inputStream, final boolean append) { + if (!createOrExistsFile(file) || inputStream == null) return false; + OutputStream os = null; + try { + os = new BufferedOutputStream(new FileOutputStream(file, append)); + byte[] data = new byte[sBufferSize]; + int len; + while ((len = inputStream.read(data, 0, sBufferSize)) != -1) { + os.write(data, 0, len); + } + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromIS"); + return false; + } finally { + closeIOQuietly(inputStream, os); + } + } + + /** + * 通过字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes) { + return writeFileFromBytesByStream(getFileByPath(filePath), bytes, false); + } + + /** + * 通过字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes, final boolean append) { + return writeFileFromBytesByStream(getFileByPath(filePath), bytes, append); + } + + /** + * 通过字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes) { + return writeFileFromBytesByStream(file, bytes, false); + } + + /** + * 通过字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes, final boolean append) { + if (bytes == null || !createOrExistsFile(file)) return false; + BufferedOutputStream bos = null; + try { + bos = new BufferedOutputStream(new FileOutputStream(file, append)); + bos.write(bytes); + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromBytesByStream"); + return false; + } finally { + closeIOQuietly(bos); + } + } + + /** + * 通过 FileChannel 把字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByChannel(final String filePath, final byte[] bytes, final boolean isForce) { + return writeFileFromBytesByChannel(getFileByPath(filePath), bytes, false, isForce); + } + + /** + * 通过 FileChannel 把字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByChannel(final String filePath, final byte[] bytes, final boolean append, final boolean isForce) { + return writeFileFromBytesByChannel(getFileByPath(filePath), bytes, append, isForce); + } + + /** + * 通过 FileChannel 把字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByChannel(final File file, final byte[] bytes, final boolean isForce) { + return writeFileFromBytesByChannel(file, bytes, false, isForce); + } + + /** + * 通过 FileChannel 把字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByChannel(final File file, final byte[] bytes, final boolean append, final boolean isForce) { + if (bytes == null) return false; + FileChannel fc = null; + try { + fc = new FileOutputStream(file, append).getChannel(); + fc.position(fc.size()); + fc.write(ByteBuffer.wrap(bytes)); + if (isForce) fc.force(true); + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromBytesByChannel"); + return false; + } finally { + closeIOQuietly(fc); + } + } + + /** + * 通过 MappedByteBuffer 把字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByMap(final String filePath, final byte[] bytes, final boolean isForce) { + return writeFileFromBytesByMap(filePath, bytes, false, isForce); + } + + /** + * 通过 MappedByteBuffer 把字节流写入文件 + * @param filePath 文件路径 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByMap(final String filePath, final byte[] bytes, final boolean append, final boolean isForce) { + return writeFileFromBytesByMap(getFileByPath(filePath), bytes, append, isForce); + } + + /** + * 通过 MappedByteBuffer 把字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByMap(final File file, final byte[] bytes, final boolean isForce) { + return writeFileFromBytesByMap(file, bytes, false, isForce); + } + + /** + * 通过 MappedByteBuffer 把字节流写入文件 + * @param file 文件 + * @param bytes byte[] + * @param append 是否追加到结尾 + * @param isForce 是否强制写入 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromBytesByMap(final File file, final byte[] bytes, final boolean append, final boolean isForce) { + if (bytes == null || !createOrExistsFile(file)) return false; + FileChannel fc = null; + try { + fc = new FileOutputStream(file, append).getChannel(); + MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, fc.size(), bytes.length); + mbb.put(bytes); + if (isForce) mbb.force(); + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromBytesByMap"); + return false; + } finally { + closeIOQuietly(fc); + } + } + + /** + * 通过字符串写入文件 + * @param filePath 文件路径 + * @param content 写入内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromString(final String filePath, final String content) { + return writeFileFromString(getFileByPath(filePath), content, false); + } + + /** + * 通过字符串写入文件 + * @param filePath 文件路径 + * @param content 写入内容 + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromString(final String filePath, final String content, final boolean append) { + return writeFileFromString(getFileByPath(filePath), content, append); + } + + /** + * 通过字符串写入文件 + * @param file 文件 + * @param content 写入内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromString(final File file, final String content) { + return writeFileFromString(file, content, false); + } + + /** + * 通过字符串写入文件 + * @param file 文件 + * @param content 写入内容 + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + public static boolean writeFileFromString(final File file, final String content, final boolean append) { + if (file == null || content == null) return false; + if (!createOrExistsFile(file)) return false; + BufferedWriter bw = null; + try { + bw = new BufferedWriter(new FileWriter(file, append)); + bw.write(content); + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromString"); + return false; + } finally { + closeIOQuietly(bw); + } + } + + // ============== + // = 读写分界线 = + // ============== + + /** + * 读取文件内容, 返回换行 List + * @param filePath 文件路径 + * @return 换行 {@link List} + */ + public static List readFileToList(final String filePath) { + return readFileToList(getFileByPath(filePath), null); + } + + /** + * 读取文件内容, 返回换行 List + * @param filePath 文件路径 + * @param charsetName 字符编码 + * @return 换行 {@link List} + */ + public static List readFileToList(final String filePath, final String charsetName) { + return readFileToList(getFileByPath(filePath), charsetName); + } + + /** + * 读取文件内容, 返回换行 List + * @param file 文件 + * @return 换行 {@link List} + */ + public static List readFileToList(final File file) { + return readFileToList(file, 0, Integer.MAX_VALUE, null); + } + + /** + * 读取文件内容, 返回换行 List + * @param file 文件 + * @param charsetName 字符编码 + * @return 换行 {@link List} + */ + public static List readFileToList(final File file, final String charsetName) { + return readFileToList(file, 0, Integer.MAX_VALUE, charsetName); + } + + /** + * 读取文件内容, 返回换行 List + * @param filePath 文件路径 + * @param start 开始位置 + * @param end 结束位置 + * @return 换行 {@link List} + */ + public static List readFileToList(final String filePath, final int start, final int end) { + return readFileToList(getFileByPath(filePath), start, end, null); + } + + /** + * 读取文件内容, 返回换行 List + * @param filePath 文件路径 + * @param start 开始位置 + * @param end 结束位置 + * @param charsetName 字符编码 + * @return 换行 {@link List} + */ + public static List readFileToList(final String filePath, final int start, final int end, final String charsetName) { + return readFileToList(getFileByPath(filePath), start, end, charsetName); + } + + /** + * 读取文件内容, 返回换行 List + * @param file 文件 + * @param start 开始位置 + * @param end 结束位置 + * @return 换行 {@link List} + */ + public static List readFileToList(final File file, final int start, final int end) { + return readFileToList(file, start, end, null); + } + + /** + * 读取文件内容, 返回换行 List + * @param file 文件 + * @param start 开始位置 + * @param end 结束位置 + * @param charsetName 字符编码 + * @return 换行 {@link List} + */ + public static List readFileToList(final File file, final int start, final int end, final String charsetName) { + if (!isFileExists(file)) return null; + if (start > end) return null; + BufferedReader br = null; + try { + String line; + int curLine = 1; + List list = new ArrayList<>(); + if (isSpace(charsetName)) { + br = new BufferedReader(new InputStreamReader(new FileInputStream(file))); + } else { + br = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName)); + } + while ((line = br.readLine()) != null) { + if (curLine > end) break; + if (start <= curLine && curLine <= end) list.add(line); + ++curLine; + } + return list; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "readFileToList"); + return null; + } finally { + closeIOQuietly(br); + } + } + + // = + + /** + * 读取文件内容, 返回字符串 + * @param filePath 文件路径 + * @return 文件内容字符串 + */ + public static String readFileToString(final String filePath) { + return readFileToString(getFileByPath(filePath), null); + } + + /** + * 读取文件内容, 返回字符串 + * @param filePath 文件路径 + * @param charsetName 字符编码 + * @return 文件内容字符串 + */ + public static String readFileToString(final String filePath, final String charsetName) { + return readFileToString(getFileByPath(filePath), charsetName); + } + + /** + * 读取文件内容, 返回字符串 + * @param file 文件 + * @return 文件内容字符串 + */ + public static String readFileToString(final File file) { + return readFileToString(file, null); + } + + /** + * 读取文件内容, 返回字符串 + * @param file 文件 + * @param charsetName 字符编码 + * @return 文件内容字符串 + */ + public static String readFileToString(final File file, final String charsetName) { + if (!isFileExists(file)) return null; + BufferedReader br = null; + try { + StringBuilder builder = new StringBuilder(); + if (isSpace(charsetName)) { + br = new BufferedReader(new InputStreamReader(new FileInputStream(file))); + } else { + br = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName)); + } + String line; + if ((line = br.readLine()) != null) { + builder.append(line); + while ((line = br.readLine()) != null) { + builder.append(NEW_LINE_STR).append(line); + } + } + return builder.toString(); + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "readFileToString"); + return null; + } finally { + closeIOQuietly(br); + } + } + + /** + * 读取文件内容, 返回 byte[] + * @param filePath 文件路径 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByStream(final String filePath) { + return readFileToBytesByStream(getFileByPath(filePath)); + } + + /** + * 读取文件内容, 返回 byte[] + * @param file 文件 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByStream(final File file) { + if (!isFileExists(file)) return null; + FileInputStream fis = null; + ByteArrayOutputStream baos = null; + try { + fis = new FileInputStream(file); + baos = new ByteArrayOutputStream(); + byte[] b = new byte[sBufferSize]; + int len; + while ((len = fis.read(b, 0, sBufferSize)) != -1) { + baos.write(b, 0, len); + } + return baos.toByteArray(); + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "readFileToBytesByStream"); + return null; + } finally { + closeIOQuietly(fis, baos); + } + } + + /** + * 通过 FileChannel, 读取文件内容, 返回 byte[] + * @param filePath 文件路径 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByChannel(final String filePath) { + return readFileToBytesByChannel(getFileByPath(filePath)); + } + + /** + * 通过 FileChannel, 读取文件内容, 返回 byte[] + * @param file 文件 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByChannel(final File file) { + if (!isFileExists(file)) return null; + FileChannel fc = null; + try { + fc = new RandomAccessFile(file, "r").getChannel(); + ByteBuffer byteBuffer = ByteBuffer.allocate((int) fc.size()); + while (true) { + if (!((fc.read(byteBuffer)) > 0)) break; + } + return byteBuffer.array(); + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "readFileToBytesByChannel"); + return null; + } finally { + closeIOQuietly(fc); + } + } + + /** + * 通过 MappedByteBuffer, 读取文件内容, 返回 byte[] + * @param filePath 文件路径 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByMap(final String filePath) { + return readFileToBytesByMap(getFileByPath(filePath)); + } + + /** + * 通过 MappedByteBuffer, 读取文件内容, 返回 byte[] + * @param file 文件 + * @return 文件内容 byte[] + */ + public static byte[] readFileToBytesByMap(final File file) { + if (!isFileExists(file)) return null; + FileChannel fc = null; + try { + fc = new RandomAccessFile(file, "r").getChannel(); + int size = (int) fc.size(); + MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, size).load(); + byte[] result = new byte[size]; + mbb.get(result, 0, size); + return result; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "readFileToBytesByMap"); + return null; + } finally { + closeIOQuietly(fc); + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============== + // = CloseUtils = + // ============== + + /** + * 安静关闭 IO + * @param closeables Closeable[] + */ + private static void closeIOQuietly(final Closeable... closeables) { + if (closeables == null) return; + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception ignore) { + } + } + } + } + + // ============= + // = FileUtils = + // ============= + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 判断文件是否存在, 不存在则判断是否创建成功 + * @param filePath 文件路径 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + private static boolean createOrExistsFile(final String filePath) { + return createOrExistsFile(getFileByPath(filePath)); + } + + /** + * 判断文件是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + private static boolean createOrExistsFile(final File file) { + if (file == null) return false; + // 如果存在, 是文件则返回 true, 是目录则返回 false + if (file.exists()) return file.isFile(); + // 判断文件是否存在, 不存在则直接返回 + if (!createOrExistsDir(file.getParentFile())) return false; + try { + // 存在, 则返回新的路径 + return file.createNewFile(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "createOrExistsFile"); + return false; + } + } + + /** + * 判断目录是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + private static boolean createOrExistsDir(final File file) { + // 如果存在, 是目录则返回 true, 是文件则返回 false, 不存在则返回是否创建成功 + return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); + } + + /** + * 检查是否存在某个文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + private static boolean isFileExists(final File file) { + return file != null && file.exists(); + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/FileUtils.java b/lib/DevJava/src/main/java/dev/utils/common/FileUtils.java new file mode 100644 index 0000000000..d37d524f90 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/FileUtils.java @@ -0,0 +1,2073 @@ +package dev.utils.common; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 文件操作工具类 + * @author Ttt + */ +public final class FileUtils { + + private FileUtils() { + } + + // 日志 TAG + private static final String TAG = FileUtils.class.getSimpleName(); + // 换行字符串 + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + public static File getFile(final String filePath) { + return getFileByPath(filePath); + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @param fileName 文件名 + * @return 文件 {@link File} + */ + public static File getFile(final String filePath, final String fileName) { + return (filePath != null && fileName != null) ? new File(filePath, fileName) : null; + } + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + public static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 获取路径, 并且进行创建目录 + * @param filePath 保存目录 + * @param fileName 文件名 + * @return 文件 {@link File} + */ + public static File getFileCreateFolder(final String filePath, final String fileName) { + // 防止不存在目录文件, 自动创建 + createFolder(filePath); + // 返回处理过后的 File + return getFile(filePath, fileName); + } + + /** + * 获取路径, 并且进行创建目录 + * @param filePath 保存目录 + * @param fileName 文件名 + * @return 文件 {@link File} + */ + public static String getFilePathCreateFolder(final String filePath, final String fileName) { + // 防止不存在目录文件, 自动创建 + createFolder(filePath); + // 返回处理过后的 File + File file = getFile(filePath, fileName); + // 返回文件路径 + return getAbsolutePath(file); + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param dirPath 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean createFolder(final String dirPath) { + return createFolder(getFileByPath(dirPath)); + } + + /** + * 判断某个文件夹是否创建, 未创建则创建 ( 纯路径 - 无文件名 ) + * @param file 文件夹路径 ( 无文件名字. 后缀 ) + * @return {@code true} success, {@code false} fail + */ + public static boolean createFolder(final File file) { + if (file != null) { + try { + // 当这个文件夹不存在的时候则创建文件夹 + if (!file.exists()) { + // 允许创建多级目录 + return file.mkdirs(); + // 这个无法创建多级目录 + // rootFile.mkdir(); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "createFolder"); + } + } + return false; + } + + /** + * 创建文件夹目录 - 可以传入文件名 + * @param filePath 文件路径 + 文件名 + * @return {@code true} success, {@code false} fail + */ + public static boolean createFolderByPath(final String filePath) { + return createFolderByPath(getFileByPath(filePath)); + } + + /** + * 创建文件夹目录 - 可以传入文件名 + * @param file 文件 + * @return {@code true} success, {@code false} fail + */ + public static boolean createFolderByPath(final File file) { + // 创建文件夹 - 如果失败才创建 + if (file != null) { + if (file.exists()) { + return true; + } else if (!file.getParentFile().mkdirs()) { + return createFolder(file.getParent()); + } + } + return false; + } + + /** + * 创建多个文件夹, 如果不存在则创建 + * @param filePaths 文件路径数组 + */ + public static void createFolderByPaths(final String... filePaths) { + if (filePaths != null && filePaths.length != 0) { + for (int i = 0, len = filePaths.length; i < len; i++) { + createFolder(filePaths[i]); + } + } + } + + /** + * 创建多个文件夹, 如果不存在则创建 + * @param files 文件数组 + */ + public static void createFolderByPaths(final File... files) { + if (files != null && files.length != 0) { + for (int i = 0, len = files.length; i < len; i++) { + createFolder(files[i]); + } + } + } + + // = + + /** + * 判断目录是否存在, 不存在则判断是否创建成功 + * @param dirPath 目录路径 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + public static boolean createOrExistsDir(final String dirPath) { + return createOrExistsDir(getFileByPath(dirPath)); + } + + /** + * 判断目录是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + public static boolean createOrExistsDir(final File file) { + // 如果存在, 是目录则返回 true, 是文件则返回 false, 不存在则返回是否创建成功 + return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); + } + + /** + * 判断文件是否存在, 不存在则判断是否创建成功 + * @param filePath 文件路径 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + public static boolean createOrExistsFile(final String filePath) { + return createOrExistsFile(getFileByPath(filePath)); + } + + /** + * 判断文件是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + public static boolean createOrExistsFile(final File file) { + if (file == null) return false; + // 如果存在, 是文件则返回 true, 是目录则返回 false + if (file.exists()) return file.isFile(); + // 判断文件是否存在, 不存在则直接返回 + if (!createOrExistsDir(file.getParentFile())) return false; + try { + // 存在, 则返回新的路径 + return file.createNewFile(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "createOrExistsFile"); + return false; + } + } + + /** + * 判断文件是否存在, 存在则在创建之前删除 + * @param filePath 文件路径 + * @return {@code true} 创建成功, {@code false} 创建失败 + */ + public static boolean createFileByDeleteOldFile(final String filePath) { + return createFileByDeleteOldFile(getFileByPath(filePath)); + } + + /** + * 判断文件是否存在, 存在则在创建之前删除 + * @param file 文件 + * @return {@code true} 创建成功, {@code false} 创建失败 + */ + public static boolean createFileByDeleteOldFile(final File file) { + if (file == null) return false; + // 文件存在并且删除失败返回 false + if (file.exists() && !file.delete()) return false; + // 创建目录失败返回 false + if (!createOrExistsDir(file.getParentFile())) return false; + try { + return file.createNewFile(); + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "createFileByDeleteOldFile"); + return false; + } + } + + /** + * 获取文件路径 + * @param file 文件 + * @return 文件路径 + */ + public static String getPath(final File file) { + return file != null ? file.getPath() : null; + } + + /** + * 获取文件绝对路径 + * @param file 文件 + * @return 文件绝对路径 + */ + public static String getAbsolutePath(final File file) { + return file != null ? file.getAbsolutePath() : null; + } + + // = + + /** + * 获取文件名 + * @param file 文件 + * @return 文件名 + */ + public static String getName(final File file) { + return file != null ? file.getName() : null; + } + + /** + * 获取文件名 + * @param filePath 文件路径 + * @return 文件名 + */ + public static String getName(final String filePath) { + return getName(filePath, ""); + } + + /** + * 获取文件名 + * @param filePath 文件路径 + * @param defaultStr 默认字符串 + * @return 文件名, 如果文件路径为 null 时, 返回默认字符串 + */ + public static String getName(final String filePath, final String defaultStr) { + return isSpace(filePath) ? defaultStr : new File(filePath).getName(); + } + + /** + * 获取文件后缀名 ( 无 "." 单独后缀 ) + * @param file 文件 + * @return 文件后缀名 ( 无 "." 单独后缀 ) + */ + public static String getFileSuffix(final File file) { + return getFileSuffix(getAbsolutePath(file)); + } + + /** + * 获取文件后缀名 ( 无 "." 单独后缀 ) + * @param filePath 文件路径或文件名 + * @return 文件后缀名 ( 无 "." 单独后缀 ) + */ + public static String getFileSuffix(final String filePath) { + // 获取最后的索引 + int lastIndexOf; + // 判断是否存在 + if (filePath != null && (lastIndexOf = filePath.lastIndexOf('.')) != -1) { + String result = filePath.substring(lastIndexOf); + if (result.startsWith(".")) { + return result.substring(1); + } + return result; + } + return null; + } + + /** + * 获取文件名 ( 无后缀 ) + * @param file 文件 + * @return 文件名 ( 无后缀 ) + */ + public static String getFileNotSuffix(final File file) { + return getFileNotSuffix(getName(file)); + } + + /** + * 获取文件名 ( 无后缀 ) + * @param filePath 文件路径 + * @return 文件名 ( 无后缀 ) + */ + public static String getFileNotSuffixToPath(final String filePath) { + return getFileNotSuffix(getName(filePath)); + } + + /** + * 获取文件名 ( 无后缀 ) + * @param fileName 文件名 + * @return 文件名 ( 无后缀 ) + */ + public static String getFileNotSuffix(final String fileName) { + if (fileName != null) { + if (fileName.lastIndexOf('.') != -1) { + return fileName.substring(0, fileName.lastIndexOf('.')); + } else { + return fileName; + } + } + return null; + } + + /** + * 获取路径中的不带拓展名的文件名 + * @param file 文件 + * @return 不带拓展名的文件名 + */ + public static String getFileNameNoExtension(final File file) { + if (file == null) return null; + return getFileNameNoExtension(file.getPath()); + } + + /** + * 获取路径中的不带拓展名的文件名 + * @param filePath 文件路径 + * @return 不带拓展名的文件名 + */ + public static String getFileNameNoExtension(final String filePath) { + if (isSpace(filePath)) return filePath; + int lastPoi = filePath.lastIndexOf('.'); + int lastSep = filePath.lastIndexOf(File.separator); + if (lastSep == -1) { + return (lastPoi == -1 ? filePath : filePath.substring(0, lastPoi)); + } + if (lastPoi == -1 || lastSep > lastPoi) { + return filePath.substring(lastSep + 1); + } + return filePath.substring(lastSep + 1, lastPoi); + } + + /** + * 获取路径中的文件拓展名 + * @param file 文件 + * @return 文件拓展名 + */ + public static String getFileExtension(final File file) { + if (file == null) return null; + return getFileExtension(file.getPath()); + } + + /** + * 获取路径中的文件拓展名 + * @param filePath 文件路径 + * @return 文件拓展名 + */ + public static String getFileExtension(final String filePath) { + if (isSpace(filePath)) return filePath; + int lastPoi = filePath.lastIndexOf('.'); + int lastSep = filePath.lastIndexOf(File.separator); + if (lastPoi == -1 || lastSep >= lastPoi) return ""; + return filePath.substring(lastPoi + 1); + } + + // = + + /** + * 检查是否存在某个文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFileExists(final File file) { + return file != null && file.exists(); + } + + /** + * 检查是否存在某个文件 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFileExists(final String filePath) { + return isFileExists(getFileByPath(filePath)); + } + + /** + * 检查是否存在某个文件 + * @param filePath 文件路径 + * @param fileName 文件名 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFileExists(final String filePath, final String fileName) { + return filePath != null && fileName != null && new File(filePath, fileName).exists(); + } + + /** + * 判断是否文件 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFile(final String filePath) { + return isFile(getFileByPath(filePath)); + } + + /** + * 判断是否文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + public static boolean isFile(final File file) { + return file != null && file.exists() && file.isFile(); + } + + /** + * 判断是否文件夹 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + public static boolean isDirectory(final String filePath) { + return isDirectory(getFileByPath(filePath)); + } + + /** + * 判断是否文件夹 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + public static boolean isDirectory(final File file) { + return file != null && file.exists() && file.isDirectory(); + } + + /** + * 判断是否隐藏文件 + * @param filePath 文件路径 + * @return {@code true} yes, {@code false} no + */ + public static boolean isHidden(final String filePath) { + return isHidden(getFileByPath(filePath)); + } + + /** + * 判断是否隐藏文件 + * @param file 文件 + * @return {@code true} yes, {@code false} no + */ + public static boolean isHidden(final File file) { + return file != null && file.exists() && file.isHidden(); + } + + // = + + /** + * 获取文件最后修改的毫秒时间戳 + * @param filePath 文件路径 + * @return 文件最后修改的毫秒时间戳 + */ + public static long getFileLastModified(final String filePath) { + return getFileLastModified(getFileByPath(filePath)); + } + + /** + * 获取文件最后修改的毫秒时间戳 + * @param file 文件 + * @return 文件最后修改的毫秒时间戳 + */ + public static long getFileLastModified(final File file) { + if (file == null) return 0L; + return file.lastModified(); + } + + /** + * 获取文件编码格式 + * @param filePath 文件路径 + * @return 文件编码格式 + */ + public static String getFileCharsetSimple(final String filePath) { + return getFileCharsetSimple(getFileByPath(filePath)); + } + + /** + * 获取文件编码格式 + * @param file 文件 + * @return 文件编码格式 + */ + public static String getFileCharsetSimple(final File file) { + if (file == null) return null; + int pos = 0; + InputStream is = null; + try { + is = new BufferedInputStream(new FileInputStream(file)); + pos = (is.read() << 8) + is.read(); + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "getFileCharsetSimple"); + } finally { + closeIOQuietly(is); + } + switch (pos) { + case 0xefbb: + return "UTF-8"; + case 0xfffe: + return "Unicode"; + case 0xfeff: + return "UTF-16BE"; + default: + return "GBK"; + } + } + + /** + * 获取文件行数 + * @param filePath 文件路径 + * @return 文件行数 + */ + public static int getFileLines(final String filePath) { + return getFileLines(getFileByPath(filePath)); + } + + /** + * 获取文件行数 ( 比 readLine 要快很多 ) + * @param file 文件 + * @return 文件行数 + */ + public static int getFileLines(final File file) { + if (file == null) return 0; + int lineCount = 1; + InputStream is = null; + try { + is = new BufferedInputStream(new FileInputStream(file)); + byte[] buffer = new byte[1024]; + int readChars; + if (NEW_LINE_STR.endsWith("\n")) { + while ((readChars = is.read(buffer, 0, 1024)) != -1) { + for (int i = 0; i < readChars; ++i) { + if (buffer[i] == '\n') ++lineCount; + } + } + } else { + while ((readChars = is.read(buffer, 0, 1024)) != -1) { + for (int i = 0; i < readChars; ++i) { + if (buffer[i] == '\r') ++lineCount; + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileLines"); + } finally { + closeIOQuietly(is); + } + return lineCount; + } + + // = + + /** + * 获取文件大小 + * @param filePath 文件路径 + * @return 文件大小 + */ + public static String getFileSize(final String filePath) { + return getFileSize(getFileByPath(filePath)); + } + + /** + * 获取文件大小 + * @param file 文件 + * @return 文件大小 + */ + public static String getFileSize(final File file) { + return formatByteMemorySize(getFileLength(file)); + } + + /** + * 获取目录大小 + * @param dirPath 目录路径 + * @return 文件大小 + */ + public static String getDirSize(final String dirPath) { + return getDirSize(getFileByPath(dirPath)); + } + + /** + * 获取目录大小 + * @param dir 目录 + * @return 文件大小 + */ + public static String getDirSize(final File dir) { + return formatByteMemorySize(getDirLength(dir)); + } + + /** + * 获取文件大小 + * @param filePath 文件路径 + * @return 文件大小 + */ + public static long getFileLength(final String filePath) { + return getFileLength(getFileByPath(filePath)); + } + + /** + * 获取文件大小 + * @param file 文件 + * @return 文件大小 + */ + public static long getFileLength(final File file) { + return file != null ? file.length() : 0L; + } + + /** + * 获取目录全部文件大小 + * @param dirPath 目录路径 + * @return 目录全部文件大小 + */ + public static long getDirLength(final String dirPath) { + return getDirLength(getFileByPath(dirPath)); + } + + /** + * 获取目录全部文件大小 + * @param dir 目录 + * @return 目录全部文件大小 + */ + public static long getDirLength(final File dir) { + if (!isDirectory(dir)) return 0; + long len = 0; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (file.isDirectory()) { + len += getDirLength(file); + } else { + len += file.length(); + } + } + } + return len; + } + + /** + * 获取文件大小 - 网络资源 + * @param httpUri 文件网络链接 + * @return 文件大小 + */ + public static long getFileLengthNetwork(final String httpUri) { + if (isSpace(httpUri)) return 0L; + boolean isURL = httpUri.toLowerCase().matches("http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?"); + if (isURL) { + try { + HttpURLConnection conn = (HttpURLConnection) new URL(httpUri).openConnection(); + conn.setRequestProperty("Accept-Encoding", "identity"); + conn.connect(); + if (conn.getResponseCode() == 200) { + return conn.getContentLength(); + } + return 0L; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileLengthNetwork"); + } + } + return 0L; + } + + /** + * 获取路径中的文件名 + * @param file 文件 + * @return 文件名 + */ + public static String getFileName(final File file) { + if (file == null) return null; + return getFileName(file.getPath()); + } + + /** + * 获取路径中的文件名 + * @param filePath 文件路径 + * @return 文件名 + */ + public static String getFileName(final String filePath) { + if (isSpace(filePath)) return filePath; + int lastSep = filePath.lastIndexOf(File.separator); + return lastSep == -1 ? filePath : filePath.substring(lastSep + 1); + } + + /** + * 获取路径中的最长目录地址 + * @param file 文件 + * @return 最长目录地址 + */ + public static String getDirName(final File file) { + if (file == null) return null; + return getDirName(file.getPath()); + } + + /** + * 获取全路径中的最长目录地址 + * @param filePath 文件路径 + * @return 最长目录地址 + */ + public static String getDirName(final String filePath) { + if (isSpace(filePath)) return filePath; + int lastSep = filePath.lastIndexOf(File.separator); + return lastSep == -1 ? "" : filePath.substring(0, lastSep + 1); + } + + // = + + /** + * 重命名文件 - 同个目录下, 修改文件名 + * @param filePath 文件路径 + * @param newFileName 文件新名称 + * @return {@code true} yes, {@code false} no + */ + public static boolean rename(final String filePath, final String newFileName) { + return rename(getFileByPath(filePath), newFileName); + } + + /** + * 重命名文件 - 同个目录下, 修改文件名 + * @param file 文件 + * @param newFileName 文件新名称 + * @return {@code true} yes, {@code false} no + */ + public static boolean rename(final File file, final String newFileName) { + // 文件为空返回 false + if (file == null) return false; + // 文件不存在返回 false + if (!file.exists()) return false; + // 如果文件名没有改变返回 true + if (newFileName.equals(file.getName())) return true; + // 拼接新的文件路径 + File newFile = new File(file.getParent() + File.separator + newFileName); + // 如果重命名的文件已存在返回 false + return !newFile.exists() && file.renameTo(newFile); + } + + // ================ + // = 文件大小处理 = + // ================ + + /** + * 传入文件路径, 返回对应的文件大小 + * @param filePath 文件路径 + * @return 文件大小转换字符串 + */ + public static String formatFileSize(final String filePath) { + File file = getFileByPath(filePath); + return formatFileSize(file != null ? file.length() : 0); + } + + /** + * 传入文件路径, 返回对应的文件大小 + * @param file 文件 + * @return 文件大小转换字符串 + */ + public static String formatFileSize(final File file) { + return formatFileSize(file != null ? file.length() : 0); + } + + /** + * 传入对应的文件大小, 返回转换后文件大小 + * @param fileSize 文件大小 + * @return 文件大小转换字符串 + */ + public static String formatFileSize(final double fileSize) { + // 转换文件大小 + DecimalFormat df = new DecimalFormat("#.00"); + String fileSizeStr; + if (fileSize <= 0) { + fileSizeStr = "0B"; + } else if (fileSize < 1024) { + fileSizeStr = df.format(fileSize) + "B"; + } else if (fileSize < 1048576) { + fileSizeStr = df.format(fileSize / 1024) + "KB"; + } else if (fileSize < 1073741824) { + fileSizeStr = df.format(fileSize / 1048576) + "MB"; + } else if (fileSize < 1099511627776d) { + fileSizeStr = df.format(fileSize / 1073741824) + "GB"; + } else { + fileSizeStr = df.format(fileSize / 1099511627776d) + "TB"; + } + return fileSizeStr; + } + + /** + * 字节数转合适内存大小 保留 3 位小数 (%.位数f) + * @param byteSize 字节数 + * @return 合适内存大小字符串 + */ + public static String formatByteMemorySize(final double byteSize) { + return formatByteMemorySize(3, byteSize); + } + + /** + * 字节数转合适内存大小 保留 number 位小数 (%.位数f) + * @param number 保留小数位数 + * @param byteSize 字节数 + * @return 合适内存大小字符串 + */ + public static String formatByteMemorySize(final int number, final double byteSize) { + if (byteSize < 0d) { + return "0B"; + } else if (byteSize < 1024d) { + return String.format("%." + number + "fB", byteSize); + } else if (byteSize < 1048576d) { + return String.format("%." + number + "fKB", byteSize / 1024d); + } else if (byteSize < 1073741824d) { + return String.format("%." + number + "fMB", byteSize / 1048576d); + } else if (byteSize < 1099511627776d) { + return String.format("%." + number + "fGB", byteSize / 1073741824d); + } else { + return String.format("%." + number + "fTB", byteSize / 1099511627776d); + } + } + + // ============ + // = 文件操作 = + // ============ + + /** + * 删除文件 + * @param filePath 文件路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean deleteFile(final String filePath) { + return deleteFile(getFileByPath(filePath)); + } + + /** + * 删除文件 + * @param file 文件 + * @return {@code true} success, {@code false} fail + */ + public static boolean deleteFile(final File file) { + // 文件存在, 并且不是目录文件, 则直接删除 + if (file != null && file.exists() && !file.isDirectory()) { + return file.delete(); + } + return false; + } + + /** + * 删除多个文件 + * @param filePaths 文件路径数组 + */ + public static void deleteFiles(final String... filePaths) { + if (filePaths != null && filePaths.length != 0) { + for (int i = 0, len = filePaths.length; i < len; i++) { + deleteFile(filePaths[i]); + } + } + } + + /** + * 删除多个文件 + * @param files 文件数组 + */ + public static void deleteFiles(final File... files) { + if (files != null && files.length != 0) { + for (int i = 0, len = files.length; i < len; i++) { + deleteFile(files[i]); + } + } + } + + // = + + /** + * 删除文件夹 + * @param filePath 文件路径 + * @return {@code true} success, {@code false} fail + */ + public static boolean deleteFolder(final String filePath) { + return deleteFolder(getFileByPath(filePath)); + } + + /** + * 删除文件夹 + * @param file 文件 + * @return {@code true} success, {@code false} fail + */ + public static boolean deleteFolder(final File file) { + if (file != null) { + try { + // 文件存在, 并且不是目录文件, 则直接删除 + if (file.exists()) { + if (file.isDirectory()) { // 属于文件目录 + File[] files = file.listFiles(); + if (null == files || files.length == 0) { + file.delete(); + } + for (File f : files) { + deleteFolder(f.getPath()); + } + return file.delete(); + } else { // 属于文件 + return deleteFile(file); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "deleteFolder"); + } + } + return false; + } + + /** + * 保存文件 + * @param file 保存文件 + * @param data 保存内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveFile(final File file, final byte[] data) { + if (file != null && data != null) { + try { + // 防止文件没创建 + createFolder(getDirName(file)); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + BufferedOutputStream bos = new BufferedOutputStream(fos); + bos.write(data); + bos.close(); + fos.close(); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 保存文件 + * @param filePath 保存路径 + * @param fileName 文件名. 后缀 + * @param data 保存内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveFile(final String filePath, final String fileName, final byte[] data) { + if (filePath != null && fileName != null && data != null) { + try { + // 防止文件没创建 + createFolder(filePath); + // 保存路径 + File file = new File(filePath, fileName); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + BufferedOutputStream bos = new BufferedOutputStream(fos); + bos.write(data); + bos.close(); + fos.close(); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 保存文件 + * @param filePath 保存路径 + * @param fileName 文件名. 后缀 + * @param content 保存内容 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveFile(final String filePath, final String fileName, final String content) { + if (filePath != null && fileName != null && content != null) { + try { + // 防止文件没创建 + createFolder(filePath); + // 保存路径 + File file = new File(filePath, fileName); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + fos.write(content.getBytes()); + fos.close(); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 保存文件 + * @param filePath 保存路径 + * @param fileName 文件名. 后缀 + * @param content 保存内容 + * @param coding 编码格式 + * @return {@code true} success, {@code false} fail + */ + public static boolean saveFile(final String filePath, final String fileName, final String content, final String coding) { + if (filePath != null && fileName != null && content != null) { + try { + // 防止文件没创建 + createFolder(filePath); + // 保存路径 + File file = new File(filePath, fileName); + // 保存内容到一个文件 + FileOutputStream fos = new FileOutputStream(file); + Writer out; + if (coding != null) { + out = new OutputStreamWriter(fos, coding); + } else { + out = new OutputStreamWriter(fos); + } + out.write(content); + out.close(); + fos.close(); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "saveFile"); + } + } + return false; + } + + /** + * 追加文件 ( 使用 FileWriter) + * @param filePath 文件路径 + * @param content 追加内容 + */ + public static void appendFile(final String filePath, final String content) { + if (filePath == null || content == null) return; + File file = new File(filePath); + // 如果文件不存在, 则跳过 + if (!file.exists()) return; + FileWriter writer = null; + try { + // 打开一个写文件器, 构造函数中的第二个参数 true 表示以追加形式写文件 + writer = new FileWriter(file, true); + writer.write(content); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "appendFile"); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + } + } + } + } + + // = + + /** + * 读取文件 + * @param filePath 文件路径 + * @return 文件内容 byte[] + */ + public static byte[] readFileBytes(final String filePath) { + return readFileBytes(getFileByPath(filePath)); + } + + /** + * 读取文件 + * @param file 文件 + * @return 文件内容 byte[] + */ + public static byte[] readFileBytes(final File file) { + if (file != null && file.exists()) { + try { + FileInputStream fis = new FileInputStream(file); + int length = fis.available(); + byte[] buffer = new byte[length]; + fis.read(buffer); + fis.close(); + return buffer; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "readFileBytes"); + } + } + return null; + } + + /** + * 读取文件 + * @param inputStream {@link InputStream} + * @return 文件内容 byte[] + */ + public static byte[] readFileBytes(final InputStream inputStream) { + if (inputStream != null) { + try { + int length = inputStream.available(); + byte[] buffer = new byte[length]; + inputStream.read(buffer); + inputStream.close(); + return buffer; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "readFileBytes"); + } + } + return null; + } + + /** + * 读取文件 + * @param filePath 文件路径 + * @return 文件内容字符串 + */ + public static String readFile(final String filePath) { + return readFile(getFileByPath(filePath)); + } + + /** + * 读取文件 + * @param file 文件 + * @return 文件内容字符串 + */ + public static String readFile(final File file) { + if (file != null && file.exists()) { + try { + return readFile(new FileInputStream(file)); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "readFile"); + } + } + return null; + } + + /** + * 读取文件 + * @param inputStream {@link InputStream} new FileInputStream(path) + * @return 文件内容字符串 + */ + public static String readFile(final InputStream inputStream) { + if (inputStream != null) { + try { + InputStreamReader isr = new InputStreamReader(inputStream); + BufferedReader br = new BufferedReader(isr); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + builder.append(line); + } + isr.close(); + br.close(); + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "readFile"); + } + } + return null; + } + + /** + * 读取文件 + * @param inputStream {@link InputStream} new FileInputStream(path) + * @param encode 编码格式 + * @return 文件内容字符串 + */ + public static String readFile(final InputStream inputStream, final String encode) { + if (inputStream != null) { + try { + InputStreamReader isr = null; + if (encode != null) { + new InputStreamReader(inputStream, encode); + } else { + new InputStreamReader(inputStream); + } + BufferedReader br = new BufferedReader(isr); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + builder.append(line); + } + isr.close(); + br.close(); + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "readFile"); + } + } + return null; + } + + // = + + /** + * 复制单个文件 + * @param inputStream 文件流 ( 被复制 ) + * @param destFilePath 目标文件地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + public static boolean copyFile(final InputStream inputStream, final String destFilePath, final boolean overlay) { + if (inputStream == null || destFilePath == null) { + return false; + } + File destFile = new File(destFilePath); + // 如果属于文件夹则跳过 + if (destFile.isDirectory()) { + return false; + } + if (destFile.exists()) { + // 如果目标文件存在并允许覆盖 + if (overlay) { + // 删除已经存在的目标文件, 无论目标文件是目录还是单个文件 + destFile.delete(); + } + } else { + // 如果目标文件所在目录不存在, 则创建目录 + if (!destFile.getParentFile().exists()) { + // 目标文件所在目录不存在 + if (!destFile.getParentFile().mkdirs()) { + // 复制文件失败: 创建目标文件所在目录失败 + return false; + } + } + } + // 复制文件 + int byteread = 0; // 读取的字节数 + InputStream is = inputStream; + OutputStream os = null; + try { + os = new FileOutputStream(destFile); + byte[] buffer = new byte[1024]; + while ((byteread = is.read(buffer)) != -1) { + os.write(buffer, 0, byteread); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "copyFile"); + return false; + } finally { + try { + if (os != null) + os.close(); + if (is != null) + is.close(); + } catch (IOException e) { + } + } + } + + /** + * 复制单个文件 + * @param srcFilePath 待复制的文件地址 + * @param destFilePath 目标文件地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + public static boolean copyFile(final String srcFilePath, final String destFilePath, final boolean overlay) { + if (srcFilePath == null || destFilePath == null) { + return false; + } + File srcFile = new File(srcFilePath); + // 判断源文件是否存在 + if (!srcFile.exists()) { + return false; + } else if (!srcFile.isFile()) { // srcFile.isDirectory(); + return false; + } + // 判断目标文件是否存在 + File destFile = new File(destFilePath); + // 如果属于文件夹则跳过 + if (destFile.isDirectory()) { + return false; + } + if (destFile.exists()) { + // 如果目标文件存在并允许覆盖 + if (overlay) { + // 删除已经存在的目标文件, 无论目标文件是目录还是单个文件 + new File(destFilePath).delete(); + } + } else { + // 如果目标文件所在目录不存在, 则创建目录 + if (!destFile.getParentFile().exists()) { + // 目标文件所在目录不存在 + if (!destFile.getParentFile().mkdirs()) { + // 复制文件失败: 创建目标文件所在目录失败 + return false; + } + } + } + // 复制文件 + int byteread = 0; // 读取的字节数 + InputStream is = null; + OutputStream os = null; + try { + is = new FileInputStream(srcFile); + os = new FileOutputStream(destFile); + byte[] buffer = new byte[1024]; + while ((byteread = is.read(buffer)) != -1) { + os.write(buffer, 0, byteread); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "copyFile"); + return false; + } finally { + try { + if (os != null) + os.close(); + if (is != null) + is.close(); + } catch (IOException e) { + } + } + } + + /** + * 复制文件夹 + * @param srcFolderPath 待复制的文件夹地址 + * @param destFolderPath 目标文件夹地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + public static boolean copyFolder(final String srcFolderPath, final String destFolderPath, final boolean overlay) { + return copyFolder(srcFolderPath, destFolderPath, srcFolderPath, overlay); + } + + /** + * 复制文件夹 + * @param srcFolderPath 待复制的文件夹地址 + * @param destFolderPath 目标文件夹地址 + * @param sourcePath 源文件地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + private static boolean copyFolder(final String srcFolderPath, final String destFolderPath, final String sourcePath, final boolean overlay) { + if (srcFolderPath == null || destFolderPath == null || sourcePath == null) { + return false; + } + File srcFile = new File(srcFolderPath); + // 判断源文件是否存在 + if (!srcFile.exists()) { + return false; + } else if (!srcFile.isDirectory()) { // 不属于文件夹则跳过 + return false; + } + // 判断目标文件是否存在 + File destFile = new File(destFolderPath); + // 如果文件夹没创建, 则创建 + if (!destFile.exists()) { + // 允许创建多级目录 + destFile.mkdirs(); + } + // 判断是否属于文件夹 + if (!destFile.isDirectory()) { // 不属于文件夹则跳过 + return false; + } + // 判断是否存在 + if (srcFile.exists()) { + // 获取文件路径 + File[] files = srcFile.listFiles(); + // 防止不存在文件 + if (files != null && files.length != 0) { + // 进行遍历 + for (File file : files) { + // 文件存在才进行处理 + if (file.exists()) { + // 属于文件夹 + if (file.isDirectory()) { + copyFolder(file.getAbsolutePath(), destFolderPath, sourcePath, overlay); + } else { // 属于文件 + // 复制的文件地址 + String filePath = file.getAbsolutePath(); + // 获取源文件地址 - 并且进行判断 + String dealSource = new File(sourcePath).getAbsolutePath(); + // 属于最前才进行处理 + if (filePath.indexOf(dealSource) == 0) { + // 获取处理后的地址 + dealSource = filePath.substring(dealSource.length()); + // 获取需要复制保存的地址 + String savePath = new File(destFolderPath, dealSource).getAbsolutePath(); + // 进行复制文件 + boolean isResult = copyFile(filePath, savePath, overlay); + } + } + } + } + } + } + return true; + } + + // = + + /** + * 移动 ( 剪切 ) 文件 + * @param srcFilePath 待移动的文件地址 + * @param destFilePath 目标文件地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + public static boolean moveFile(final String srcFilePath, final String destFilePath, final boolean overlay) { + // 复制文件 + if (copyFile(srcFilePath, destFilePath, overlay)) { + // 删除文件 + return deleteFile(srcFilePath); + } + return false; + } + + /** + * 移动 ( 剪切 ) 文件夹 + * @param srcFilePath 待移动的文件夹地址 + * @param destFilePath 目标文件夹地址 + * @param overlay 如果目标文件存在, 是否覆盖 + * @return {@code true} success, {@code false} fail + */ + public static boolean moveFolder(final String srcFilePath, final String destFilePath, final boolean overlay) { + // 复制文件夹 + if (copyFolder(srcFilePath, destFilePath, overlay)) { + // 删除文件夹 + return deleteFolder(srcFilePath); + } + return false; + } + + // ============= + // = FileUtils = + // ============= + + /** + * 复制或移动目录 + * @param srcDirPath 源目录路径 + * @param destDirPath 目标目录路径 + * @param listener 是否覆盖监听器 + * @param isMove 是否移动 + * @return {@code true} 复制或移动成功, {@code false} 复制或移动失败 + */ + public static boolean copyOrMoveDir(final String srcDirPath, final String destDirPath, final OnReplaceListener listener, final boolean isMove) { + return copyOrMoveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener, isMove); + } + + /** + * 复制或移动目录 + * @param srcDir 源目录 + * @param destDir 目标目录 + * @param listener 是否覆盖监听器 + * @param isMove 是否移动 + * @return {@code true} 复制或移动成功, {@code false} 复制或移动失败 + */ + public static boolean copyOrMoveDir(final File srcDir, final File destDir, final OnReplaceListener listener, final boolean isMove) { + if (srcDir == null || destDir == null || listener == null) return false; + // 为防止以上这种情况出现出现误判, 须分别在后面加个路径分隔符 + String srcPath = srcDir.getPath() + File.separator; + String destPath = destDir.getPath() + File.separator; + if (destPath.contains(srcPath)) return false; + // 源文件不存在或者不是目录则返回 false + if (!srcDir.exists() || !srcDir.isDirectory()) return false; + if (destDir.exists()) { + if (listener.onReplace()) { // 需要覆盖则删除旧目录 + if (!deleteAllInDir(destDir)) { // 删除文件失败的话返回 false + return false; + } + } else { // 不需要覆盖直接返回即可 true + return true; + } + } + // 目标目录不存在返回 false + if (!createOrExistsDir(destDir)) return false; + File[] files = srcDir.listFiles(); + for (File file : files) { + File oneDestFile = new File(destPath + file.getName()); + if (file.isFile()) { + // 如果操作失败返回 false + if (!copyOrMoveFile(file, oneDestFile, listener, isMove)) return false; + } else if (file.isDirectory()) { + // 如果操作失败返回 false + if (!copyOrMoveDir(file, oneDestFile, listener, isMove)) return false; + } + } + return !isMove || deleteDir(srcDir); + } + + /** + * 复制或移动文件 + * @param srcFilePath 源文件路径 + * @param destFilePath 目标文件路径 + * @param listener 是否覆盖监听器 + * @param isMove 是否移动 + * @return {@code true} 复制或移动成功, {@code false} 复制或移动失败 + */ + public static boolean copyOrMoveFile(final String srcFilePath, final String destFilePath, final OnReplaceListener listener, final boolean isMove) { + return copyOrMoveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener, isMove); + } + + /** + * 复制或移动文件 + * @param srcFile 源文件 + * @param destFile 目标文件 + * @param listener 是否覆盖监听器 + * @param isMove 是否移动 + * @return {@code true} 复制或移动成功, {@code false} 复制或移动失败 + */ + public static boolean copyOrMoveFile(final File srcFile, final File destFile, final OnReplaceListener listener, final boolean isMove) { + if (srcFile == null || destFile == null || listener == null) return false; + // 如果源文件和目标文件相同则返回 false + if (srcFile.equals(destFile)) return false; + // 源文件不存在或者不是文件则返回 false + if (!srcFile.exists() || !srcFile.isFile()) return false; + if (destFile.exists()) { // 目标文件存在 + if (listener.onReplace()) { // 需要覆盖则删除旧文件 + if (!destFile.delete()) { // 删除文件失败的话返回 false + return false; + } + } else { // 不需要覆盖直接返回即可 true + return true; + } + } + // 目标目录不存在返回 false + if (!createOrExistsDir(destFile.getParentFile())) return false; + try { + return writeFileFromIS(destFile, new FileInputStream(srcFile), false) && !(isMove && !deleteFile(srcFile)); + } catch (FileNotFoundException e) { + JCLogUtils.eTag(TAG, e, "copyOrMoveFile"); + return false; + } + } + + /** + * 复制目录 + * @param srcDirPath 源目录路径 + * @param destDirPath 目标目录路径 + * @param listener 是否覆盖监听器 + * @return {@code true} 复制成功, {@code false} 复制失败 + */ + public static boolean copyDir(final String srcDirPath, final String destDirPath, final OnReplaceListener listener) { + return copyDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener); + } + + /** + * 复制目录 + * @param srcDir 源目录 + * @param destDir 目标目录 + * @param listener 是否覆盖监听器 + * @return {@code true} 复制成功, {@code false} 复制失败 + */ + public static boolean copyDir(final File srcDir, final File destDir, final OnReplaceListener listener) { + return copyOrMoveDir(srcDir, destDir, listener, false); + } + + /** + * 复制文件 + * @param srcFilePath 源文件路径 + * @param destFilePath 目标文件路径 + * @param listener 是否覆盖监听器 + * @return {@code true} 复制成功, {@code false} 复制失败 + */ + public static boolean copyFile(final String srcFilePath, final String destFilePath, final OnReplaceListener listener) { + return copyFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener); + } + + /** + * 复制文件 + * @param srcFile 源文件 + * @param destFile 目标文件 + * @param listener 是否覆盖监听器 + * @return {@code true} 复制成功, {@code false} 复制失败 + */ + public static boolean copyFile(final File srcFile, final File destFile, final OnReplaceListener listener) { + return copyOrMoveFile(srcFile, destFile, listener, false); + } + + /** + * 移动目录 + * @param srcDirPath 源目录路径 + * @param destDirPath 目标目录路径 + * @param listener 是否覆盖监听器 + * @return {@code true} 移动成功, {@code false} 移动失败 + */ + public static boolean moveDir(final String srcDirPath, final String destDirPath, final OnReplaceListener listener) { + return moveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener); + } + + /** + * 移动目录 + * @param srcDir 源目录 + * @param destDir 目标目录 + * @param listener 是否覆盖监听器 + * @return {@code true} 移动成功, {@code false} 移动失败 + */ + public static boolean moveDir(final File srcDir, final File destDir, final OnReplaceListener listener) { + return copyOrMoveDir(srcDir, destDir, listener, true); + } + + /** + * 移动文件 + * @param srcFilePath 源文件路径 + * @param destFilePath 目标文件路径 + * @param listener 是否覆盖监听器 + * @return {@code true} 移动成功, {@code false} 移动失败 + */ + public static boolean moveFile(final String srcFilePath, final String destFilePath, final OnReplaceListener listener) { + return moveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener); + } + + /** + * 移动文件 + * @param srcFile 源文件 + * @param destFile 目标文件 + * @param listener 是否覆盖监听器 + * @return {@code true} 移动成功, {@code false} 移动失败 + */ + public static boolean moveFile(final File srcFile, final File destFile, final OnReplaceListener listener) { + return copyOrMoveFile(srcFile, destFile, listener, true); + } + + /** + * 删除目录 + * @param dirPath 目录路径 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteDir(final String dirPath) { + return deleteDir(getFileByPath(dirPath)); + } + + /** + * 删除目录 + * @param dir 目录 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteDir(final File dir) { + if (dir == null) return false; + // dir doesn't exist then return true + if (!dir.exists()) return true; + // dir isn't a directory then return false + if (!dir.isDirectory()) return false; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (file.isFile()) { + if (!file.delete()) return false; + } else if (file.isDirectory()) { + if (!deleteDir(file)) return false; + } + } + } + return dir.delete(); + } + + /** + * 删除目录下所有东西 + * @param dirPath 目录路径 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteAllInDir(final String dirPath) { + return deleteAllInDir(getFileByPath(dirPath)); + } + + /** + * 删除目录下所有东西 + * @param dir 目录 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteAllInDir(final File dir) { + return deleteFilesInDirWithFilter(dir, new FileFilter() { + @Override + public boolean accept(File pathname) { + return true; + } + }); + } + + /** + * 删除目录下所有文件 + * @param dirPath 目录路径 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteFilesInDir(final String dirPath) { + return deleteFilesInDir(getFileByPath(dirPath)); + } + + /** + * 删除目录下所有文件 + * @param dir 目录 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteFilesInDir(final File dir) { + return deleteFilesInDirWithFilter(dir, new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.isFile(); + } + }); + } + + /** + * 删除目录下所有过滤的文件 + * @param dirPath 目录路径 + * @param filter 过滤器 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteFilesInDirWithFilter(final String dirPath, final FileFilter filter) { + return deleteFilesInDirWithFilter(getFileByPath(dirPath), filter); + } + + /** + * 删除目录下所有过滤的文件 + * @param dir 目录 + * @param filter 过滤器 + * @return {@code true} 删除成功, {@code false} 删除失败 + */ + public static boolean deleteFilesInDirWithFilter(final File dir, final FileFilter filter) { + if (filter == null) return false; + // dir is null then return false + if (dir == null) return false; + // dir doesn't exist then return true + if (!dir.exists()) return true; + // dir isn't a directory then return false + if (!dir.isDirectory()) return false; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (filter.accept(file)) { + if (file.isFile()) { + if (!file.delete()) return false; + } else if (file.isDirectory()) { + if (!deleteDir(file)) return false; + } + } + } + } + return true; + } + + /** + * 获取目录下所有文件 - 不递归进子目录 + * @param dirPath 目录路径 + * @return 文件链表 + */ + public static List listFilesInDir(final String dirPath) { + return listFilesInDir(dirPath, false); + } + + /** + * 获取目录下所有文件 - 不递归进子目录 + * @param dir 目录 + * @return 文件链表 + */ + public static List listFilesInDir(final File dir) { + return listFilesInDir(dir, false); + } + + /** + * 获取目录下所有文件 + * @param dirPath 目录路径 + * @param isRecursive 是否递归进子目录 + * @return 文件链表 + */ + public static List listFilesInDir(final String dirPath, final boolean isRecursive) { + return listFilesInDir(getFileByPath(dirPath), isRecursive); + } + + /** + * 获取目录下所有文件 + * @param dir 目录 + * @param isRecursive 是否递归进子目录 + * @return 文件链表 + */ + public static List listFilesInDir(final File dir, final boolean isRecursive) { + return listFilesInDirWithFilter(dir, new FileFilter() { + @Override + public boolean accept(File pathname) { + return true; + } + }, isRecursive); + } + + /** + * 获取目录下所有过滤的文件 - 不递归进子目录 + * @param dirPath 目录路径 + * @param filter 过滤器 + * @return 文件链表 + */ + public static List listFilesInDirWithFilter(final String dirPath, final FileFilter filter) { + return listFilesInDirWithFilter(getFileByPath(dirPath), filter, false); + } + + /** + * 获取目录下所有过滤的文件 - 不递归进子目录 + * @param dir 目录 + * @param filter 过滤器 + * @return 文件链表 + */ + public static List listFilesInDirWithFilter(final File dir, final FileFilter filter) { + return listFilesInDirWithFilter(dir, filter, false); + } + + /** + * 获取目录下所有过滤的文件 + * @param dirPath 目录路径 + * @param filter 过滤器 + * @param isRecursive 是否递归进子目录 + * @return 文件链表 + */ + public static List listFilesInDirWithFilter(final String dirPath, final FileFilter filter, final boolean isRecursive) { + return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive); + } + + /** + * 获取目录下所有过滤的文件 + * @param dir 目录 + * @param filter 过滤器 + * @param isRecursive 是否递归进子目录 + * @return 文件链表 + */ + public static List listFilesInDirWithFilter(final File dir, final FileFilter filter, final boolean isRecursive) { + if (!isDirectory(dir) || filter == null) return null; + List list = new ArrayList<>(); + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (filter.accept(file)) { + list.add(file); + } + if (isRecursive && file.isDirectory()) { + // noinspection ConstantConditions + list.addAll(listFilesInDirWithFilter(file, filter, true)); + } + } + } + return list; + } + + /** + * detail: 覆盖 / 替换事件 + * @author Ttt + */ + public interface OnReplaceListener { + + /** + * 是否覆盖 / 替换文件 + * @return {@code true} yes, {@code false} no + */ + boolean onReplace(); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============ + // = MD5Utils = + // ============ + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值 + */ + public static byte[] getFileMD5(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileMD5(file); + } + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String getFileMD5ToHexString(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileMD5ToHexString(file); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String getFileMD5ToHexString(final File file) { + return toHexString(getFileMD5(file)); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值 byte[] + */ + public static byte[] getFileMD5(final File file) { + if (file == null) return null; + DigestInputStream dis = null; + try { + FileInputStream fis = new FileInputStream(file); + MessageDigest digest = MessageDigest.getInstance("MD5"); + dis = new DigestInputStream(fis, digest); + byte[] buffer = new byte[256 * 1024]; + while (true) { + if (!(dis.read(buffer) > 0)) break; + } + digest = dis.getMessageDigest(); + return digest.digest(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileMD5"); + return null; + } finally { + if (dis != null) { + try { + dis.close(); + } catch (Exception e) { + } + } + } + } + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制 String + */ + private static String toHexString(final byte[] data) { + return toHexString(data, HEX_DIGITS); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // ============== + // = CloseUtils = + // ============== + + /** + * 安静关闭 IO + * @param closeables Closeable[] + */ + private static void closeIOQuietly(final Closeable... closeables) { + if (closeables == null) return; + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception ignore) { + } + } + } + } + + // =============== + // = FileIOUtils = + // =============== + + // 缓存大小 + private static final int sBufferSize = 8192; + + /** + * 通过输入流写入文件 + * @param file 文件 + * @param inputStream {@link InputStream} + * @param append 是否追加到结尾 + * @return {@code true} success, {@code false} fail + */ + private static boolean writeFileFromIS(final File file, final InputStream inputStream, final boolean append) { + if (!createOrExistsFile(file) || inputStream == null) return false; + OutputStream os = null; + try { + os = new BufferedOutputStream(new FileOutputStream(file, append)); + byte[] data = new byte[sBufferSize]; + int len; + while ((len = inputStream.read(data, 0, sBufferSize)) != -1) { + os.write(data, 0, len); + } + return true; + } catch (IOException e) { + JCLogUtils.eTag(TAG, e, "writeFileFromIS"); + return false; + } finally { + closeIOQuietly(inputStream, os); + } + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/HttpParamsUtils.java b/lib/DevJava/src/main/java/dev/utils/common/HttpParamsUtils.java new file mode 100644 index 0000000000..25fd3c7b30 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/HttpParamsUtils.java @@ -0,0 +1,229 @@ +package dev.utils.common; + +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: Http 参数工具类 + * @author Ttt + */ +public final class HttpParamsUtils { + + private HttpParamsUtils() { + } + + // 日志 TAG + private static final String TAG = HttpParamsUtils.class.getSimpleName(); + + /** + * 拆分参数 + * @param params 请求参数字符串 + * @return 拆分后的参数 Map + */ + public static Map splitParams(final String params) { + return splitParams(params, false); + } + + /** + * 拆分参数 + * @param params 请求参数字符串 + * @param urlEncode 是否需要 URL 编码 + * @return 拆分后的参数 Map + */ + public static Map splitParams(final String params, final boolean urlEncode) { + Map mapParams = new HashMap<>(); + if (params != null) { + // 拆分数据 + String[] keyValues = params.split("&"); + // 数据长度 + int valLength = 0; + // 进行循环遍历 + for (String val : keyValues) { + // 数据不为 null + if (val != null && (valLength = val.length()) != 0) { + // 获取首位 = 索引 + int indexOf = val.indexOf('='); + // 不存在则不处理 + if (indexOf != -1) { + // 获取 key + String key = val.substring(0, indexOf); + // 获取 value + String value = null; + // 防止资源浪费 + if (indexOf + 1 == valLength) { + value = ""; + } else { + value = val.substring(indexOf + 1, valLength); + } + // 判断是否编码 + if (urlEncode) { + mapParams.put(key, toUrlEncode(value)); + } else { + mapParams.put(key, value); + } + } + } + } + } + return mapParams; + } + + // = + + /** + * 拼接请求参数 - value(String) + * @param mapParams Map 请求参数 + * @return 拼接后的参数 + */ + public static String joinParams(final Map mapParams) { + return joinParams(mapParams, false); + } + + /** + * 拼接请求参数 - value(String) + * @param mapParams Map 请求参数 + * @param urlEncode 是否需要 URL 编码 + * @return 拼接后的参数 + */ + public static String joinParams(final Map mapParams, final boolean urlEncode) { + if (mapParams != null) { + int index = 0; + // = + StringBuilder builder = new StringBuilder(); + Iterator> iterator = mapParams.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + // = + if (index > 0) builder.append('&'); + builder.append(entry.getKey()); + builder.append('='); + builder.append(urlEncode ? toUrlEncode(entry.getValue()) : entry.getValue()); + index++; + } + return builder.toString(); + } + return null; + } + + // = + + /** + * 拼接请求参数 - value(Object) + * @param mapParams Map 请求参数 + * @return 拼接后的参数 + */ + public static String joinParamsObj(final Map mapParams) { + return joinParamsObj(mapParams, false); + } + + /** + * 拼接请求参数 - value(Object) + * @param mapParams Map 请求参数 + * @param urlEncode 是否需要 URL 编码 + * @return 拼接后的参数 + */ + public static String joinParamsObj(final Map mapParams, final boolean urlEncode) { + if (mapParams != null) { + int index = 0; + // = + StringBuilder builder = new StringBuilder(); + Iterator> iterator = mapParams.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + // = + if (index > 0) builder.append('&'); + builder.append(entry.getKey()); + builder.append('='); + if (urlEncode) { + if (entry.getValue() instanceof String) { + builder.append(toUrlEncode((String) entry.getValue())); + } + } else { + builder.append(entry.getValue()); + } + index++; + } + return builder.toString(); + } + return null; + } + + // ============================================ + // = 拼接成, 模拟 JavaScript 传递对象数组格式 = + // ============================================ + + /** + * 进行转换对象处理 ( 请求发送对象 ) + * @param mapParams Map 请求参数 + * @param objStr 数组名 + * @param key 数组 key + * @param value 数组 [key] 保存值 + */ + public static void toConvertObjToMS(final Map mapParams, final String objStr, final String key, final String value) { + if (mapParams != null) { + String data = null; + try { + data = URLEncoder.encode(value, "UTF-8"); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toConvertObjToMS"); + } + mapParams.put(objStr + "[" + key + "]", data); + } + } + + /** + * 进行转换对象处理 ( 请求发送对象 ) + * @param mapParams Map 请求参数 + * @param objStr 数组名 + * @param key 数组 key + * @param value 数组 [key] 保存值 + */ + public static void toConvertObjToMO(final Map mapParams, final String objStr, final String key, final Object value) { + if (mapParams != null) { + Object data = null; + try { + data = URLEncoder.encode(value.toString(), "UTF-8"); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toConvertObjToMO"); + } + mapParams.put(objStr + "[" + key + "]", data); + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 进行 URL 编码, 默认 UTF-8 + * @param str 待处理字符串 + * @return UTF-8 编码格式 URL 编码后的字符串 + */ + public static String toUrlEncode(final String str) { + return toUrlEncode(str, "UTF-8"); + } + + /** + * 进行 URL 编码 + * @param str 待处理字符串 + * @param enc 编码格式 + * @return 指定编码格式 URL 编码后的字符串 + */ + public static String toUrlEncode(final String str, final String enc) { + if (str == null || enc == null) return null; + try { + return URLEncoder.encode(str, enc); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toUrlEncode"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/HttpURLConnectionUtils.java b/lib/DevJava/src/main/java/dev/utils/common/HttpURLConnectionUtils.java new file mode 100644 index 0000000000..b3b2641929 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/HttpURLConnectionUtils.java @@ -0,0 +1,267 @@ +package dev.utils.common; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Iterator; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: HttpURLConnection 网络工具类 + * @author Ttt + */ +public final class HttpURLConnectionUtils { + + private HttpURLConnectionUtils() { + } + + // 日志 TAG + private static final String TAG = HttpURLConnectionUtils.class.getSimpleName(); + // 请求超时时间 + private static final int TIMEOUT_IN_MILLIONS = 5000; + + /** + * detail: 请求回调 + * @author Ttt + */ + public interface CallBack { + + /** + * 请求响应回调 + * @param result 请求结果 + * @param response 请求响应时间 + */ + void onResponse(String result, long response); + + /** + * 请求失败 + * @param e 失败异常 + */ + void onFail(Exception e); + } + + /** + * 异步的 Get 请求 + * @param urlStr 请求地址 + * @param callBack 请求回调接口 + */ + public static void doGetAsyn(final String urlStr, final CallBack callBack) { + new Thread() { + public void run() { + try { + request("GET", urlStr, null, null, callBack); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "doGetAsyn"); + } + } + }.start(); + } + + /** + * 异步的 Post 请求 + * @param urlStr 请求地址 + * @param params 请求参数 + * @param callBack 请求回调接口 + */ + public static void doPostAsyn(final String urlStr, final String params, final CallBack callBack) { + new Thread() { + public void run() { + try { + request("POST", urlStr, null, params, callBack); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "doPostAsyn"); + } + } + }.start(); + } + + /** + * 发送请求 + * @param method 请求方法 + * @param urlStr 请求地址字符串 + * @param headers 请求头信息 + * @param params 请求参数 + * @param callBack 请求回调接口 + */ + public static void request(final String method, final String urlStr, final Map headers, final String params, final CallBack callBack) { + // 获取连接对象 + HttpURLConnection connection = null; + InputStream is = null; + ByteArrayOutputStream baos = null; + try { + // 请求路径 + URL url = new URL(urlStr); + // 获取连接对象 + connection = (HttpURLConnection) url.openConnection(); + // 设置请求方法 + connection.setRequestMethod(method); + // 设置请求头信息 + if (headers != null) { + Iterator> iterator = headers.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + connection.setRequestProperty(entry.getKey(), entry.getValue()); + } + } + // 判断是否需要写入数据 + if (params != null && params.length() != 0) { + // 允许写入 + connection.setDoInput(true); + // 设置是否向 connection 输出, 如果是 post 请求, 参数要放在 http 正文内, 因此需要设为 true + connection.setDoOutput(true); + // post 请求不能使用缓存 + connection.setUseCaches(false); + // 写入数据 + OutputStream os = connection.getOutputStream(); + os.write(params.getBytes()); + os.flush(); + os.close(); + } + // 单位是毫秒 + connection.setConnectTimeout(TIMEOUT_IN_MILLIONS); // 设置连接超时 + connection.setReadTimeout(TIMEOUT_IN_MILLIONS); // 设置读取超时 + // 获取请求状态码 + int responseCode = connection.getResponseCode(); + // 判断请求码是否是 200 + if (responseCode >= 200 && responseCode < 300) { + // 输入流 + is = connection.getInputStream(); + baos = new ByteArrayOutputStream(); + // 设置缓存流大小 + byte[] buffer = new byte[1024]; + int len = 0; + while (((len = is.read(buffer)) != -1)) { + baos.write(buffer, 0, len); + } + // 获取请求结果 + String result = new String(baos.toByteArray()); + // 判断是否回调 + if (callBack != null) { + // 请求成功, 触发回调 + callBack.onResponse(result, connection.getDate()); + } + } else { + // 响应成功, 非 200 直接返回 null + if (callBack != null) { + callBack.onFail(new Exception("responseCode not >= 200 or < 300, code: " + responseCode)); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "request"); + if (callBack != null) { + callBack.onFail(e); + } + } finally { + if (baos != null) { + try { + baos.close(); + } catch (Exception ignore) { + } + } + if (is != null) { + try { + is.close(); + } catch (Exception ignore) { + } + } + if (connection != null) { + try { + // 关闭底层连接 Socket + connection.disconnect(); + } catch (Exception ignore) { + } + } + } + } + + // ==================== + // = 获取网络时间处理 = + // ==================== + + public static final String BAIDU_URL = "https://www.baidu.com"; + + /** + * detail: 时间回调 + * @author Ttt + */ + public interface TimeCallBack { + + /** + * 请求响应回调 + * @param time 响应时间 ( 毫秒 ) + */ + void onResponse(long time); + + /** + * 请求失败 + * @param e 失败异常 + */ + void onFail(Exception e); + } + + /** + * 获取网络时间 - 默认使用百度链接 + * @param timeCallBack 请求时间回调接口 + */ + public static void getNetTime(final TimeCallBack timeCallBack) { + getNetTime(BAIDU_URL, timeCallBack); + } + + /** + * 获取网络时间 + * @param urlStr 请求地址 + * @param timeCallBack 请求时间回调接口 + */ + public static void getNetTime(final String urlStr, final TimeCallBack timeCallBack) { + new Thread(new Runnable() { + @Override + public void run() { + reqNetTime(urlStr, timeCallBack); + } + }).start(); + } + + /** + * 请求网络时间 ( 内部私有 ) + * @param urlStr 请求地址 + * @param timeCallBack 请求时间回调接口 + */ + private static void reqNetTime(final String urlStr, final TimeCallBack timeCallBack) { + // 获取连接对象 + HttpURLConnection connection = null; + try { + // 请求路径 + URL url = new URL(urlStr); + // 获取连接对象 + connection = (HttpURLConnection) url.openConnection(); + // 获取时间 + long date = connection.getDate(); + // 获取失败, 则进行修改 + if (date <= 0) { + date = -1L; + } + // 触发回调 + if (timeCallBack != null) { + timeCallBack.onResponse(date); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getNetTime"); + // 触发回调 + if (timeCallBack != null) { + timeCallBack.onFail(e); + } + } finally { + if (connection != null) { + try { + // 关闭底层连接 Socket + connection.disconnect(); + } catch (Exception ignore) { + } + } + } + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/MapUtils.java b/lib/DevJava/src/main/java/dev/utils/common/MapUtils.java new file mode 100644 index 0000000000..bccd410984 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/MapUtils.java @@ -0,0 +1,1162 @@ +package dev.utils.common; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: Map 工具类 + * @author Ttt + *
+ *     @see 
+ * 
+ */ +public final class MapUtils { + + private MapUtils() { + } + + // 日志 TAG + private static final String TAG = MapUtils.class.getSimpleName(); + + // ======= + // = Map = + // ======= + + /** + * 判断 Map 是否为 null + * @param map {@link Map} + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Map map) { + return (map == null || map.size() == 0); + } + + /** + * 判断 Map 是否不为 null + * @param map {@link Map} + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Map map) { + return (map != null && map.size() != 0); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取 Map 长度 + * @param map {@link Map} + * @return 如果 Map 为 null, 则返回默认长度, 如果不为 null, 则返回 map.size() + */ + public static int length(final Map map) { + return length(map, 0); + } + + /** + * 获取 Map 长度 + * @param map {@link Map} + * @param defaultLength 集合为 null 默认长度 + * @return 如果 Map 为 null, 则返回 defaultLength, 如果不为 null, 则返回 map.size() + */ + public static int length(final Map map, final int defaultLength) { + return map != null ? map.size() : defaultLength; + } + + // = + + /** + * 获取长度 to Map 是否等于期望长度 + * @param map {@link Map} + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final Map map, final int length) { + return map != null && map.size() == length; + } + + // = + + /** + * 判断 Map 长度是否大于指定长度 + * @param map {@link Map} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean greaterThan(final Map map, final int length) { + return map != null && map.size() > length; + } + + /** + * 判断 Map 长度是否大于等于指定长度 + * @param map {@link Map} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean greaterThanOrEqual(final Map map, final int length) { + return map != null && map.size() >= length; + } + + // = + + /** + * 判断 Map 长度是否小于指定长度 + * @param map {@link Map} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean lessThan(final Map map, final int length) { + return map != null && map.size() < length; + } + + /** + * 判断 Map 长度是否小于等于指定长度 + * @param map {@link Map} + * @param length 指定长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean lessThanOrEqual(final Map map, final int length) { + return map != null && map.size() <= length; + } + + // ============ + // = 数据获取 = + // ============ + + /** + * 获取 value + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return 指定 key 的 value + */ + public static V get(final Map map, final K key) { + if (map != null) { + try { + return map.get(key); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return null; + } + + /** + * 获取 value 如果 value 为 null, 则返回 defaultValue + * @param map {@link Map} + * @param key key + * @param defaultValue 默认 value + * @param key + * @param value + * @return 指定 key 的 value 如果 value 为 null, 则返回 defaultValue + */ + public static V get(final Map map, final K key, final V defaultValue) { + if (map != null) { + try { + V value = map.get(key); + if (value == null) { + return defaultValue; + } else { + return value; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "get"); + } + } + return null; + } + + /** + * 通过 value 获取 key + * @param map {@link Map} + * @param value Value + * @param key + * @param value + * @return 等于 value 的 key + */ + public static K getKeyByValue(final Map map, final V value) { + if (map != null) { + try { + List lists = new ArrayList<>(); + // 进行遍历判断 + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + V v = entry.getValue(); + if (equals(v, value)) { + return entry.getKey(); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getKeyByValue"); + } + } + return null; + } + + /** + * 通过 value 获取 key 集合 ( 返回等于 value 的 key 集合 ) + * @param map {@link Map} + * @param value Value + * @param key + * @param value + * @return 等于 value 的 key 集合 + */ + public static List getKeysByValue(final Map map, final V value) { + if (map != null) { + try { + List lists = new ArrayList<>(); + // 进行遍历判断 + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + V v = entry.getValue(); + if (equals(v, value)) { + // 保存 key + lists.add(entry.getKey()); + } + } + // 返回 key 集合 + return lists; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getKeysByValue"); + } + } + return null; + } + + // = + + /** + * 通过 Map 获取 key 集合 + * @param map {@link Map} + * @param key + * @param value + * @return 全部存储 key 集合 + */ + public static List getKeys(final Map map) { + if (map != null) { + try { + return new ArrayList<>(map.keySet()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getKeys"); + } + } + return null; + } + + /** + * 通过 Map 获取 key 数组 + * @param map {@link Map} + * @param key + * @param value + * @return 全部存储 key 数组 + */ + public static K[] getKeysToArrays(final Map map) { + if (map != null) { + try { + return (K[]) getKeys(map).toArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getKeysToArrays"); + } + } + return null; + } + + /** + * 通过 Map 获取 value 集合 + * @param map {@link Map} + * @param key + * @param value + * @return 全部存储 value 数组 + */ + public static List getValues(final Map map) { + if (map != null) { + try { + return new ArrayList<>(map.values()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getValues"); + } + } + return null; + } + + /** + * 通过 Map 获取 value 数组 + * @param map {@link Map} + * @param key + * @param value + * @return 全部存储 value 数组 + */ + public static V[] getValuesToArrays(final Map map) { + if (map != null) { + try { + return (V[]) getValues(map).toArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getValuesToArrays"); + } + } + return null; + } + + // = + + /** + * 获取第一条数据 + * @param map {@link Map} + * @param key + * @param value + * @return 第一条数据 {@link Map.Entry} + */ + public static Map.Entry getFirst(final LinkedHashMap map) { + if (map != null) { + try { + return map.entrySet().iterator().next(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFirst"); + } + } + return null; + } + + /** + * 获取最后一条数据 + * @param map {@link Map} + * @param key + * @param value + * @return 最后一条数据 {@link Map.Entry} + */ + public static Map.Entry getLast(final LinkedHashMap map) { + return getLast(map, true); + } + + /** + * 获取最后一条数据 + * @param map {@link Map} + * @param isReflection 是否通过反射获取 + * @param key + * @param value + * @return 最后一条数据 {@link Map.Entry} + */ + public static Map.Entry getLast(final LinkedHashMap map, final boolean isReflection) { + if (map != null) { + if (isReflection) { + try { + // 反射方式 + Field tail = map.getClass().getDeclaredField("tail"); + tail.setAccessible(true); + return (Map.Entry) tail.get(map); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getLast"); + } + } else { + try { + // 遍历方式 + Iterator> iterator = map.entrySet().iterator(); + Map.Entry tail = null; + while (iterator.hasNext()) { + tail = iterator.next(); + } + return tail; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getLast"); + } + } + } + return null; + } + + // = + + /** + * 根据指定 key 获取 key 所在位置的下一条数据 + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return 指定 key 下一条数据 {@link Map.Entry} + */ + public static Map.Entry getNext(final LinkedHashMap map, final K key) { + if (map != null) { + try { + // 进行遍历判断 + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + K k = entry.getKey(); + // 判断 key 是否相同 + if (equals(k, key)) { + return entry; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getNext"); + } + } + return null; + } + + /** + * 根据指定 key 获取 key 所在位置的上一条数据 + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return 指定 key 上一条数据 {@link Map.Entry} + */ + public static Map.Entry getPrevious(final LinkedHashMap map, final K key) { + if (map != null) { + try { + // 进行遍历判断 + Iterator> iterator = map.entrySet().iterator(); + // 临时保存处理 + Map.Entry temp = null; + // 循环处理 + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + K k = entry.getKey(); + // 判断 key 是否相同 + if (equals(k, key)) { + return temp; + } + // 赋值上一条数据 + temp = entry; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPrevious"); + } + } + return null; + } + + // ============ + // = 添加数据 = + // ============ + + /** + * 添加一条数据 + * @param map {@link Map} + * @param key key + * @param value value + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean put(final Map map, final K key, final V value) { + return put(map, key, value, false); + } + + /** + * 添加一条数据 + * @param map {@link Map} + * @param key key + * @param value value + * @param notNull 是否不允许 key 为 null + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean put(final Map map, final K key, final V value, final boolean notNull) { + if (map != null) { + if (notNull && key == null) { + return false; + } + try { + map.put(key, value); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "put"); + } + } + return false; + } + + /** + * 添加一条数据, 不允许 key 为 null + * @param map {@link Map} + * @param key key + * @param value value + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putNotNull(final Map map, final K key, final V value) { + return put(map, key, value, true); + } + + // = + + /** + * 添加一条数据 + * @param map {@link Map} + * @param entry entry + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean put(final Map map, final Map.Entry entry) { + return put(map, entry, false); + } + + /** + * 添加一条数据 + * @param map {@link Map} + * @param entry entry + * @param notNull 是否不允许 key 为 null + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean put(final Map map, final Map.Entry entry, final boolean notNull) { + if (map != null && entry != null) { + if (notNull && entry.getKey() == null) { + return false; + } + try { + map.put(entry.getKey(), entry.getValue()); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "put"); + } + } + return false; + } + + /** + * 添加一条数据, 不允许 key 为 null + * @param map {@link Map} + * @param entry entry + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putNotNull(final Map map, final Map.Entry entry) { + return put(map, entry, true); + } + + // = + + /** + * 添加多条数据 + * @param map {@link Map} + * @param listKeys keys + * @param listValues values + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final List listKeys, final List listValues) { + return putAll(map, listKeys, listValues, false); + } + + /** + * 添加多条数据 + * @param map {@link Map} + * @param listKeys keys + * @param listValues values + * @param notNull 是否不允许 key 为 null + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final List listKeys, final List listValues, final boolean notNull) { + if (map != null && listKeys != null && listValues != null && listKeys.size() == listValues.size()) { + try { + // 循环保存 + for (int i = 0, len = listKeys.size(); i < len; i++) { + K key = listKeys.get(i); + if (notNull && key == null) { + continue; // 忽略进行下一个 + } + // 添加数据 + map.put(key, listValues.get(i)); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putAll"); + } + } + return false; + } + + /** + * 添加多条数据, 不允许 key 为 null + * @param map {@link Map} + * @param listKeys keys + * @param listValues values + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAllNotNull(final Map map, final List listKeys, final List listValues) { + return putAll(map, listKeys, listValues, true); + } + + // = + + /** + * 添加多条数据 + * @param map {@link Map} + * @param keys keys + * @param values values + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final K[] keys, final V[] values) { + return putAll(map, keys, values, false); + } + + /** + * 添加多条数据 + * @param map {@link Map} + * @param keys keys + * @param values values + * @param notNull 是否不允许 key 为 null + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final K[] keys, final V[] values, final boolean notNull) { + if (map != null && keys != null && values != null && keys.length == values.length) { + try { + // 循环保存 + for (int i = 0, len = keys.length; i < len; i++) { + K key = keys[i]; + if (notNull && key == null) { + continue; // 忽略进行下一个 + } + // 添加数据 + map.put(key, values[i]); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putAll"); + } + } + return false; + } + + /** + * 添加多条数据, 不允许 key 为 null + * @param map {@link Map} + * @param keys keys + * @param values values + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAllNotNull(final Map map, final K[] keys, final V[] values) { + return putAll(map, keys, values, true); + } + + // = + + /** + * 添加多条数据 + * @param map {@link Map} + * @param mapData map 数据 + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final Map mapData) { + return putAll(map, mapData, false); + } + + /** + * 添加多条数据 + * @param map {@link Map} + * @param mapData map 数据 + * @param notNull 是否不允许 key 为 null + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAll(final Map map, final Map mapData, final boolean notNull) { + if (map != null && mapData != null) { + if (notNull) { + try { + // 进行遍历判断 + Iterator> iterator = mapData.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + K key = entry.getKey(); + if (key != null) { + // 保存数据 + map.put(key, entry.getValue()); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putAll"); + } + } else { + try { + map.putAll(mapData); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putAll"); + } + } + } + return false; + } + + /** + * 添加多条数据, 不允许 key 为 null + * @param map {@link Map} + * @param mapData map 数据 + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean putAllNotNull(final Map map, final Map mapData) { + return putAll(map, mapData, true); + } + + // ============ + // = 删除数据 = + // ============ + + /** + * 移除一条数据 + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean remove(final Map map, final K key) { + if (map != null) { + try { + map.remove(key); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remove"); + } + } + return false; + } + + /** + * 移除一条数据 + * @param map {@link Map} + * @param key key + * @param value value + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean remove(final Map map, final K key, final V value) { + if (map != null) { + try { + // 判断值是否一样, 一样则移除 key + if (equals(value, map.get(key))) { + map.remove(key); + } + // return map.remove(key, value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "remove"); + } + } + return false; + } + + /** + * 移除多条数据 + * @param map {@link Map} + * @param keys keys + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToKeys(final Map map, final Collection keys) { + if (map != null && keys != null) { + try { + for (K key : keys) { + map.remove(key); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToKeys"); + } + } + return false; + } + + /** + * 移除等于 value 的所有数据 + * @param map {@link Map} + * @param value value + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToValue(final Map map, final V value) { + if (map != null) { + try { + // 进行遍历判断 + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + V v = entry.getValue(); + if (equals(v, value)) { + iterator.remove(); + } + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToValue"); + } + } + return false; + } + + /** + * 移除等于 value 的所有数据 (Collection) + * @param map {@link Map} + * @param values values + * @param key + * @param value + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToValues(final Map map, final Collection values) { + if (map != null && values != null) { + try { + for (V value : values) { + removeToValue(map, value); + } + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToValues"); + } + } + return false; + } + + // ============ + // = 快捷判断 = + // ============ + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + // = + + /** + * 切换保存状态 + *
+     *     1. 如果存在, 则删除
+     *     2. 如果不存在, 则保存
+     * 
+ * @param map {@link Map} + * @param key key + * @param value value + * @param key + * @param value + */ + public static void toggle(final Map map, final K key, final V value) { + if (map != null) { + // 判断是否存在 key + boolean existKey = map.containsKey(key); + try { + if (existKey) { // 存在则删除 + map.remove(key); + } else { // 不存在则保存 + map.put(key, value); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toggle"); + } + } + } + + /** + * 判断指定 key 的 value 是否为 null + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return {@code true} yes, {@code false} no + */ + public static boolean isNullToValue(final Map map, final K key) { + if (map != null) { + return map.get(key) == null; + } + return true; + } + + /** + * 判断 Map 是否存储了 key + * @param map {@link Map} + * @param key key + * @param key + * @param value + * @return {@code true} yes, {@code false} no + */ + public static boolean containsKey(final Map map, final K key) { + if (map != null) { + try { + return map.containsKey(key); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "containsKey"); + } + } + return false; + } + + /** + * 判断 Map 是否存储了 value + * @param map {@link Map} + * @param value value + * @param key + * @param value + * @return {@code true} yes, {@code false} no + */ + public static boolean containsValue(final Map map, final V value) { + if (map != null) { + try { + return map.containsValue(value); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "containsValue"); + } + } + return false; + } + + // ================= + // = 特殊 Map 操作 = + // ================= + + /** + * 添加一条数据 - (Value) List + * @param map 待添加 {@link Map} + * @param key key + * @param value value, add to list + * @param key + * @param value type + * @return {@code true} success, {@code false} fail + */ + public static boolean putToList(final Map> map, final K key, final T value) { + return putToList(map, key, value, true); + } + + /** + * 添加一条数据 - (Value) List + * @param map {@link Map} + * @param key key + * @param value value, add to list + * @param isNew 当指定 (key) 的 value 为 null, 是否创建 + * @param key + * @param value type + * @return {@code true} success, {@code false} fail + */ + public static boolean putToList(final Map> map, final K key, final T value, final boolean isNew) { + if (map != null) { + if (map.containsKey(key)) { + ArrayList lists = map.get(key); + if (lists != null) { + try { + lists.add(value); + map.put(key, lists); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putToList"); + } + } + } else { + // 判断是否创建 + if (isNew) { + try { + ArrayList lists = new ArrayList<>(); + lists.add(value); + map.put(key, lists); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "putToList"); + } + } + } + } + return false; + } + + // = + + /** + * 移除一条数据 - (Value) List + * @param map {@link Map} + * @param key key + * @param key + * @param value type + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToList(final Map> map, final K key) { + if (map != null) { + try { + map.remove(key); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToList"); + } + } + return false; + } + + /** + * 移除一条数据 - (Value) List + * @param map {@link Map} + * @param key key + * @param value value, remove to list + * @param key + * @param value type + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToList(final Map> map, final K key, final T value) { + if (map != null) { + if (map.containsKey(key)) { + ArrayList lists = map.get(key); + if (lists != null) { + try { + lists.remove(value); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToList"); + } + } + } + } + return false; + } + + /** + * 移除多条数据 - (Value) List + * @param map {@link Map} + * @param key key + * @param lists 删除的 list 数据源 + * @param key + * @param value type + * @return {@code true} success, {@code false} fail + */ + public static boolean removeToLists(final Map> map, final K key, final ArrayList lists) { + if (map != null && lists != null) { + if (map.containsKey(key)) { + ArrayList list = map.get(key); + if (list != null) { + try { + list.removeAll(lists); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToLists"); + } + } + } + } + return false; + } + + // = + + /** + * 移除多条数据 - 通过 Map 进行移除 + * @param map {@link Map} + * @param removeMap {@link Map} 移除对比数据源 + * @param key + * @param value type + */ + public static void removeToMap(final Map> map, final Map> removeMap) { + removeToMap(map, removeMap, true, false); + } + + /** + * 移除多条数据 - 通过 Map 进行移除 + * @param map {@link Map} + * @param removeMap {@link Map} 移除对比数据源 + * @param removeEmpty 是否移除 null、长度为 0 的数据 + * @param isNullRemoveAll 如果待移除的 ArrayList 是 null, 是否移除全部 + * @param key + * @param value type + */ + public static void removeToMap(final Map> map, final Map> removeMap, + final boolean removeEmpty, final boolean isNullRemoveAll) { + if (map != null && removeMap != null) { + Iterator>> iterator = removeMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + // 获取 key + K key = entry.getKey(); + // 进行移除处理 + if (map.containsKey(key)) { + ArrayList value = entry.getValue(); + try { + if (value != null) { + map.get(key).removeAll(value); + } else { + if (isNullRemoveAll) { + map.remove(key); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToMap - removeAll"); + } + // 判断是否移除 null、长度为 0 的数据 + if (removeEmpty) { + ArrayList lists = map.get(key); + try { + // 不存在数据了, 则移除 + if (lists == null || lists.size() == 0) { + map.remove(key); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "removeToMap"); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/NumberUtils.java b/lib/DevJava/src/main/java/dev/utils/common/NumberUtils.java new file mode 100644 index 0000000000..1f06ef40aa --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/NumberUtils.java @@ -0,0 +1,1008 @@ +package dev.utils.common; + +import java.math.BigDecimal; +import java.util.regex.Pattern; + +import dev.utils.JCLogUtils; + +/** + * detail: 数字 ( 计算 ) 工具类 + * @author Ttt + */ +public final class NumberUtils { + + private NumberUtils() { + } + + // 日志 TAG + private static final String TAG = NumberUtils.class.getSimpleName(); + + // ======= + // = int = + // ======= + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI(final int value, final int max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + if (value >= max) return 1; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI(final float value, final float max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + if (value >= max) return 1; + return (int) (value / max); + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI(final long value, final long max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + if (value >= max) return 1; + return (int) (value / max); + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI(final double value, final double max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + if (value >= max) return 1; + return (int) (value / max); + } + + // ========== + // = double = + // ========== + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD(final int value, final int max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + if (value >= max) return 1.0d; + return (double) value / (double) max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD(final float value, final float max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + if (value >= max) return 1.0d; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD(final long value, final long max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + if (value >= max) return 1.0d; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD(final double value, final double max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + if (value >= max) return 1.0d; + return value / max; + } + + // ======== + // = long = + // ======== + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL(final int value, final int max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + if (value >= max) return 1L; + return (long) value / (long) max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL(final float value, final float max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + if (value >= max) return 1L; + return (long) value / (long) max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL(final long value, final long max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + if (value >= max) return 1L; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL(final double value, final double max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + if (value >= max) return 1L; + return (long) (value / max); + } + + // ========= + // = float = + // ========= + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF(final int value, final int max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return (float) value / (float) max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF(final float value, final float max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF(final long value, final long max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return value / max; + } + + /** + * 计算百分比值 ( 最大 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF(final double value, final double max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return (float) (value / max); + } + + // =============================== + // = 计算百分比值 ( 可超出 100%) = + // =============================== + + // ======= + // = int = + // ======= + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI2(final int value, final int max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI2(final float value, final float max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + return (int) (value / max); + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI2(final long value, final long max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + return (int) (value / max); + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static int percentI2(final double value, final double max) { + if (max <= 0) return 0; + if (value <= 0) return 0; + return (int) (value / max); + } + + // ========== + // = double = + // ========== + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD2(final int value, final int max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + return (double) value / (double) max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD2(final float value, final float max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD2(final long value, final long max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static double percentD2(final double value, final double max) { + if (max <= 0) return 0.0d; + if (value <= 0) return 0.0d; + return value / max; + } + + // ======== + // = long = + // ======== + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL2(final int value, final int max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + return (long) value / (long) max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL2(final float value, final float max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + return (long) value / (long) max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL2(final long value, final long max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static long percentL2(final double value, final double max) { + if (max <= 0) return 0L; + if (value <= 0) return 0L; + return (long) (value / max); + } + + // ========= + // = float = + // ========= + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF2(final int value, final int max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + return (float) value / (float) max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF2(final float value, final float max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF2(final long value, final long max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + return value / max; + } + + /** + * 计算百分比值 ( 可超出 100%) + * @param value 指定值 + * @param max 最大值 + * @return 百分比值 + */ + public static float percentF2(final double value, final double max) { + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + return (float) (value / max); + } + + // = + + /** + * 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max + * @param value 指定值 + * @param max 最大值 + * @param min 最小值 + * @return 介于 max、min 之间的 value + */ + public static int clamp(final int value, final int max, final int min) { + return value > max ? max : value < min ? min : value; + } + + /** + * 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max + * @param value 指定值 + * @param max 最大值 + * @param min 最小值 + * @return 介于 max、min 之间的 value + */ + public static double clamp(final double value, final double max, final double min) { + return value > max ? max : value < min ? min : value; + } + + /** + * 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max + * @param value 指定值 + * @param max 最大值 + * @param min 最小值 + * @return 介于 max、min 之间的 value + */ + public static long clamp(final long value, final long max, final long min) { + return value > max ? max : value < min ? min : value; + } + + /** + * 返回的 value 介于 max、min 之间, 若 value 小于 min, 返回 min, 若大于 max, 返回 max + * @param value 指定值 + * @param max 最大值 + * @param min 最小值 + * @return 介于 max、min 之间的 value + */ + public static float clamp(final float value, final float max, final float min) { + return value > max ? max : value < min ? min : value; + } + + // ======== + // = calc = + // ======== + + /** + * 获取倍数 ( 自动补 1) + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultiple(final int value, final int divisor) { + if (value <= 0 || divisor <= 0) return 0; + if (value <= divisor) return 1; + return (value % divisor == 0) ? (value / divisor) : (value / divisor) + 1; + } + + /** + * 获取倍数 ( 自动补 1) + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultiple(final double value, final double divisor) { + if (value <= 0 || divisor <= 0) return 0; + if (value <= divisor) return 1; + int result = (int) (value / divisor); + return ((value - divisor * result == 0d) ? result : result + 1); + } + + /** + * 获取倍数 ( 自动补 1) + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultiple(final long value, final long divisor) { + if (value <= 0 || divisor <= 0) return 0; + if (value <= divisor) return 1; + return (int) ((value % divisor == 0) ? (value / divisor) : (value / divisor) + 1); + } + + /** + * 获取倍数 ( 自动补 1) + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultiple(final float value, final float divisor) { + if (value <= 0 || divisor <= 0) return 0; + if (value <= divisor) return 1; + int result = (int) (value / divisor); + return ((value - divisor * result == 0f) ? result : result + 1); + } + + // ======= + // = int = + // ======= + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultipleI(final int value, final int divisor) { + if (value <= 0 || divisor <= 0) return 0; + return (int) (value / divisor); + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultipleI(final double value, final double divisor) { + if (value <= 0 || divisor <= 0) return 0; + return (int) (value / divisor); + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultipleI(final long value, final long divisor) { + if (value <= 0 || divisor <= 0) return 0; + return (int) (value / divisor); + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static int getMultipleI(final float value, final float divisor) { + if (value <= 0 || divisor <= 0) return 0; + return (int) (value / divisor); + } + + // ========== + // = double = + // ========== + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static double getMultipleD(final int value, final int divisor) { + if (value <= 0d || divisor <= 0d) return 0d; + return (double) value / (double) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static double getMultipleD(final double value, final double divisor) { + if (value <= 0d || divisor <= 0d) return 0d; + return (double) value / (double) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static double getMultipleD(final long value, final long divisor) { + if (value <= 0d || divisor <= 0d) return 0d; + return (double) value / (double) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static double getMultipleD(final float value, final float divisor) { + if (value <= 0d || divisor <= 0d) return 0d; + return (double) value / (double) divisor; + } + + // ======== + // = long = + // ======== + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static long getMultipleL(final int value, final int divisor) { + if (value <= 0L || divisor <= 0L) return 0L; + return (long) value / (long) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static long getMultipleL(final double value, final double divisor) { + if (value <= 0L || divisor <= 0L) return 0L; + return (long) value / (long) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static long getMultipleL(final long value, final long divisor) { + if (value <= 0L || divisor <= 0L) return 0L; + return (long) value / (long) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static long getMultipleL(final float value, final float divisor) { + if (value <= 0L || divisor <= 0L) return 0L; + return (long) value / (long) divisor; + } + + // ========= + // = float = + // ========= + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static float getMultipleF(final int value, final int divisor) { + if (value <= 0f || divisor <= 0f) return 0f; + return (float) value / (float) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static float getMultipleF(final double value, final double divisor) { + if (value <= 0f || divisor <= 0f) return 0f; + return (float) value / (float) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static float getMultipleF(final long value, final long divisor) { + if (value <= 0f || divisor <= 0f) return 0f; + return (float) value / (float) divisor; + } + + /** + * 获取倍数 + * @param value 被除数 + * @param divisor 除数 + * @return 倍数 + */ + public static float getMultipleF(final float value, final float divisor) { + if (value <= 0f || divisor <= 0f) return 0f; + return (float) value / (float) divisor; + } + + // ================== + // = 数字、中文互转 = + // ================== + + // 零索引 + private static final int ZERO = 0; + // 十位数索引 + private static final int TEN_POS = 10; + // 中文 ( 数字单位 ) + private static final String[] CHN_NUMBER_UNITS = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "百", "千", "万", "亿", "兆"}; + // 中文大写 ( 数字单位 ) + private static final String[] CHN_NUMBER_UPPER_UNITS = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖", "拾", "佰", "仟", "万", "亿", "兆"}; + // 数字单位对应数值 + private static final double[] NUMBER_UNITS = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1000, 10000, 100000000, 1000000000000d}; + + // ============ + // = 对外公开 = + // ============ + + // ============== + // = 数字转中文 = + // ============== + + /** + * 数字转中文数值 + * @param number 数值 + * @param isUpper 是否大写金额 + * @return 数字中文化字符串 + */ + public static String numberToCHN(final double number, final boolean isUpper) { + try { + return numberToCHNNumber(BigDecimal.valueOf(number), isUpper ? CHN_NUMBER_UPPER_UNITS : CHN_NUMBER_UNITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "numberToCHN"); + } + return null; + } + + /** + * 数字转中文数值 + * @param number 数值 + * @param isUpper 是否大写金额 + * @return 数字中文化字符串 + */ + public static String numberToCHN(final String number, final boolean isUpper) { + if (number != null) { + try { + return numberToCHNNumber(new BigDecimal(number), isUpper ? CHN_NUMBER_UPPER_UNITS : CHN_NUMBER_UNITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "numberToCHN"); + } + } + return null; + } + + /** + * 数字转中文数值 + * @param number 数值 + * @param isUpper 是否大写金额 + * @return 数字中文化字符串 + */ + public static String numberToCHN(final BigDecimal number, final boolean isUpper) { + return numberToCHNNumber(number, isUpper ? CHN_NUMBER_UPPER_UNITS : CHN_NUMBER_UNITS); + } + + // ============ + // = 内部方法 = + // ============ + + // ============== + // = 数字转中文 = + // ============== + + /** + * 数字转中文数值 + * @param bigDecimal 数值 + * @param chnUnits 中文数字单位数组 + * @return 数字中文化字符串 + */ + private static String numberToCHNNumber(final BigDecimal bigDecimal, final String[] chnUnits) { + // 防止为 null + if (bigDecimal == null) return null; + // 去除小数点 + BigDecimal number = bigDecimal.setScale(0, BigDecimal.ROUND_DOWN); + // 防止小于 1, 直接返回 0 + if (number.doubleValue() < 1) return chnUnits[ZERO]; + + StringBuilder builder = new StringBuilder(); + // 索引记录 + int unitIndex = 0; // 当前数字单位 + int beforeUnitIndex = 0; // 之前的数字单位 + // 循环处理 + for (int i = NUMBER_UNITS.length - 1; i > 0; i--) { + if (number.doubleValue() >= NUMBER_UNITS[i]) { + final int multiple = (int) (number.doubleValue() / NUMBER_UNITS[i]); // 倍数 + number = number.subtract(BigDecimal.valueOf(multiple * NUMBER_UNITS[i])); // 递减 + // 如果倍数大于万倍, 则直接递归 + if (multiple >= 10000) { + // 拼接数值 + builder.append(numberToCHNNumber(new BigDecimal(multiple), chnUnits)); + builder.append(chnUnits[i]); // 数字单位 + // 判断是否需要补零 + if (unitIndex > i && unitIndex != 0) { + builder.append(chnUnits[ZERO]); // 补零 + } + } else { + // 判断 兆级与亿级、亿级与万级 补零操作 + if ((i == 14 && unitIndex == 15) || (i == 13 && unitIndex == 14)) { + if (multiple < 1000) { + builder.append(chnUnits[ZERO]); // 补零 + } + } else if (unitIndex > i && unitIndex != 0) { // 跨数字单位处理 + builder.append(chnUnits[ZERO]); // 补零 + } + // 拼接数值 + builder.append(thousandConvertCHN(multiple, chnUnits)); + builder.append(chnUnits[i]); // 数字单位 + } + // 保存旧的数字单位索引 + beforeUnitIndex = unitIndex; + // 保存新的数字单位索引 + unitIndex = i; + } + + double numberValue = number.doubleValue(); + // 如果位数小于万位 ( 属于千位 ), 则进行处理 + if (numberValue < 10000) { + // 判断是否需要补零 + if (unitIndex >= (TEN_POS + 3)) { + // 是否大于 1 ( 结尾零, 则不补充数字单位 ) + if (numberValue >= 1) { + if (beforeUnitIndex == 0) { + beforeUnitIndex = unitIndex; + } + // 如果旧的索引, 大于当前索引, 则补零 + if (unitIndex != 13 && (beforeUnitIndex == 14 || beforeUnitIndex == 15) + && beforeUnitIndex >= unitIndex) { // 属于亿、兆级别, 都需要补零 + builder.append(chnUnits[ZERO]); // 补零 + } else { // 当前数字单位属于万级 + if (numberValue < 1000) { + builder.append(chnUnits[ZERO]); // 补零 + } + } + } + } + // 拼接数值 + builder.append(thousandConvertCHN((int) numberValue, chnUnits)); + return builder.toString(); + } + } + return builder.toString(); + } + + /** + * 万位以下 ( 千位 ) 级别转换 + * @param value 数值 + * @param chnUnits 中文数字单位数组 + * @return 数字中文化字符串 + */ + private static String thousandConvertCHN(final int value, final String[] chnUnits) { + StringBuilder builder = new StringBuilder(); + // 转换数字 + int number = value; + // 对应数值 + int[] units = {10, 100, 1000}; + // 判断是否需要补零 + boolean[] zeros = new boolean[3]; + zeros[0] = number >= 10; // 大于等于十位, 才需要补零 + zeros[1] = number >= 100; // 大于等于百位, 才需要补零 + zeros[2] = false; // 千位数, 不需要补零 + // 循环处理 + for (int i = 2; i >= 0; i--) { + if (number >= units[i]) { + int multiple = (number / units[i]); + number -= multiple * units[i]; + // 拼接数值 + builder.append(chnUnits[multiple]); // 个位数 + builder.append(chnUnits[TEN_POS + i]); // 数字单位 + // 进行改变处理 + zeros[i] = false; + } + // 补零判断处理 + if (number >= 1 && zeros[i]) { + if (i == 1) { // 属于百位 + builder.append(chnUnits[ZERO]); // 补零 + } else { // 属于十位 + // 如果百位, 补零了, 个位数则不用补零 + if (!zeros[i + 1]) { + builder.append(chnUnits[ZERO]); // 补零 + } + } + } + } + // 判断最后值, 是否大于 1 ( 结尾零, 则不补充数字单位 ) + if (number >= 1) { + builder.append(chnUnits[number]); + } + return builder.toString(); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + // ================== + // = ValidatorUtils = + // ================== + + // 正则表达式: 验证数字 + private static final String REGEX_NUMBER = "^[0-9]*$"; + // 正则表达式: 验证数字或包含小数点 + private static final String REGEX_NUMBER_OR_DECIMAL = "^[0-9]*[.]?[0-9]*$"; + + /** + * 检验数字 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNumber(final String str) { + return match(REGEX_NUMBER, str); + } + + /** + * 检验数字或包含小数点 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNumberDecimal(final String str) { + return match(REGEX_NUMBER_OR_DECIMAL, str); + } + + /** + * 通用匹配函数 + * @param regex 正则表达式 + * @param input 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean match(final String regex, final String input) { + if (!isEmpty(input)) { + try { + return Pattern.matches(regex, input); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "match"); + } + } + return false; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/ObjectUtils.java b/lib/DevJava/src/main/java/dev/utils/common/ObjectUtils.java new file mode 100644 index 0000000000..05150e8166 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/ObjectUtils.java @@ -0,0 +1,161 @@ +package dev.utils.common; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: 对象相关工具类 + * @author Ttt + */ +public final class ObjectUtils { + + private ObjectUtils() { + } + + // 日志 TAG + private static final String TAG = ObjectUtils.class.getSimpleName(); + + // ========== + // = Object = + // ========== + + /** + * 判断对象是否为空 + * @param object 对象 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final Object object) { + if (object == null) { + return true; + } + try { + if (object.getClass().isArray() && Array.getLength(object) == 0) { + return true; + } + if (object instanceof CharSequence && object.toString().length() == 0) { + return true; + } + if (object instanceof Collection && ((Collection) object).isEmpty()) { + return true; + } + if (object instanceof Map && ((Map) object).isEmpty()) { + return true; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isEmpty"); + } + return false; + } + + /** + * 判断对象是否非空 + * @param object 对象 + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final Object object) { + return !isEmpty(object); + } + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + /** + * 检查对象是否为 null, 为 null 则抛出异常, 不为 null 则返回该对象 + * @param object 对象 + * @param message 报错信息 + * @param 泛型 + * @return 非空对象 + * @throws NullPointerException null 异常 + */ + public static T requireNonNull(final T object, final String message) throws NullPointerException { + if (object == null) throw new NullPointerException(message); + return object; + } + + /** + * 获取非空或默认对象 + * @param object 对象 + * @param defaultObject 默认值 + * @param 泛型 + * @return 非空或默认对象 + */ + public static T getOrDefault(final T object, final T defaultObject) { + return (object != null) ? object : defaultObject; + } + + /** + * 获取对象哈希值 + * @param object 对象 + * @return 哈希值 + */ + public static int hashCode(final Object object) { + return object != null ? object.hashCode() : 0; + } + + /** + * 获取一个对象的独一无二的标记 + * @param object 对象 + * @return 对象唯一标记 + */ + public static String getObjectTag(final Object object) { + if (object == null) return null; + // 对象所在的包名 + 对象的内存地址 + return object.getClass().getName() + Integer.toHexString(object.hashCode()); + } + + /** + * 获取转换对象 + * @param object 对象 + * @param 泛型 + * @return 非空或默认对象 + */ + public static T converObj(final Object object) { + if (object == null) return null; + try { + return (T) object; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "converObj"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/RandomUtils.java b/lib/DevJava/src/main/java/dev/utils/common/RandomUtils.java new file mode 100644 index 0000000000..c409a5bcd1 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/RandomUtils.java @@ -0,0 +1,557 @@ +package dev.utils.common; + +import java.util.Random; + +import dev.utils.JCLogUtils; + +/** + * detail: 随机工具类 + * @author Ttt + */ +public final class RandomUtils { + + private RandomUtils() { + } + + // 日志 TAG + private static final String TAG = RandomUtils.class.getSimpleName(); + + // 0123456789 + public static final char[] NUMBERS = new char[]{48, 49, 50, 51, 52, 53, 54, 55, 56, 57}; + + // abcdefghijklmnopqrstuvwxyz + public static final char[] LOWER_CASE_LETTERS = new char[]{97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122}; + + // ABCDEFGHIJKLMNOPQRSTUVWXYZ + public static final char[] CAPITAL_LETTERS = new char[]{65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90}; + + // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + public static final char[] LETTERS = new char[]{97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90}; + + // 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + public static final char[] NUMBERS_AND_LETTERS = new char[]{48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90}; + + /** + * 获取伪随机 boolean 值 + * @param random Random + * @return 随机 boolean 值 + */ + public static boolean nextBoolean(final Random random) { + return random != null ? random.nextBoolean() : new Random().nextBoolean(); + } + + /** + * 获取伪随机 byte[] + * @param random Random + * @param data 随机数据源 + * @return 随机 byte[] + */ + public static byte[] nextBytes(final Random random, final byte[] data) { + if (random == null || data == null) return data; + try { + random.nextBytes(data); + } catch (Exception e) { + } + return data; + } + + /** + * 获取伪随机 double 值 + * @param random Random + * @return 随机 double 值 + */ + public static double nextDouble(final Random random) { + return random != null ? random.nextDouble() : new Random().nextDouble(); + } + + /** + * 获取伪随机高斯分布值 + * @param random Random + * @return 伪随机高斯分布值 + */ + public static double nextGaussian(final Random random) { + return random != null ? random.nextGaussian() : new Random().nextGaussian(); + } + + /** + * 获取伪随机 float 值 + * @param random Random + * @return 随机 float 值 + */ + public static float nextFloat(final Random random) { + return random != null ? random.nextFloat() : new Random().nextFloat(); + } + + /** + * 获取伪随机 int 值 + * @param random Random + * @return 随机 int 值 + */ + public static int nextInt(final Random random) { + return random != null ? random.nextInt() : new Random().nextInt(); + } + + /** + * 获取伪随机 int 值 - 该值介于 [0, n) 的区间 + * @param random Random + * @param number 最大随机值 + * @return 随机介于 [0, n) 的区间值 + */ + public static int nextInt(final Random random, final int number) { + if (number <= 0) return 0; + return random != null ? random.nextInt(number) : new Random().nextInt(number); + } + + /** + * 获取伪随机 long 值 + * @param random Random + * @return 随机 long 值 + */ + public static long nextLong(final Random random) { + return random != null ? random.nextLong() : new Random().nextLong(); + } + + // = + + /** + * 获取伪随机 boolean 值 + * @return 随机 boolean 值 + */ + public static boolean nextBoolean() { + return new Random().nextBoolean(); + } + + /** + * 获取伪随机 byte[] + * @param data 随机数据源 + * @return 随机 byte[] + */ + public static byte[] nextBytes(final byte[] data) { + if (data == null) return null; + try { + new Random().nextBytes(data); + } catch (Exception e) { + } + return data; + } + + /** + * 获取伪随机 double 值 + * @return 随机 double 值 + */ + public static double nextDouble() { + return new Random().nextDouble(); + } + + /** + * 获取伪随机高斯分布值 + * @return 伪随机高斯分布值 + */ + public static double nextGaussian() { + return new Random().nextGaussian(); + } + + /** + * 获取伪随机 float 值 + * @return 随机 float 值 + */ + public static float nextFloat() { + return new Random().nextFloat(); + } + + /** + * 获取伪随机 int 值 + * @return 随机 int 值 + */ + public static int nextInt() { + return new Random().nextInt(); + } + + /** + * 获取伪随机 int 值 - 该值介于 [0, n) 的区间 + * @param number 最大随机值 + * @return 随机介于 [0, n) 的区间值 + */ + public static int nextInt(final int number) { + if (number <= 0) return 0; + return new Random().nextInt(number); + } + + /** + * 获取伪随机 long 值 + * @return 随机 long 值 + */ + public static long nextLong() { + return new Random().nextLong(); + } + + // = + + /** + * 获取数字自定义长度的随机数 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandomNumbers(final int length) { + return getRandom(NUMBERS, length); + } + + /** + * 获取小写字母自定义长度的随机数 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandomLowerCaseLetters(final int length) { + return getRandom(LOWER_CASE_LETTERS, length); + } + + /** + * 获取大写字母自定义长度的随机数 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandomCapitalLetters(final int length) { + return getRandom(CAPITAL_LETTERS, length); + } + + /** + * 获取大小写字母自定义长度的随机数 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandomLetters(final int length) { + return getRandom(LETTERS, length); + } + + /** + * 获取数字、大小写字母自定义长度的随机数 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandomNumbersAndLetters(final int length) { + return getRandom(NUMBERS_AND_LETTERS, length); + } + + /** + * 获取自定义数据自定义长度的随机数 + * @param source 随机的数据源 + * @param length 长度 + * @return 随机字符串 + */ + public static String getRandom(final String source, final int length) { + if (source == null) return null; + return getRandom(source.toCharArray(), length); + } + + /** + * 获取 char[] 内的随机数 + * @param chars 随机的数据源 + * @param length 需要最终长度 + * @return 随机字符串 + */ + public static String getRandom(final char[] chars, final int length) { + if (length > 0 && chars != null && chars.length != 0) { + StringBuilder builder = new StringBuilder(length); + Random random = new Random(); + for (int i = 0; i < length; i++) { + builder.append(chars[random.nextInt(chars.length)]); + } + return builder.toString(); + } + return null; + } + + /** + * 获取 0 - 最大随机数之间的随机数 + * @param max 最大随机数 + * @return 随机介于 [0, max) 的区间值 + */ + public static int getRandom(final int max) { + return getRandom(0, max); + } + + /** + * 获取两个数之间的随机数 ( 不含最大随机数, 需要 + 1) + * @param min 最小随机数 + * @param max 最大随机数 + * @return 随机介于 [min, max) 的区间值 + */ + public static int getRandom(final int min, final int max) { + if (min > max) { + return 0; + } else if (min == max) { + return min; + } + return min + new Random().nextInt(max - min); + } + + // = + + // 内置洗牌算法 + // java.util.Collections.shuffle(List list); + + /** + * 洗牌算法 ( 第一种 ), 随机置换指定的数组使用的默认源的随机性 ( 随机数据源小于三个, 则无效 ) + * @param objects 随机数据源 + * @return {@code true} success, {@code false} fail + */ + public static boolean shuffle(final Object[] objects) { + if (objects == null) return false; + return shuffle(objects, getRandom(1, objects.length)); + } + + /** + * 洗牌算法 ( 第一种 ), 随机置换指定的数组使用的默认源的随机性 ( 随机数据源小于三个, 则无效 ) + * @param objects 随机数据源 + * @param shuffleCount 洗牌次数 + * @return {@code true} success, {@code false} fail + */ + public static boolean shuffle(final Object[] objects, final int shuffleCount) { + int length; + if (shuffleCount > 0 && objects != null && (length = objects.length) >= shuffleCount) { + for (int i = 1; i <= shuffleCount; i++) { + int random = getRandom(0, length - i); + Object temp = objects[length - i]; + objects[length - i] = objects[random]; + objects[random] = temp; + } + return true; + } + return false; + } + + /** + * 洗牌算法 ( 第一种 ), 随机置换指定的数组使用的默认源的随机性 ( 随机数据源小于三个, 则无效 ) + * @param ints 随机数据源 + * @return 随机 int[] + */ + public static int[] shuffle(final int[] ints) { + if (ints == null) return null; + return shuffle(ints, getRandom(1, ints.length)); + } + + /** + * 洗牌算法 ( 第一种 ), 随机置换指定的数组使用的默认源的随机性 ( 随机数据源小于三个, 则无效 ) + * @param ints 随机数据源 + * @param shuffleCount 洗牌次数 + * @return 随机 int[] + */ + public static int[] shuffle(final int[] ints, final int shuffleCount) { + int length; + if (shuffleCount > 0 && ints != null && (length = ints.length) >= shuffleCount) { + int[] out = new int[shuffleCount]; + for (int i = 1; i <= shuffleCount; i++) { + int random = getRandom(0, length - i); + out[i - 1] = ints[random]; + int temp = ints[length - i]; + ints[length - i] = ints[random]; + ints[random] = temp; + } + return out; + } + return null; + } + + // = + + /** + * 洗牌算法 ( 第二种 ), 随机置换指定的数组使用的默认源的随机性 + * @param objects 随机数据源 + * @return {@code true} success, {@code false} fail + */ + public static boolean shuffle2(final Object[] objects) { + if (objects == null) return false; + int len = objects.length; + if (len > 0) { + for (int i = 0; i < len - 1; i++) { + int idx = (int) (Math.random() * (len - i)); + Object temp = objects[idx]; + objects[idx] = objects[len - i - 1]; + objects[len - i - 1] = temp; + } + return true; + } + return false; + } + + // = + + /** + * 获取指定范围 int 值 + * @param origin 开始值 + * @param bound 范围值 + * @return 属于指定范围随机 int 值 + * @throws IllegalArgumentException 参数错误 + */ + public static int nextIntRange(final int origin, final int bound) throws IllegalArgumentException { + if (origin > bound) { + throw new IllegalArgumentException("bound must be greater than origin"); + } else if (origin == bound) { + return origin; + } + Random random = new Random(); + int n = bound - origin; + if (n > 0) { + return random.nextInt(n) + origin; + } else { + int r; + do { + r = random.nextInt(); + } while (r < origin || r >= bound); + return r; + } + } + + /** + * 获取指定范围 long 值 + * @param origin 开始值 + * @param bound 范围值 + * @return 属于指定范围随机 long 值 + * @throws IllegalArgumentException 参数错误 + */ + public static long nextLongRange(final long origin, final long bound) throws IllegalArgumentException { + if (origin > bound) { + throw new IllegalArgumentException("bound must be greater than origin"); + } else if (origin == bound) { + return origin; + } + Random random = new Random(); + long r = random.nextLong(); + long n = bound - origin, m = n - 1; + if ((n & m) == 0L) // power of two + r = (r & m) + origin; + else if (n > 0L) { // reject over-represented candidates + for (long u = r >>> 1; // ensure nonnegative + u + m - (r = u % n) < 0L; // rejection check + u = random.nextLong() >>> 1) // retry + ; + r += origin; + } else { // range not representable as long + while (r < origin || r >= bound) + r = random.nextLong(); + } + return r; + } + + /** + * 获取指定范围 double 值 + * @param origin 开始值 + * @param bound 范围值 + * @return 属于指定范围随机 double 值 + * @throws IllegalArgumentException 参数错误 + */ + public static double nextDoubleRange(final double origin, final double bound) throws IllegalArgumentException { + if (origin > bound) { + throw new IllegalArgumentException("bound must be greater than origin"); + } else if (origin == bound) { + return origin; + } + double r = new Random().nextDouble(); + r = r * (bound - origin) + origin; + if (r >= bound) // correct for rounding + r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); + return r; + } + + /** + * 获取随机 int[] + * @param streamSize 数组长度 + * @param randomNumberOrigin 开始值 + * @param randomNumberBound 结束值 ( 最大值范围 ) + * @return 指定范围随机 int[] + */ + public static int[] ints(final int streamSize, final int randomNumberOrigin, final int randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) { + return null; + } else if (streamSize < 0) { + return null; + } +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +// IntStream intStream = new Random().ints(streamSize, randomNumberOrigin, randomNumberBound); +// return intStream.toArray(); +// } else { + try { + int[] ints = new int[streamSize]; + for (int i = 0; i < streamSize; i++) { + ints[i] = nextIntRange(randomNumberOrigin, randomNumberBound); + } + return ints; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "ints"); + } + return null; +// } + } + + /** + * 获取随机 long[] + * @param streamSize 数组长度 + * @param randomNumberOrigin 开始值 + * @param randomNumberBound 结束值 ( 最大值范围 ) + * @return 指定范围随机 long[] + */ + public static long[] longs(final int streamSize, final long randomNumberOrigin, final long randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) { + return null; + } else if (streamSize < 0) { + return null; + } +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +// LongStream longStream = new Random().longs(streamSize, randomNumberOrigin, randomNumberBound); +// return longStream.toArray(); +// } else { + try { + long[] longs = new long[streamSize]; + for (int i = 0; i < streamSize; i++) { + longs[i] = nextLongRange(randomNumberOrigin, randomNumberBound); + } + return longs; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "longs"); + } + return null; +// } + } + + /** + * 获取随机 double[] + * @param streamSize 数组长度 + * @param randomNumberOrigin 开始值 + * @param randomNumberBound 结束值 ( 最大值范围 ) + * @return 指定范围随机 double[] + */ + public static double[] doubles(final int streamSize, final double randomNumberOrigin, final double randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) { + return null; + } else if (streamSize < 0) { + return null; + } +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +// DoubleStream doubleStream = new Random().doubles(streamSize, randomNumberOrigin, randomNumberBound); +// return doubleStream.toArray(); +// } else { + try { + double[] doubles = new double[streamSize]; + for (int i = 0; i < streamSize; i++) { + doubles[i] = nextDoubleRange(randomNumberOrigin, randomNumberBound); + } + return doubles; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "doubles"); + } + return null; +// } + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/Reflect2Utils.java b/lib/DevJava/src/main/java/dev/utils/common/Reflect2Utils.java new file mode 100644 index 0000000000..3ba1cbc3ec --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/Reflect2Utils.java @@ -0,0 +1,594 @@ +package dev.utils.common; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import dev.utils.JCLogUtils; + +/** + * detail: 反射相关工具类 + * @author Ttt + *
+ *     有两个方法: getMethod, getDeclaredMethod
+ *     

+ * getDeclaredMethod() 获取的是类自身声明的所有方法, 包含 public、protected 和 private 方法 + * getMethod() 获取的是类的所有共有方法, 这就包括自身的所有 public 方法, 和从基类继承的、从接口实现的所有 public 方法 + *

+ * getMethod 只能调用 public 声明的方法, 而 getDeclaredMethod 基本可以调用任何类型声明的方法 + * 反射多用 getDeclaredMethod, 尽量少用 getMethod + *
+ */ +public final class Reflect2Utils { + + private Reflect2Utils() { + } + + // 日志 TAG + private static final String TAG = Reflect2Utils.class.getSimpleName(); + + // ============ + // = 对象变量 = + // ============ + + /** + * 设置某个对象变量值 ( 可设置静态变量 ) + * @param object 对象 + * @param fieldName 属性名 + * @param value 字段值 + * @return {@code true} success, {@code false} fail + */ + public static boolean setProperty(final Object object, final String fieldName, final Object value) { + if (object == null || fieldName == null) return false; + try { + Field field = object.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(object, value); + return true; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "setProperty"); + } + return false; + } + + /** + * 获取某个对象的变量 ( 可获取静态变量 ) + * @param object 对象 + * @param fieldName 属性名 + * @param 泛型 + * @return 该变量对象 + */ + public static T getProperty(final Object object, final String fieldName) { + if (object == null || fieldName == null) return null; + try { + Field field = object.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return (T) field.get(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getProperty"); + } + return null; + } + + // ======================== + // = 获取某个类的静态变量 = + // ======================== + + /** + * 获取某个类的静态变量 ( 只能获取静态变量 ) + * @param object 对象 + * @param fieldName 属性名 + * @param 泛型 + * @return 该变量对象 + */ + public static T getStaticProperty(final Object object, final String fieldName) { + if (object == null) return null; + return getStaticProperty(object.getClass().getName(), fieldName); + } + + /** + * 获取某个类的静态变量 ( 只能获取静态变量 ) + * @param clazz 类 + * @param fieldName 属性名 + * @param 泛型 + * @return 该变量对象 + */ + public static T getStaticProperty(final Class clazz, final String fieldName) { + if (clazz == null) return null; + return getStaticProperty(clazz.getName(), fieldName); + } + + /** + * 获取某个类的静态变量 ( 只能获取静态变量 ) + * @param className 类名 + * @param fieldName 属性名 + * @param 泛型 + * @return 该变量对象 + */ + public static T getStaticProperty(final String className, final String fieldName) { + if (className == null || fieldName == null) return null; + try { + Class clazz = Class.forName(className); + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return (T) field.get(clazz); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getStaticProperty"); + } + return null; + } + + // ==================== + // = 执行某个对象方法 = + // ==================== + + /** + * 执行某个对象方法 ( 可执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeMethod(final Object object, final String methodName) { + return invokeMethod(object, methodName, null, null); + } + + /** + * 执行某个对象方法 ( 可执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param args 参数 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeMethod(final Object object, final String methodName, final Object[] args) { + return invokeMethod(object, methodName, args, getArgsClass(args)); + } + + /** + * 执行某个对象方法 ( 可执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param args 参数 + * @param argsClass 参数类型 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeMethod(final Object object, final String methodName, final Object[] args, final Class[] argsClass) { + if (object == null || methodName == null) return null; + try { + Class clazz = object.getClass(); + if (args != null && argsClass != null) { // 参数、参数类型不为 null, 并且数量相等 + if (args.length == argsClass.length && args.length != 0) { + Method method = clazz.getDeclaredMethod(methodName, argsClass); + method.setAccessible(true); + return (T) method.invoke(object, args); + } + } else { + // 无参数、参数类型, 才执行 + if (args == null && argsClass == null) { + Method method = clazz.getDeclaredMethod(methodName); + method.setAccessible(true); + return (T) method.invoke(object); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "invokeMethod"); + } + return null; + } + + // ======================== + // = 执行某个类的静态方法 = + // ======================== + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Object object, final String methodName) { + if (object == null) return null; + return invokeStaticMethod(object.getClass().getName(), methodName, null, null); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param args 参数数组 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Object object, final String methodName, final Object[] args) { + if (object == null) return null; + return invokeStaticMethod(object.getClass().getName(), methodName, args, getArgsClass(args)); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param object 对象 + * @param methodName 方法名 + * @param args 参数数组 + * @param argsClass 参数类型 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Object object, final String methodName, final Object[] args, final Class[] argsClass) { + if (object == null) return null; + return invokeStaticMethod(object.getClass().getName(), methodName, args, argsClass); + } + + // = + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param clazz 类 + * @param methodName 方法名 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Class clazz, final String methodName) { + if (clazz == null) return null; + return invokeStaticMethod(clazz.getName(), methodName, null, null); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param clazz 类 + * @param methodName 方法名 + * @param args 参数数组 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Class clazz, final String methodName, final Object[] args) { + if (clazz == null) return null; + return invokeStaticMethod(clazz.getName(), methodName, args, getArgsClass(args)); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param clazz 类 + * @param methodName 方法名 + * @param args 参数数组 + * @param argsClass 参数类型 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final Class clazz, final String methodName, final Object[] args, final Class[] argsClass) { + if (clazz == null) return null; + return invokeStaticMethod(clazz.getName(), methodName, args, argsClass); + } + + // = + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param className 类名 + * @param methodName 方法名 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final String className, final String methodName) { + if (className == null) return null; + return invokeStaticMethod(className, methodName, null, null); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param className 类名 + * @param methodName 方法名 + * @param args 参数数组 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final String className, final String methodName, final Object[] args) { + if (className == null) return null; + return invokeStaticMethod(className, methodName, args, getArgsClass(args)); + } + + /** + * 执行某个类的静态方法 ( 只能执行静态方法 ) + * @param className 类名 + * @param methodName 方法名 + * @param args 参数数组 + * @param argsClass 参数类型 + * @param 泛型 + * @return 执行方法返回的结果 + */ + public static T invokeStaticMethod(final String className, final String methodName, final Object[] args, final Class[] argsClass) { + if (className == null || methodName == null) return null; + try { + Class clazz = Class.forName(className); + if (args != null && argsClass != null) { // 参数、参数类型不为 null, 并且数量相等 + if (args.length == argsClass.length && args.length != 0) { + Method method = clazz.getDeclaredMethod(methodName, argsClass); + method.setAccessible(true); + return (T) method.invoke(clazz, args); + } + } else { + // 无参数、参数类型, 才执行 + if (args == null && argsClass == null) { + Method method = clazz.getDeclaredMethod(methodName); + method.setAccessible(true); + return (T) method.invoke(clazz); + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "invokeStaticMethod"); + } + return null; + } + + // ============================= + // = 新建实例 ( 构造函数创建 ) = + // ============================= + + /** + * 新建实例 ( 构造函数创建 ) + * @param object 对象 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Object object) { + if (object == null) return null; + return newInstance(object.getClass().getName(), null, null); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param object 对象 + * @param args 参数 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Object object, final Object[] args) { + if (object == null) return null; + return newInstance(object.getClass().getName(), args, getArgsClass(args)); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param object 对象 + * @param args 参数 + * @param argsClass 参数类型 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Object object, final Object[] args, final Class[] argsClass) { + if (object == null) return null; + return newInstance(object.getClass().getName(), args, argsClass); + } + + // = + + /** + * 新建实例 ( 构造函数创建 ) + * @param clazz 类 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Class clazz) { + if (clazz == null) return null; + return newInstance(clazz.getName(), null, null); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param clazz 类 + * @param args 参数 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Class clazz, final Object[] args) { + if (clazz == null) return null; + return newInstance(clazz.getName(), args, getArgsClass(args)); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param clazz 类 + * @param args 参数 + * @param argsClass 参数类型 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final Class clazz, final Object[] args, final Class[] argsClass) { + if (clazz == null) return null; + return newInstance(clazz.getName(), args, argsClass); + } + + // = + + /** + * 新建实例 ( 构造函数创建 ) + * @param className 类名 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final String className) { + if (className == null) return null; + return newInstance(className, null, null); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param className 类名 + * @param args 参数 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final String className, final Object[] args) { + if (className == null) return null; + return newInstance(className, args, getArgsClass(args)); + } + + /** + * 新建实例 ( 构造函数创建 ) + * @param className 类名 + * @param args 参数 + * @param argsClass 参数类型 + * @param 泛型 + * @return 新建的实例 + */ + public static T newInstance(final String className, final Object[] args, final Class[] argsClass) { + if (className == null) return null; + try { + Class newoneClass = Class.forName(className); + if (args == null) { + return (T) newoneClass.newInstance(); + } else { + Constructor cons = newoneClass.getConstructor(argsClass); + return (T) cons.newInstance(args); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "newInstance"); + } + return null; + } + + // = + + /** + * 是不是某个类的实例 + * @param object 实例 + * @param clazz 待判断类 + * @return 如果 obj 是此类的实例, 则返回 true + */ + public static boolean isInstance(final Object object, final Class clazz) { + if (object == null || clazz == null) return false; + try { + return clazz.isInstance(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isInstance"); + } + return false; + } + + /** + * 获取参数类型 + * @param args 参数 + * @return 参数类型数组 + */ + public static Class[] getArgsClass(final Object... args) { + if (args != null) { + try { + Class[] argsClass = new Class[args.length]; + for (int i = 0, len = args.length; i < len; i++) { + argsClass[i] = args[i].getClass(); + } + return argsClass; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getArgsClass"); + } + } + return new Class[0]; + } + + // = + + /** + * 获取某个对象的变量 + *
+     *     例: 获取父类中的变量
+     *     Object object = 对象;
+     *     getObject(getDeclaredFieldParent(object, "父类中变量名"), object);
+     * 
+ * @param object 对象 + * @param field {@link Field} + * @param 泛型 + * @return 该变量对象 + */ + public static T getProperty(final Object object, final Field field) { + if (object == null || field == null) return null; + try { + field.setAccessible(true); + return (T) field.get(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getProperty"); + } + return null; + } + + /** + * 获取父类中的变量对象 + * @param object 子类对象 + * @param fieldName 父类中的属性名 + * @param 泛型 + * @return 父类中的变量对象 + */ + public static T getPropertyByParent(final Object object, final String fieldName) { + return getPropertyByParent(object, fieldName, 1); + } + + /** + * 获取父类中的变量对象 + * @param object 子类对象 + * @param fieldName 父类中的属性名 + * @param fieldNumber 字段出现次数, 如果父类还有父类, 并且有相同变量名, 设置负数 一直会跟到最后的变量 + * @param 泛型 + * @return 父类中的变量对象 + */ + public static T getPropertyByParent(final Object object, final String fieldName, final int fieldNumber) { + if (object == null || fieldName == null) return null; + try { + Field field = getDeclaredFieldParent(object, fieldName, fieldNumber); + return getProperty(object, field); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getPropertyByParent"); + } + return null; + } + + /** + * 获取父类中的变量对象 ( 循环向上转型, 获取对象的 DeclaredField) + * @param object 对象 + * @param fieldName 属性名 + * @return {@link Field} + */ + public static Field getDeclaredFieldParent(final Object object, final String fieldName) { + return getDeclaredFieldParent(object, fieldName, 1); + } + + /** + * 获取父类中的变量对象 ( 循环向上转型, 获取对象的 DeclaredField) + * @param object 子类对象 + * @param fieldName 父类中的属性名 + * @param fieldNumber 字段出现次数, 如果父类还有父类, 并且有相同变量名, 设置负数 一直会跟到最后的变量 + * @return {@link Field} 父类中的属性对象 + */ + public static Field getDeclaredFieldParent(final Object object, final String fieldName, final int fieldNumber) { + if (object == null || fieldName == null) return null; + try { + if (fieldNumber == 0) return null; + // 获取当前出现次数 + int number = 0; + // 限制值 + int limitNumber = (fieldNumber >= 0) ? fieldNumber : Integer.MAX_VALUE; + // = + Field field = null; + Class clazz = object.getClass(); + for (; clazz != Object.class; clazz = clazz.getSuperclass()) { + try { + field = clazz.getDeclaredField(fieldName); + number++; + } catch (Exception e) { + // 这里甚么都不要做, 并且这里的异常必须这样写, 不能抛出去 + // 如果这里的异常打印或者往外抛, 则就不会执行 clazz = clazz.getSuperclass(), 最后就不会进入到父类中了 + } + if (number >= limitNumber) { + return field; + } + } + // 负数表示跟到最后 + if (fieldNumber < 0) { + return field; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDeclaredFieldParent"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/ReflectUtils.java b/lib/DevJava/src/main/java/dev/utils/common/ReflectUtils.java new file mode 100644 index 0000000000..93800beff3 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/ReflectUtils.java @@ -0,0 +1,702 @@ +package dev.utils.common; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: 反射相关工具类 + * @author Ttt + */ +public final class ReflectUtils { + + // 日志 TAG + private static final String TAG = ReflectUtils.class.getSimpleName(); + + private final Class mType; + + private final Object mObject; + + private ReflectUtils(final Class type) { + this(type, type); + } + + private ReflectUtils(final Class type, final Object object) { + this.mType = type; + this.mObject = object; + } + + // =========== + // = reflect = + // =========== + + /** + * 设置要反射的类 + * @param className 完整类名 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + public static ReflectUtils reflect(final String className) throws ReflectException { + return reflect(forName(className)); + } + + /** + * 设置要反射的类 + * @param className 完整类名 + * @param classLoader 类加载器 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + public static ReflectUtils reflect(final String className, final ClassLoader classLoader) throws ReflectException { + return reflect(forName(className, classLoader)); + } + + /** + * 设置要反射的类 + * @param clazz 类的类型 + * @return {@link ReflectUtils} + */ + public static ReflectUtils reflect(final Class clazz) { + return new ReflectUtils(clazz); + } + + /** + * 设置要反射的类 + * @param object 类对象 + * @return {@link ReflectUtils} + */ + public static ReflectUtils reflect(final Object object) { + return new ReflectUtils(object == null ? Object.class : object.getClass(), object); + } + + // = + + /** + * 获取 Class + * @param className 类名 + * @param 未知类型 + * @return 指定类 + * @throws ReflectException 反射异常 + */ + private static Class forName(final String className) throws ReflectException { + try { + return Class.forName(className); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "forName"); + throw new ReflectException(e); + } + } + + /** + * 获取 Class + * @param name 类名 + * @param classLoader 类加载器 + * @param 未知类型 + * @return 指定类 + * @throws ReflectException 反射异常 + */ + private static Class forName(final String name, final ClassLoader classLoader) throws ReflectException { + try { + return Class.forName(name, true, classLoader); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "forName"); + throw new ReflectException(e); + } + } + + // =============== + // = newInstance = + // =============== + + /** + * 实例化反射对象 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + public ReflectUtils newInstance() throws ReflectException { + return newInstance(new Object[0]); + } + + /** + * 实例化反射对象 + * @param args 实例化需要的参数 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + public ReflectUtils newInstance(final Object... args) throws ReflectException { + Class[] types = getArgsType(args); + try { + Constructor constructor = type().getDeclaredConstructor(types); + return newInstance(constructor, args); + } catch (NoSuchMethodException e) { + List> list = new ArrayList<>(); + for (Constructor constructor : type().getDeclaredConstructors()) { + if (match(constructor.getParameterTypes(), types)) { + list.add(constructor); + } + } + if (list.isEmpty()) { + throw new ReflectException(e); + } else { + sortConstructors(list); + return newInstance(list.get(0), args); + } + } catch (Exception e) { + throw new ReflectException(e); + } + } + + /** + * 获取参数类型 + * @param args 参数数组 + * @param 未知类型 + * @return 参数类型数组 + */ + private Class[] getArgsType(final Object... args) { + if (args == null) return new Class[0]; + Class[] result = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + Object value = args[i]; + result[i] = value == null ? NULL.class : value.getClass(); + } + return result; + } + + /** + * 进行排序 + * @param list 类构造函数信息集合 + */ + private void sortConstructors(final List> list) { + if (list == null) return; + Collections.sort(list, new Comparator>() { + @Override + public int compare(Constructor o1, Constructor o2) { + Class[] types1 = o1.getParameterTypes(); + Class[] types2 = o2.getParameterTypes(); + int len = types1.length; + for (int i = 0; i < len; i++) { + if (!types1[i].equals(types2[i])) { + if (wrapper(types1[i]).isAssignableFrom(wrapper(types2[i]))) { + return 1; + } else { + return -1; + } + } + } + return 0; + } + }); + } + + /** + * 获取实例对象 + * @param constructor 类构造函数信息 + * @param args 构造参数数组 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + private ReflectUtils newInstance(final Constructor constructor, final Object... args) throws ReflectException { + try { + return new ReflectUtils(constructor.getDeclaringClass(), accessible(constructor).newInstance(args)); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "newInstance"); + throw new ReflectException(e); + } + } + + // ========= + // = field = + // ========= + + /** + * 设置反射的字段 + * @param name 字段名 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + public ReflectUtils field(final String name) throws ReflectException { + try { + Field field = getField(name); + return new ReflectUtils(field.getType(), field.get(mObject)); + } catch (Exception e) { + throw new ReflectException(e); + } + } + + /** + * 设置反射的字段 + * @param name 字段名 + * @param value 字段值 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + public ReflectUtils field(final String name, final Object value) throws ReflectException { + try { + Field field = getField(name); + field.set(mObject, unwrap(value)); + return this; + } catch (Exception e) { + throw new ReflectException(e); + } + } + + /** + * 获取 Field 对象 + * @param name 字段名 + * @return {@link Field} + * @throws ReflectException 反射异常 + */ + private Field getField(final String name) throws ReflectException { + Field field = getAccessibleField(name); + if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) { + try { + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + } catch (NoSuchFieldException ignore) { + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getField"); + throw new ReflectException(e); + } + } + return field; + } + + /** + * 获取可访问字段, 返回 Field 对象 + * @param name 字段名 + * @return {@link Field} + * @throws ReflectException 反射异常 + */ + private Field getAccessibleField(final String name) throws ReflectException { + Class type = type(); + try { + return accessible(type.getField(name)); + } catch (NoSuchFieldException e) { + do { + try { + return accessible(type.getDeclaredField(name)); + } catch (NoSuchFieldException ignore) { + } + type = type.getSuperclass(); + } while (type != null); + JCLogUtils.eTag(TAG, e, "getAccessibleField"); + throw new ReflectException(e); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAccessibleField"); + throw new ReflectException(e); + } + } + + /** + * 获取对象 + * @param object 对象 + * @return 需要反射的对象 + */ + private Object unwrap(final Object object) { + if (object instanceof ReflectUtils) { + return ((ReflectUtils) object).get(); + } + return object; + } + + // = + + /** + * 设置枚举值 + * @param clazz 类型 + * @param name 字段名 + * @param value 字段值 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + public ReflectUtils setEnumVal(final Class clazz, final String name, final String value) throws ReflectException { + try { + return field(name, Enum.valueOf((Class) clazz, value)); + } catch (Exception e) { + throw new ReflectException(e); + } + } + + // ========== + // = method = + // ========== + + /** + * 设置反射的方法 + * @param name 方法名 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + public ReflectUtils method(final String name) throws ReflectException { + return method(name, new Object[0]); + } + + /** + * 设置反射的方法 + * @param name 方法名 + * @param args 方法需要的参数 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + public ReflectUtils method(final String name, final Object... args) throws ReflectException { + Class[] types = getArgsType(args); + try { + Method method = exactMethod(name, types); + return method(method, mObject, args); + } catch (Exception e) { + try { + Method method = similarMethod(name, types); + return method(method, mObject, args); + } catch (Exception e1) { + throw new ReflectException(e1); + } + } + } + + /** + * 设置反射的方法处理 + * @param method 方法 + * @param object 对象 + * @param args 参数 + * @return {@link ReflectUtils} + * @throws ReflectException 反射异常 + */ + private ReflectUtils method(final Method method, final Object object, final Object... args) throws ReflectException { + try { + accessible(method); + if (method.getReturnType() == void.class) { + method.invoke(object, args); + return reflect(object); + } else { + return reflect(method.invoke(object, args)); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "method"); + throw new ReflectException(e); + } + } + + /** + * 获取准确参数的方法 + * @param name 方法 + * @param types 参数类型 + * @return {@link Method} + * @throws ReflectException 反射异常 + */ + private Method exactMethod(final String name, final Class[] types) throws ReflectException { + Class type = type(); + try { + return type.getMethod(name, types); + } catch (Exception e) { + do { + try { + return type.getDeclaredMethod(name, types); + } catch (Exception ignore) { + } + type = type.getSuperclass(); + } while (type != null); + JCLogUtils.eTag(TAG, e, "exactMethod"); + throw new ReflectException(e); + } + } + + /** + * 获取相似参数的方法 + * @param name 方法 + * @param types 参数类型 + * @return {@link Method} + * @throws ReflectException 反射异常 + */ + private Method similarMethod(final String name, final Class[] types) throws ReflectException { + Class type = type(); + List methods = new ArrayList<>(); + for (Method method : type.getMethods()) { + if (isSimilarSignature(method, name, types)) { + methods.add(method); + } + } + if (!methods.isEmpty()) { + sortMethods(methods); + return methods.get(0); + } + do { + for (Method method : type.getDeclaredMethods()) { + if (isSimilarSignature(method, name, types)) { + methods.add(method); + } + } + if (!methods.isEmpty()) { + sortMethods(methods); + return methods.get(0); + } + type = type.getSuperclass(); + } while (type != null); + throw new ReflectException(String.format("No similar method %s with params %s could be found on type %s", name, Arrays.toString(types), type())); + } + + /** + * 进行方法排序 + * @param methods 方法集合 + */ + private void sortMethods(final List methods) { + if (methods == null) return; + Collections.sort(methods, new Comparator() { + @Override + public int compare(Method o1, Method o2) { + Class[] types1 = o1.getParameterTypes(); + Class[] types2 = o2.getParameterTypes(); + int len = types1.length; + for (int i = 0; i < len; i++) { + if (!types1[i].equals(types2[i])) { + if (wrapper(types1[i]).isAssignableFrom(wrapper(types2[i]))) { + return 1; + } else { + return -1; + } + } + } + return 0; + } + }); + } + + /** + * 判断是否相似方法 + * @param possiblyMatchingMethod 可能的匹配方法 + * @param desiredMethodName 期望方法名 + * @param desiredParamTypes 所需参数类型 + * @return {@code true} yes, {@code false} no + */ + private boolean isSimilarSignature(final Method possiblyMatchingMethod, final String desiredMethodName, final Class[] desiredParamTypes) { + return possiblyMatchingMethod.getName().equals(desiredMethodName) && match(possiblyMatchingMethod.getParameterTypes(), desiredParamTypes); + } + + /** + * 对比处理, 判断是否一样 + * @param declaredTypes 声明类型 + * @param actualTypes 实际类型 + * @return {@code true} yes, {@code false} no + */ + private boolean match(final Class[] declaredTypes, final Class[] actualTypes) { + if (declaredTypes != null && actualTypes != null && declaredTypes.length == actualTypes.length) { + for (int i = 0; i < actualTypes.length; i++) { + if (actualTypes[i] == NULL.class || wrapper(declaredTypes[i]).isAssignableFrom(wrapper(actualTypes[i]))) { + continue; + } + return false; + } + return true; + } else { + return false; + } + } + + /** + * 设置对象可访问处理 + * @param accessible 对象 + * @param 泛型 + * @return 传入的对象 + */ + private T accessible(final T accessible) { + if (accessible == null) return null; + if (accessible instanceof Member) { + Member member = (Member) accessible; + if (Modifier.isPublic(member.getModifiers()) && Modifier.isPublic(member.getDeclaringClass().getModifiers())) { + return accessible; + } + } + if (!accessible.isAccessible()) accessible.setAccessible(true); + return accessible; + } + + // ======== + // = 代理 = + // ======== + + /** + * 根据类, 代理创建并返回对象 + * @param proxyType 代理类 + * @param

泛型 + * @return 代理的对象 + */ + public

P proxy(final Class

proxyType) { + if (proxyType == null || mObject == null) return null; + final boolean isMap = (mObject instanceof Map); + final InvocationHandler handler = new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + String name = method.getName(); + try { + return reflect(mObject).method(name, args).get(); + } catch (Exception e) { + if (isMap) { + Map map = (Map) mObject; + int length = (args == null ? 0 : args.length); + if (length == 0 && name.startsWith("get")) { + return map.get(property(name.substring(3))); + } else if (length == 0 && name.startsWith("is")) { + return map.get(property(name.substring(2))); + } else if (length == 1 && name.startsWith("set")) { + map.put(property(name.substring(3)), args[0]); + return null; + } + } + JCLogUtils.eTag(TAG, e, "proxy"); + } + return null; + } + }; + return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), new Class[]{proxyType}, handler); + } + + /** + * 获取实体类属性名 get/set + * @param str 属性名 + * @return 属性名字 + */ + private String property(final String str) { + int length = str.length(); + if (length == 0) { + return ""; + } else if (length == 1) { + return str.toLowerCase(); + } else { + return str.substring(0, 1).toLowerCase() + str.substring(1); + } + } + + // = + + /** + * 获取类型 + * @return {@link Class} + */ + public Class type() { + return mType; + } + + /** + * 获取类型 + * @param type {@link Class} + * @param 未知类型 + * @return {@link Class} 类所属类型 + */ + private Class wrapper(final Class type) { + if (type == null) { + return null; + } else if (type.isPrimitive()) { + if (boolean.class == type) { + return Boolean.class; + } else if (int.class == type) { + return Integer.class; + } else if (long.class == type) { + return Long.class; + } else if (short.class == type) { + return Short.class; + } else if (byte.class == type) { + return Byte.class; + } else if (double.class == type) { + return Double.class; + } else if (float.class == type) { + return Float.class; + } else if (char.class == type) { + return Character.class; + } else if (void.class == type) { + return Void.class; + } + } + return type; + } + + /** + * 获取反射想要获取的 + * @param 泛型 + * @return 反射想要获取的 + */ + public T get() { + return (T) mObject; + } + + /** + * 获取 HashCode + * @return hashCode + */ + @Override + public int hashCode() { + return this.mObject != null ? mObject.hashCode() : 0; + } + + /** + * 判断反射的两个对象是否一样 + * @param object 对象 + * @return {@code true} yes, {@code false} no + */ + @Override + public boolean equals(final Object object) { + if (this.mObject == null && object == null) { + return true; + } else { + if (this.mObject != null && object != null) { + if (object instanceof ReflectUtils) { + return this.mObject.equals(((ReflectUtils) object).get()); + } else { + this.mObject.equals(object); + } + } + return false; + } + } + + /** + * 获取反射获取的对象 + * @return {@link Object#toString()} + */ + @Override + public String toString() { + return this.mObject != null ? mObject.toString() : null; + } + + // = + + /** + * detail: 内部标记 null + * @author Ttt + */ + private static class NULL { + } + + /** + * detail: 定义 ReflectUtils 工具异常类 + * @author Ttt + */ + public static class ReflectException extends Exception { + + private static final long serialVersionUID = 858774075258496016L; + + public ReflectException(String message) { + super(message); + } + + public ReflectException(String message, Throwable cause) { + super(message, cause); + } + + public ReflectException(Throwable cause) { + super(cause); + } + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/ScaleUtils.java b/lib/DevJava/src/main/java/dev/utils/common/ScaleUtils.java new file mode 100644 index 0000000000..850fd1eadf --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/ScaleUtils.java @@ -0,0 +1,520 @@ +package dev.utils.common; + +import dev.utils.JCLogUtils; + +/** + * detail: 计算比例工具类 + * @author Ttt + */ +public final class ScaleUtils { + + private ScaleUtils() { + } + + // 日志 TAG + private static final String TAG = ScaleUtils.class.getSimpleName(); + + // ======= + // = int = + // ======= + + /** + * 计算缩放比例 - 根据宽度比例转换高度 + * @param targetWidth 需要的最终宽度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return int[] { 宽度, 高度 } + */ + public static int[] calcScaleToWidth(final int targetWidth, final int currentWidth, final int currentHeight) { + try { + if (currentWidth == 0) { + return new int[]{0, 0}; + } + // 计算比例 + float scale = ((float) targetWidth) / ((float) currentWidth); + // 计算缩放后的高度 + int scaleHeight = (int) (scale * (float) currentHeight); + // 返回对应的数据 + return new int[]{targetWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToWidth"); + } + return null; + } + + /** + * 计算缩放比例 - 根据高度比例转换宽度 + * @param targetHeight 需要的最终高度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return int[] { 宽度, 高度 } + */ + public static int[] calcScaleToHeight(final int targetHeight, final int currentWidth, final int currentHeight) { + try { + if (currentHeight == 0) { + return new int[]{0, 0}; + } + // 计算比例 + float scale = ((float) targetHeight) / ((float) currentHeight); + // 计算缩放后的宽度 + int scaleWidth = (int) (scale * (float) currentWidth); + // 返回对应的数据 + return new int[]{scaleWidth, targetHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToHeight"); + } + return null; + } + + /** + * 通过宽度、高度根据对应的比例, 转换成对应的比例宽度高度 - 智能转换 + * @param width 宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return int[] { 宽度, 高度 } + */ + public static int[] calcWidthHeightToScale(final int width, final int height, final float widthScale, final float heightScale) { + try { + // 如果宽度的比例, 大于等于高度比例 + if (widthScale >= heightScale) { // 以宽度为基准 + // 设置宽度, 以宽度为基准 + int scaleWidth = width; + // 计算宽度 + int scaleHeight = (int) (((float) scaleWidth) * (heightScale / widthScale)); + // 返回对应的比例 + return new int[]{scaleWidth, scaleHeight}; + } else { // 以高度为基准 + // 设置高度 + int scaleHeight = height; + // 同步缩放比例 + int scaleWidth = (int) (((float) scaleHeight) * (widthScale / heightScale)); + // 返回对应的比例 + return new int[]{scaleWidth, scaleHeight}; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthHeightToScale"); + } + return null; + } + + /** + * 以宽度为基准, 转换对应比例的高度 + * @param width 宽度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return int[] { 宽度, 高度 } + */ + public static int[] calcWidthToScale(final int width, final float widthScale, final float heightScale) { + try { + // 设置宽度 + int scaleWidth = width; + // 计算高度 + int scaleHeight = (int) (((float) scaleWidth) * (heightScale / widthScale)); + // 返回对应的比例 + return new int[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthToScale"); + } + return null; + } + + /** + * 以高度为基准, 转换对应比例的宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return int[] { 宽度, 高度 } + */ + public static int[] calcHeightToScale(final int height, final float widthScale, final float heightScale) { + try { + // 设置高度 + int scaleHeight = height; + // 计算宽度 + int scaleWidth = (int) (((float) scaleHeight) * (widthScale / heightScale)); + // 返回对应的比例 + return new int[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcHeightToScale"); + } + return null; + } + + // ========== + // = double = + // ========== + + /** + * 计算缩放比例 - 根据宽度比例转换高度 + * @param targetWidth 需要的最终宽度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return double[] { 宽度, 高度 } + */ + public static double[] calcScaleToWidth(final double targetWidth, final double currentWidth, final double currentHeight) { + try { + if (currentWidth == 0d) { + return new double[]{0d, 0d}; + } + // 计算比例 + double scale = targetWidth / currentWidth; + // 计算缩放后的高度 + double scaleHeight = scale * currentHeight; + // 返回对应的数据 + return new double[]{targetWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToWidth"); + } + return null; + } + + /** + * 计算缩放比例 - 根据高度比例转换宽度 + * @param targetHeight 需要的最终高度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return double[] { 宽度, 高度 } + */ + public static double[] calcScaleToHeight(final double targetHeight, final double currentWidth, final double currentHeight) { + try { + if (currentHeight == 0d) { + return new double[]{0d, 0d}; + } + // 计算比例 + double scale = targetHeight / currentHeight; + // 计算缩放后的宽度 + double scaleWidth = scale * currentWidth; + // 返回对应的数据 + return new double[]{scaleWidth, targetHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToHeight"); + } + return null; + } + + /** + * 通过宽度、高度根据对应的比例, 转换成对应的比例宽度高度 - 智能转换 + * @param width 宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return double[] { 宽度, 高度 } + */ + public static double[] calcWidthHeightToScale(final double width, final double height, final double widthScale, final double heightScale) { + try { + // 如果宽度的比例, 大于等于高度比例 + if (widthScale >= heightScale) { // 以宽度为基准 + // 设置宽度, 以宽度为基准 + double scaleWidth = width; + // 计算宽度 + double scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new double[]{scaleWidth, scaleHeight}; + } else { // 以高度为基准 + // 设置高度 + double scaleHeight = height; + // 同步缩放比例 + double scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new double[]{scaleWidth, scaleHeight}; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthHeightToScale"); + } + return null; + } + + /** + * 以宽度为基准, 转换对应比例的高度 + * @param width 宽度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return double[] { 宽度, 高度 } + */ + public static double[] calcWidthToScale(final double width, final double widthScale, final double heightScale) { + try { + // 设置宽度 + double scaleWidth = width; + // 计算高度 + double scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new double[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthToScale"); + } + return null; + } + + /** + * 以高度为基准, 转换对应比例的宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return double[] { 宽度, 高度 } + */ + public static double[] calcHeightToScale(final double height, final double widthScale, final double heightScale) { + try { + // 设置高度 + double scaleHeight = height; + // 计算宽度 + double scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new double[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcHeightToScale"); + } + return null; + } + + // ========= + // = long = + // ========= + + /** + * 计算缩放比例 - 根据宽度比例转换高度 + * @param targetWidth 需要的最终宽度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return long[] { 宽度, 高度 } + */ + public static long[] calcScaleToWidth(final long targetWidth, final long currentWidth, final long currentHeight) { + try { + if (currentWidth == 0L) { + return new long[]{0L, 0L}; + } + // 计算比例 + long scale = targetWidth / currentWidth; + // 计算缩放后的高度 + long scaleHeight = scale * currentHeight; + // 返回对应的数据 + return new long[]{targetWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToWidth"); + } + return null; + } + + /** + * 计算缩放比例 - 根据高度比例转换宽度 + * @param targetHeight 需要的最终高度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return long[] { 宽度, 高度 } + */ + public static long[] calcScaleToHeight(final long targetHeight, final long currentWidth, final long currentHeight) { + try { + if (currentHeight == 0L) { + return new long[]{0L, 0L}; + } + // 计算比例 + long scale = targetHeight / currentHeight; + // 计算缩放后的宽度 + long scaleWidth = scale * currentWidth; + // 返回对应的数据 + return new long[]{scaleWidth, targetHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToHeight"); + } + return null; + } + + /** + * 通过宽度、高度根据对应的比例, 转换成对应的比例宽度高度 - 智能转换 + * @param width 宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return long[] { 宽度, 高度 } + */ + public static long[] calcWidthHeightToScale(final long width, final long height, final long widthScale, final long heightScale) { + try { + // 如果宽度的比例, 大于等于高度比例 + if (widthScale >= heightScale) { // 以宽度为基准 + // 设置宽度, 以宽度为基准 + long scaleWidth = width; + // 计算宽度 + long scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new long[]{scaleWidth, scaleHeight}; + } else { // 以高度为基准 + // 设置高度 + long scaleHeight = height; + // 同步缩放比例 + long scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new long[]{scaleWidth, scaleHeight}; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthHeightToScale"); + } + return null; + } + + /** + * 以宽度为基准, 转换对应比例的高度 + * @param width 宽度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return long[] { 宽度, 高度 } + */ + public static long[] calcWidthToScale(final long width, final long widthScale, final long heightScale) { + try { + // 设置宽度 + long scaleWidth = width; + // 计算高度 + long scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new long[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthToScale"); + } + return null; + } + + /** + * 以高度为基准, 转换对应比例的宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return long[] { 宽度, 高度 } + */ + public static long[] calcHeightToScale(final long height, final long widthScale, final long heightScale) { + try { + // 设置高度 + long scaleHeight = height; + // 计算宽度 + long scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new long[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcHeightToScale"); + } + return null; + } + + // ========= + // = float = + // ========= + + /** + * 计算缩放比例 - 根据宽度比例转换高度 + * @param targetWidth 需要的最终宽度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return float[] { 宽度, 高度 } + */ + public static float[] calcScaleToWidth(final float targetWidth, final float currentWidth, final float currentHeight) { + try { + if (currentWidth == 0f) { + return new float[]{0f, 0f}; + } + // 计算比例 + float scale = targetWidth / currentWidth; + // 计算缩放后的高度 + float scaleHeight = scale * currentHeight; + // 返回对应的数据 + return new float[]{targetWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToWidth"); + } + return null; + } + + /** + * 计算缩放比例 - 根据高度比例转换宽度 + * @param targetHeight 需要的最终高度 + * @param currentWidth 当前宽度 + * @param currentHeight 当前高度 + * @return float[] { 宽度, 高度 } + */ + public static float[] calcScaleToHeight(final float targetHeight, final float currentWidth, final float currentHeight) { + try { + if (currentHeight == 0f) { + return new float[]{0f, 0f}; + } + // 计算比例 + float scale = targetHeight / currentHeight; + // 计算缩放后的宽度 + float scaleWidth = scale * currentWidth; + // 返回对应的数据 + return new float[]{scaleWidth, targetHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcScaleToHeight"); + } + return null; + } + + /** + * 通过宽度、高度根据对应的比例, 转换成对应的比例宽度高度 - 智能转换 + * @param width 宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return float[] { 宽度, 高度 } + */ + public static float[] calcWidthHeightToScale(final float width, final float height, final float widthScale, final float heightScale) { + try { + // 如果宽度的比例, 大于等于高度比例 + if (widthScale >= heightScale) { // 以宽度为基准 + // 设置宽度, 以宽度为基准 + float scaleWidth = width; + // 计算宽度 + float scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new float[]{scaleWidth, scaleHeight}; + } else { // 以高度为基准 + // 设置高度 + float scaleHeight = height; + // 同步缩放比例 + float scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new float[]{scaleWidth, scaleHeight}; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthHeightToScale"); + } + return null; + } + + /** + * 以宽度为基准, 转换对应比例的高度 + * @param width 宽度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return float[] { 宽度, 高度 } + */ + public static float[] calcWidthToScale(final float width, final float widthScale, final float heightScale) { + try { + // 设置宽度 + float scaleWidth = width; + // 计算高度 + float scaleHeight = scaleWidth * (heightScale / widthScale); + // 返回对应的比例 + return new float[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcWidthToScale"); + } + return null; + } + + /** + * 以高度为基准, 转换对应比例的宽度 + * @param height 高度 + * @param widthScale 宽度比例 + * @param heightScale 高度比例 + * @return float[] { 宽度, 高度 } + */ + public static float[] calcHeightToScale(final float height, final float widthScale, final float heightScale) { + try { + // 设置高度 + float scaleHeight = height; + // 计算宽度 + float scaleWidth = scaleHeight * (widthScale / heightScale); + // 返回对应的比例 + return new float[]{scaleWidth, scaleHeight}; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "calcHeightToScale"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/StreamUtils.java b/lib/DevJava/src/main/java/dev/utils/common/StreamUtils.java new file mode 100644 index 0000000000..d49601f3c9 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/StreamUtils.java @@ -0,0 +1,221 @@ +package dev.utils.common; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import dev.utils.JCLogUtils; + +/** + * detail: 流操作工具类 + * @author Ttt + */ +public final class StreamUtils { + + private StreamUtils() { + } + + // 日志 TAG + private static final String TAG = StreamUtils.class.getSimpleName(); + + /** + * 输入流转输出流 + * @param inputStream {@link InputStream} + * @return {@link ByteArrayOutputStream} + */ + public static ByteArrayOutputStream inputToOutputStream(final InputStream inputStream) { + if (inputStream == null) return null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer, 0, 1024)) != -1) { + baos.write(buffer, 0, len); + } + return baos; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "inputToOutputStream"); + return null; + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (Exception e) { + } + } + } + } + + /** + * 输出流转输入流 + * @param outputStream {@link OutputStream} + * @return {@link ByteArrayInputStream} + */ + public static ByteArrayInputStream outputToInputStream(final OutputStream outputStream) { + if (outputStream == null) return null; + try { + return new ByteArrayInputStream(((ByteArrayOutputStream) outputStream).toByteArray()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "outputToInputStream"); + return null; + } + } + + /** + * 输入流转 byte[] + * @param inputStream {@link InputStream} + * @return byte[] + */ + public static byte[] inputStreamToBytes(final InputStream inputStream) { + if (inputStream == null) return null; + try { + return inputToOutputStream(inputStream).toByteArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "inputStreamToBytes"); + return null; + } + } + + /** + * byte[] 转输出流 + * @param bytes 数据源 + * @return {@link InputStream} + */ + public static InputStream bytesToInputStream(final byte[] bytes) { + if (bytes == null || bytes.length == 0) return null; + try { + return new ByteArrayInputStream(bytes); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToInputStream"); + return null; + } + } + + /** + * 输出流转 byte[] + * @param outputStream {@link OutputStream} + * @return byte[] + */ + public static byte[] outputStreamToBytes(final OutputStream outputStream) { + if (outputStream == null) return null; + try { + return ((ByteArrayOutputStream) outputStream).toByteArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "outputStreamToBytes"); + return null; + } + } + + /** + * byte[] 转 输出流 + * @param bytes 数据源 + * @return {@link OutputStream} + */ + public static OutputStream bytesToOutputStream(final byte[] bytes) { + if (bytes == null || bytes.length == 0) return null; + ByteArrayOutputStream baos = null; + try { + baos = new ByteArrayOutputStream(); + baos.write(bytes); + return baos; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToOutputStream"); + return null; + } finally { + if (baos != null) { + try { + baos.close(); + } catch (Exception e) { + } + } + } + } + + /** + * 输入流转 String + * @param inputStream {@link InputStream} + * @param charsetName 编码格式 + * @return 指定编码字符串 + */ + public static String inputStreamToString(final InputStream inputStream, final String charsetName) { + if (inputStream == null || isSpace(charsetName)) return null; + try { + return new String(inputStreamToBytes(inputStream), charsetName); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "inputStreamToString"); + return null; + } + } + + /** + * String 转换输入流 + * @param string 数据源 + * @param charsetName 编码格式 + * @return {@link InputStream} + */ + public static InputStream stringToInputStream(final String string, final String charsetName) { + if (string == null || isSpace(charsetName)) return null; + try { + return new ByteArrayInputStream(string.getBytes(charsetName)); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "stringToInputStream"); + return null; + } + } + + /** + * 输出流转 String + * @param outputStream {@link OutputStream} + * @param charsetName 编码格式 + * @return 指定编码字符串 + */ + public static String outputStreamToString(final OutputStream outputStream, final String charsetName) { + if (outputStream == null || isSpace(charsetName)) return null; + try { + return new String(outputStreamToBytes(outputStream), charsetName); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "outputStreamToString"); + return null; + } + } + + /** + * String 转 输出流 + * @param string 数据源 + * @param charsetName 编码格式 + * @return {@link OutputStream} + */ + public static OutputStream stringToOutputStream(final String string, final String charsetName) { + if (string == null || isSpace(charsetName)) return null; + try { + return bytesToOutputStream(string.getBytes(charsetName)); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "stringToOutputStream"); + return null; + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/StringUtils.java b/lib/DevJava/src/main/java/dev/utils/common/StringUtils.java new file mode 100644 index 0000000000..55fbd61b5c --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/StringUtils.java @@ -0,0 +1,1179 @@ +package dev.utils.common; + +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import dev.utils.JCLogUtils; + +/** + * detail: 字符串工具类 + * @author Ttt + */ +public final class StringUtils { + + private StringUtils() { + } + + // 日志 TAG + private static final String TAG = StringUtils.class.getSimpleName(); + // 换行字符串 + public static final String NEW_LINE_STR = System.getProperty("line.separator"); + // 换行字符串 ( 两行 ) + public static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; + + // ========== + // = String = + // ========== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @param isTrim 是否调用 trim() + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String str, final boolean isTrim) { + if (str != null) { + return isEmpty(isTrim ? str.trim() : str); + } + return false; + } + + /** + * 判断多个字符串是否存在为 null 的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} is null, {@code false} not null + */ + public static boolean isEmpty(final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + if (isEmpty(strs[i])) { + return true; + } + } + return false; + } + // 默认表示属于 null + return true; + } + + // = + + /** + * 判断字符串是否不为 null + * @param str 待校验的字符串 + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final String str) { + return (str != null && str.length() != 0); + } + + /** + * 判断字符串是否不为 null + * @param str 待校验的字符串 + * @param isTrim 是否调用 trim() + * @return {@code true} not null, {@code false} is null + */ + public static boolean isNotEmpty(final String str, final boolean isTrim) { + return isNotEmpty(isTrim ? str.trim() : str); + } + + // ============ + // = 判断长度 = + // ============ + + /** + * 获取字符串长度 + * @param str 待校验的字符串 + * @return 字符串长度, 如果字符串为 null, 则返回 0 + */ + public static int length(final String str) { + return str == null ? 0 : str.length(); + } + + /** + * 获取字符串长度 + * @param str 待校验的字符串 + * @param defaultLength 字符串为 null 时, 返回的长度 + * @return 字符串长度, 如果字符串为 null, 则返回 defaultLength + */ + public static int length(final String str, final int defaultLength) { + return str != null ? str.length() : defaultLength; + } + + // = + + /** + * 获取字符串长度 是否等于期望长度 + * @param str 待校验的字符串 + * @param length 期望长度 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLength(final String str, final int length) { + return str != null && str.length() == length; + } + + // ============ + // = 对比判断 = + // ============ + + /** + * 判断两个值是否一样 + * @param value1 第一个值 + * @param value2 第二个值 + * @param 泛型 + * @return {@code true} yes, {@code false} no + */ + public static boolean equals(final T value1, final T value2) { + // 两个值都不为 null + if (value1 != null && value2 != null) { + try { + if (value1 instanceof String && value2 instanceof String) { + return value1.equals(value2); + } else if (value1 instanceof CharSequence && value2 instanceof CharSequence) { + CharSequence v1 = (CharSequence) value1; + CharSequence v2 = (CharSequence) value2; + // 获取数据长度 + int length = v1.length(); + // 判断数据长度是否一致 + if (length == v2.length()) { + for (int i = 0; i < length; i++) { + if (v1.charAt(i) != v2.charAt(i)) { + return false; + } + } + return true; + } + return false; + } + // 其他都使用 equals 判断 + return value1.equals(value2); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "equals"); + } + return false; + } + // 防止两个值都为 null + return (value1 == null && value2 == null); + } + + /** + * 判断多个字符串是否相等, 只有全相等才返回 true - 对比大小写 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEquals(final String... strs) { + return isEquals(false, strs); + } + + /** + * 判断多个字符串是否相等, 只有全相等才返回 true + * @param isIgnore 是否忽略大小写 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEquals(final boolean isIgnore, final String... strs) { + if (strs != null) { + String last = null; + // 获取数据长度 + int len = strs.length; + // 如果最多只有一个数据判断, 则直接跳过 + if (len <= 1) { + return false; + } + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 如果等于 null, 则跳过 + if (val == null) { + return false; + } + if (last != null) { + if (isIgnore) { + if (!val.equalsIgnoreCase(last)) { + return false; + } + } else { + if (!val.equals(last)) { + return false; + } + } + } + last = val; + } + return true; + } + return false; + } + + /** + * 判断多个字符串, 只要有一个符合条件则通过 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isOrEquals(final String str, final String... strs) { + return isOrEquals(false, str, strs); + } + + /** + * 判断多个字符串, 只要有一个符合条件则通过 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isOrEquals(final boolean isIgnore, final String str, final String... strs) { + if (str != null && strs != null && strs.length != 0) { + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 如果等于 null, 则跳过 + if (val == null) { + continue; + } else { + if (isIgnore) { + if (val.equalsIgnoreCase(str)) { + return true; + } + } else { + if (val.equals(str)) { + return true; + } + } + } + } + } + return false; + } + + /** + * 统计字符串匹配个数 + * @param str 待匹配字符串 + * @param keyword 匹配 key + * @return 字符串 key 匹配个数 + */ + public static int countMatches(final String str, final String keyword) { + if (isEmpty(str) || isEmpty(keyword)) return 0; + try { + int count = 0; + Matcher matcher = Pattern.compile(keyword).matcher(str); + // find() 对字符串进行匹配, 匹配到的字符串可以在任何位置 + while (matcher.find()) { + count++; + } + return count; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "countMatches"); + } + return -1; + } + + /** + * 统计字符串匹配个数 + * @param str 待匹配字符串 + * @param keyword 匹配 key + * @return 字符串 key 匹配个数 + */ + public static int countMatches2(final String str, final String keyword) { + if (isEmpty(str) || isEmpty(keyword)) return 0; + try { + // 获取匹配 key 长度 + int keyLength = keyword.length(); + // = + int count = 0; + int index = 0; + while ((index = str.indexOf(keyword, index)) != -1) { + index = index + keyLength; + count++; + } + return count; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "countMatches2"); + } + return -1; + } + + /** + * 判断一堆值中, 是否存在符合该条件的 ( 包含 ) + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContains(final String str, final String... strs) { + return isContains(false, str, strs); + } + + /** + * 判断一堆值中, 是否存在符合该条件的 ( 包含 ) + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContains(final boolean isIgnore, final String str, final String... strs) { + if (str != null && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取内容长度 + int cLength = strTemp.length(); + // 遍历判断 + for (int i = 0, len = strs.length; i < len; i++) { + // 获取参数 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val) && cLength != 0) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否包含 + if (valIgnore.indexOf(strTemp) != -1) { + return true; + } + } else { + // 判断是否包含 + if (val.indexOf(strTemp) != -1) { + return true; + } + } + } else { + // 下面这一串可以不要, 因为判断字符串是否包含 + // 已经处理了值不为 null, 并且需要判断的值长度不能为 0, 下面则不需要加上 + if (strTemp.equals(val)) { + return true; + } + } + } + } + return false; + } + + /** + * 判断内容, 是否属于特定字符串开头 - 对比大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isStartsWith(final String str, final String... strs) { + return isStartsWith(false, str, strs); + } + + /** + * 判断内容, 是否属于特定字符串开头 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isStartsWith(final boolean isIgnore, final String str, final String... strs) { + if (!isEmpty(str) && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val)) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否属于 val 开头 + if (strTemp.startsWith(valIgnore)) { + return true; + } + } else { + // 判断是否属于 val 开头 + if (strTemp.startsWith(val)) { + return true; + } + } + } + } + } + return false; + } + + /** + * 判断内容, 是否属于特定字符串结尾 - 对比大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEndsWith(final String str, final String... strs) { + return isEndsWith(false, str, strs); + } + + /** + * 判断内容, 是否属于特定字符串结尾 + * @param isIgnore 是否忽略大小写 + * @param str 待校验的字符串 + * @param strs 待校验的字符串数组 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEndsWith(final boolean isIgnore, final String str, final String... strs) { + if (!isEmpty(str) && strs != null && strs.length != 0) { + String strTemp = str; + // 判断是否需要忽略大小写 + if (isIgnore) { + strTemp = strTemp.toLowerCase(); + } + // 获取数据长度 + int len = strs.length; + // 遍历判断 + for (int i = 0; i < len; i++) { + // 获取临时变量 + String val = strs[i]; + // 判断是否为 null, 或者长度为 0 + if (!isEmpty(val)) { + if (isIgnore) { + // 转换小写 + String valIgnore = val.toLowerCase(); + // 判断是否属于 val 结尾 + if (strTemp.endsWith(valIgnore)) { + return true; + } + } else { + // 判断是否属于 val 结尾 + if (strTemp.endsWith(val)) { + return true; + } + } + } + } + } + return false; + } + + // ============ + // = 其他处理 = + // ============ + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + * 清空字符串全部空格 + * @param str 待处理字符串 + * @return 处理后的字符串 + */ + public static String toClearSpace(final String str) { + if (isEmpty(str)) return str; + return str.replaceAll(" ", ""); + } + + /** + * 清空字符串前后所有空格 + * @param str 待处理字符串 + * @return 处理后的字符串 + */ + public static String toClearSpaceTrim(final String str) { + if (isEmpty(str)) return str; + String strTemp = str; + // 如果前面或者后面都是空格开头, 就一直进行处理 + while (strTemp.startsWith(" ") || strTemp.endsWith(" ")) { + strTemp = strTemp.trim(); + } + return strTemp; + } + + // = + + /** + * 追加空格 + * @param number 空格数量 + * @return 指定数量的空格字符串 + */ + public static String appendSpace(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append(" "); + } + } + return builder.toString(); + } + + /** + * 追加 Tab + * @param number tab 键数量 + * @return 指定数量的 Tab 字符串 + */ + public static String appendTab(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append("\t"); + } + } + return builder.toString(); + } + + /** + * 追加换行 + * @param number 换行数量 + * @return 指定数量的换行字符串 + */ + public static String appendLine(final int number) { + StringBuilder builder = new StringBuilder(); + if (number > 0) { + for (int i = 0; i < number; i++) { + builder.append(NEW_LINE_STR); + } + } + return builder.toString(); + } + + /** + * 冒号分割处理 + * @param str 待处理字符串 + * @return 冒号分割后的字符串 + */ + public static String colonSplit(final String str) { + if (!isEmpty(str)) { + return str.replaceAll("(?<=[0-9A-F]{2})[0-9A-F]{2}", ":$0"); + } + return str; + } + + // = + + /** + * 检查字符串 + * @param str 待校验字符串 + * @return 如果待校验字符串为 null, 则返回默认字符串, 如果不为 null, 则返回该字符串 + */ + public static String toCheckValue(final String str) { + return toCheckValue("", str); + } + + /** + * 检查字符串 + * @param defaultStr 默认字符串 + * @param str 待校验字符串 + * @return 如果待校验字符串为 null, 则返回 defaultStr, 如果不为 null, 则返回该字符串 + */ + public static String toCheckValue(final String defaultStr, final String str) { + return isEmpty(str) ? defaultStr : str; + } + + /** + * 检查字符串 - 单独检查两个值 + * @param defaultStr 默认字符串 + * @param value1 第一个待校验字符串 + * @param value2 第二个待校验字符串 + * @return 两个待校验字符串中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValue(final String defaultStr, final String value1, final String value2) { + if (isEmpty(value1)) { + if (isEmpty(value2)) { + return defaultStr; + } else { + return value2; + } + } else { + return value1; + } + } + + /** + * 检查字符串 - 多个值 + * @param defaultStr 默认字符串 + * @param strs 待校验字符串数组 + * @return 字符串数组中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValues(final String defaultStr, final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + String val = strs[i]; + if (isEmpty(val)) { + if (i == len - 1) { + return defaultStr; // 属于最后一个, 则返回默认值 + } else { + continue; // 不属于最后一个则跳过 + } + } else { + return val; + } + } + } + return defaultStr; + } + + /** + * 检查字符串 - 多个值 ( 删除前后空格对比判断 ) + * @param defaultStr 默认字符串 + * @param strs 待校验字符串数组 + * @return 字符串数组中不为 null 的字符串, 如果都为 null, 则返回 defaultStr + */ + public static String toCheckValuesSpace(final String defaultStr, final String... strs) { + if (strs != null && strs.length != 0) { + for (int i = 0, len = strs.length; i < len; i++) { + // 删除前后空格处理后, 进行返回 + String val = toClearSpaceTrim(strs[i]); + if (isEmpty(val)) { + if (i == len - 1) { + return defaultStr; // 属于最后一个, 则返回默认值 + } else { + continue; // 不属于最后一个则跳过 + } + } else { + return val; + } + } + } + return defaultStr; + } + + // ================== + // = 数据格式化处理 = + // ================== + + /** + * 获取格式化后的字符串 + * @param format 待格式化字符串 + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getFormatString(final String format, final Object... args) { + if (format == null) return null; + try { + if (args != null && args.length != 0) { + return String.format(format, args); + } else { + return format; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFormatString"); + } + return null; + } + + // = + + /** + * 获取自动数量格式化后的字符串 ( 可变参数 ) + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getAutoFormatString(final Object... args) { + if (args != null && args.length != 0) { + try { + int length = args.length; + StringBuilder builder = new StringBuilder(); + builder.append("%s"); + if (length > 1) { + for (int i = 1; i < length; i++) { + builder.append(" %s"); + } + } + return String.format(builder.toString(), args); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAutoFormatString"); + } + } + return null; + } + + /** + * 获取自动数量格式化后的字符串 ( 可变参数 ) + * @param args 格式化参数 + * @return 格式化后的字符串 + */ + public static String getAutoFormatString2(final Object... args) { + if (args != null && args.length != 0) { + try { + int length = args.length; + StringBuilder builder = new StringBuilder(); + builder.append("【%s】"); + if (length > 1) { + for (int i = 1; i < length; i++) { + builder.append(" %s"); + } + } + return String.format(builder.toString(), args); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAutoFormatString2"); + } + } + return null; + } + + // = + + /** + * StringBuilder 拼接处理 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final Object... args) { + return appends(new StringBuilder(), null, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param split 追加间隔 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final String split, final Object... args) { + return appends(new StringBuilder(), split, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param builder 拼接 Builder + * @param split 追加间隔 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final StringBuilder builder, final String split, final Object... args) { + return appends(builder, split, false, args); + } + + /** + * StringBuilder 拼接处理 + * @param builder 拼接 Builder + * @param split 追加间隔 + * @param end 结尾是否追加 + * @param args 拼接数据源 + * @return {@link StringBuilder} + */ + public static StringBuilder appends(final StringBuilder builder, final String split, final boolean end, final Object... args) { + if (builder != null && args != null) { + // 获取间隔字符串, 优化循环判断 + String str = isEmpty(split) ? "" : split; + // 循环处理 + for (int i = 0, len = args.length; i < len; i++) { + builder.append(args[i]); // 拼接数据 + // 判断是否结尾 + if (len - i == 1) { + // 判断结尾是否追加 + if (end) builder.append(str); // 间隔追加 + } else { + builder.append(str); // 间隔追加 + } + } + return builder; + } + return builder; + } + + // ============ + // = 转换处理 = + // ============ + + /** + * 字符串进行 GBK 编码 + * @param str 待处理字符串 + * @return GBK 编码后的字符串 + */ + public static String toGBKEncode(final String str) { + return toStrEncode(str, "GBK"); + } + + /** + * 字符串进行 GBK2312 编码 + * @param str 待处理字符串 + * @return GBK2312 编码后的字符串 + */ + public static String toGBK2312Encode(final String str) { + return toStrEncode(str, "GBK-2312"); + } + + /** + * 字符串进行 UTF-8 编码 + * @param str 待处理字符串 + * @return UTF-8 编码后的字符串 + */ + public static String toUTF8Encode(final String str) { + return toStrEncode(str, "UTF-8"); + } + + /** + * 进行字符串编码 + * @param str 待处理字符串 + * @param enc 编码格式 + * @return 指定编码格式编码后的字符串 + */ + public static String toStrEncode(final String str, final String enc) { + if (str == null || enc == null) return null; + try { + return new String(str.getBytes(), enc); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toStrEncode"); + } + return str; + } + + // = + + /** + * 进行 URL 编码, 默认 UTF-8 + * @param str 待处理字符串 + * @return UTF-8 编码格式 URL 编码后的字符串 + */ + public static String toUrlEncode(final String str) { + return toUrlEncode(str, "UTF-8"); + } + + /** + * 进行 URL 编码 + * @param str 待处理字符串 + * @param enc 编码格式 + * @return 指定编码格式 URL 编码后的字符串 + */ + public static String toUrlEncode(final String str, final String enc) { + if (str == null || enc == null) return null; + try { + return URLEncoder.encode(str, enc); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toUrlEncode"); + } + return null; + } + + // = + + /** + * 进行 URL 解码, 默认 UTF-8 + * @param str 待处理字符串 + * @return UTF-8 编码格式 URL 解码后的字符串 + */ + public static String toUrlDecode(final String str) { + return toUrlDecode(str, "UTF-8"); + } + + /** + * 进行 URL 解码 + * @param str 待处理字符串 + * @param enc 解码格式 + * @return 指定编码格式 URL 解码后的字符串 + */ + public static String toUrlDecode(final String str, final String enc) { + if (str == null || enc == null) return null; + try { + return URLDecoder.decode(str, enc); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toUrlDecode"); + } + return null; + } + + // = + + /** + * 将字符串转移为 ASCII 码 + * @param str 待处理字符串 + * @return 字符串转 ASCII 码后的字符串 + */ + public static String toASCII(final String str) { + if (isEmpty(str)) return str; + try { + StringBuilder builder = new StringBuilder(); + byte[] bytes = str.getBytes(); + for (int i = 0, len = bytes.length; i < len; i++) { + builder.append(Integer.toHexString(bytes[i] & 0xff)); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toASCII"); + } + return null; + } + + /** + * 将字符串转移为 Unicode 码 + * @param str 待处理字符串 + * @return 字符串转 Unicode 码后的字符串 + */ + public static String toUnicode(final String str) { + if (isEmpty(str)) return str; + try { + StringBuilder builder = new StringBuilder(); + char[] chars = str.toCharArray(); + for (int i = 0, len = chars.length; i < len; i++) { + builder.append("\\u").append(Integer.toHexString(chars[i])); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toUnicode"); + } + return null; + } + + /** + * 将字符数组转移为 Unicode 码 + * @param chars char[] + * @return char[] 转 Unicode 码后的字符串 + */ + public static String toUnicodeString(final char[] chars) { + if (chars == null) return null; + try { + StringBuilder builder = new StringBuilder(); + for (int i = 0, len = chars.length; i < len; i++) { + builder.append("\\u").append(Integer.toHexString(chars[i])); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toUnicodeString"); + } + return null; + } + + /** + * 转化为半角字符 + * @param str 待处理字符串 + * @return 转换半角字符串 + */ + public static String toDBC(final String str) { + if (isEmpty(str)) return str; + char[] chars = str.toCharArray(); + for (int i = 0, len = chars.length; i < len; i++) { + if (chars[i] == 12288) { + chars[i] = ' '; + } else if (65281 <= chars[i] && chars[i] <= 65374) { + chars[i] = (char) (chars[i] - 65248); + } else { + chars[i] = chars[i]; + } + } + return new String(chars); + } + + /** + * 转化为全角字符 如: a = a, A = A + * @param str 待处理字符串 + * @return 转换全角字符串 + */ + public static String toSBC(final String str) { + if (isEmpty(str)) return str; + char[] chars = str.toCharArray(); + for (int i = 0, len = chars.length; i < len; i++) { + if (chars[i] == ' ') { + chars[i] = (char) 12288; + } else if (33 <= chars[i] && chars[i] <= 126) { + chars[i] = (char) (chars[i] + 65248); + } else { + chars[i] = chars[i]; + } + } + return new String(chars); + } + + // = + + /** + * 检测字符串是否全是中文 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean checkChineseToString(final String str) { + if (isEmpty(str)) return false; + boolean result = true; + char[] chars = str.toCharArray(); + for (int i = 0, len = chars.length; i < len; i++) { + if (!isChinese(chars[i])) { + result = false; + break; + } + } + return result; + } + + /** + * 判定输入汉字 + * @param ch 待校验字符 + * @return {@code true} yes, {@code false} no + */ + public static boolean isChinese(final char ch) { + Character.UnicodeBlock ub = Character.UnicodeBlock.of(ch); + return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS + || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS + || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A + || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION + || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION + || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS; + } + + // ================== + // = 字符串处理方法 = + // ================== + + /** + * 首字母大写 + * @param str 待处理字符串 + * @return 首字母大写字符串 + */ + public static String upperFirstLetter(final String str) { + if (isEmpty(str) || !Character.isLowerCase(str.charAt(0))) return str; + try { + return (char) (str.charAt(0) - 32) + str.substring(1); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "upperFirstLetter"); + return str; + } + } + + /** + * 首字母小写 + * @param str 待处理字符串 + * @return 首字母小写字符串 + */ + public static String lowerFirstLetter(final String str) { + if (isEmpty(str) || !Character.isUpperCase(str.charAt(0))) return str; + try { + return (char) (str.charAt(0) + 32) + str.substring(1); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "lowerFirstLetter"); + return str; + } + } + + /** + * 反转字符串 + * @param str 待处理字符串 + * @return 反转字符串 + */ + public static String reverse(final String str) { + int len = length(str); + if (len <= 1) return str; + int mid = len >> 1; + char[] chars = str.toCharArray(); + char ch; + for (int i = 0; i < mid; ++i) { + ch = chars[i]; + chars[i] = chars[len - i - 1]; + chars[len - i - 1] = ch; + } + return new String(chars); + } + + /** + * 字符串连接, 将参数列表拼接为一个字符串 + * @param args 追加数据 + * @return 拼接后的字符串 + */ + public static String concat(final Object... args) { + return concatSpiltWith("", args); + } + + /** + * 字符串连接, 将参数列表拼接为一个字符串 + * @param startStr 开始字符串 + * @param args 追加数据 + * @return 拼接后的字符串 + */ + public static String concatSpiltWith(final String startStr, final Object... args) { + if (args == null) return null; + StringBuilder builder = new StringBuilder(); + for (int i = 0, len = args.length; i < len; i++) { + if (i != 0) builder.append(startStr); + builder.append(args[i]); + } + return builder.toString(); + } + + /** + * 下划线命名转为驼峰命名 + * @param str 下划线命名格式字符串 + * @return 驼峰命名格式字符串 + */ + public static String underScoreCaseToCamelCase(final String str) { + if (isEmpty(str)) return str; + if (!str.contains("_")) return str; + StringBuilder builder = new StringBuilder(); + char[] chars = str.toCharArray(); + boolean hitUnderScore = false; + builder.append(chars[0]); + for (int i = 1, len = chars.length; i < len; i++) { + char c = chars[i]; + if (c == '_') { + hitUnderScore = true; + } else { + if (hitUnderScore) { + builder.append(Character.toUpperCase(c)); + hitUnderScore = false; + } else { + builder.append(c); + } + } + } + return builder.toString(); + } + + /** + * 驼峰命名法转为下划线命名 + * @param str 驼峰命名格式字符串 + * @return 下划线命名格式字符串 + */ + public static String camelCaseToUnderScoreCase(final String str) { + if (isEmpty(str)) return str; + StringBuilder builder = new StringBuilder(); + char[] chars = str.toCharArray(); + for (int i = 0, len = chars.length; i < len; i++) { + char c = chars[i]; + if (Character.isUpperCase(c)) { + builder.append("_").append(Character.toLowerCase(c)); + } else { + builder.append(c); + } + } + return builder.toString(); + } + + /** + * 字符串数据库字符转义 + * @param str 待处理字符串 + * @return 转义处理后的字符串 + */ + public static String sqliteEscape(final String str) { + if (isEmpty(str)) return str; + String keyWord = str; + // 替换关键字 + keyWord = keyWord.replace("/", "//"); + keyWord = keyWord.replace("'", "''"); + keyWord = keyWord.replace("[", "/["); + keyWord = keyWord.replace("]", "/]"); + keyWord = keyWord.replace("%", "/%"); + keyWord = keyWord.replace("&", "/&"); + keyWord = keyWord.replace("_", "/_"); + keyWord = keyWord.replace("(", "/("); + keyWord = keyWord.replace(")", "/)"); + return keyWord; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/ThrowableUtils.java b/lib/DevJava/src/main/java/dev/utils/common/ThrowableUtils.java new file mode 100644 index 0000000000..827b3f9e9f --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/ThrowableUtils.java @@ -0,0 +1,99 @@ +package dev.utils.common; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; + +import dev.utils.JCLogUtils; + +/** + * detail: 异常处理工具类 + * @author Ttt + */ +public final class ThrowableUtils { + + private ThrowableUtils() { + } + + // 日志 TAG + private static final String TAG = ThrowableUtils.class.getSimpleName(); + + /** + * 获取异常信息 + * @param throwable 异常 + * @return 异常信息字符串 + */ + public static String getThrowable(final Throwable throwable) { + return getThrowable(throwable, "error(null)"); + } + + /** + * 获取异常信息 + * @param throwable 异常 + * @param errorInfo 获取失败返回字符串 + * @return 异常信息字符串 + */ + public static String getThrowable(final Throwable throwable, final String errorInfo) { + if (throwable != null) { + Throwable cause = throwable.getCause(); + if (cause != null) { + return cause.toString(); + } + return throwable.toString(); + } + return errorInfo; + } + + // ============== + // = 异常栈信息 = + // ============== + + /** + * 获取异常栈信息 + * @param throwable 异常 + * @return 异常栈信息字符串 + */ + public static String getThrowableStackTrace(final Throwable throwable) { + return getThrowableStackTrace(throwable, "error(null)"); + } + + /** + * 获取异常栈信息 + * @param throwable 异常 + * @param errorInfo 获取失败返回字符串 + * @return 异常栈信息字符串 + */ + public static String getThrowableStackTrace(final Throwable throwable, final String errorInfo) { + if (throwable != null) { + PrintWriter printWriter = null; + try { + Writer writer = new StringWriter(); + printWriter = new PrintWriter(writer); + throwable.printStackTrace(printWriter); +// // 获取错误栈信息 +// StackTraceElement[] stElement = throwable.getStackTrace(); +// // 标题, 提示属于什么异常 +// printWriter.append(throwable.toString()); +// printWriter.append(NEW_LINE_STR); +// // 遍历错误栈信息, 并且进行换行缩进 +// for (StackTraceElement element : stElement) { +// printWriter.append("\tat "); +// printWriter.append(element.toString()); +// printWriter.append(NEW_LINE_STR); +// } + return writer.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getThrowableStackTrace"); + return e.toString(); + } finally { + if (printWriter != null) { + try { + printWriter.close(); + } catch (Exception e) { + } + } + } + } + return errorInfo; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/ZipUtils.java b/lib/DevJava/src/main/java/dev/utils/common/ZipUtils.java new file mode 100644 index 0000000000..24b774768f --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/ZipUtils.java @@ -0,0 +1,460 @@ +package dev.utils.common; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +import dev.utils.JCLogUtils; + +/** + * detail: 压缩相关工具类 + * @author Ttt + */ +public final class ZipUtils { + + private ZipUtils() { + } + + // 日志 TAG + private static final String TAG = ZipUtils.class.getSimpleName(); + // 缓存大小 + private static final int BUFFER_LEN = 8192; + + /** + * 批量压缩文件 + * @param resFiles 待压缩文件路径集合 + * @param zipFilePath 压缩文件路径 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 + */ + public static boolean zipFiles(final Collection resFiles, final String zipFilePath) throws Exception { + return zipFiles(resFiles, zipFilePath, null); + } + + /** + * 批量压缩文件 + * @param resFilePaths 待压缩文件路径集合 + * @param zipFilePath 压缩文件路径 + * @param comment 压缩文件的注释 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 + */ + public static boolean zipFiles(final Collection resFilePaths, final String zipFilePath, final String comment) throws Exception { + if (resFilePaths == null || zipFilePath == null) return false; + ZipOutputStream zos = null; + try { + zos = new ZipOutputStream(new FileOutputStream(zipFilePath)); + for (String resFile : resFilePaths) { + if (!zipFile(getFileByPath(resFile), "", zos, comment)) return false; + } + return true; + } finally { + if (zos != null) { + zos.finish(); + closeIOQuietly(zos); + } + } + } + + /** + * 批量压缩文件 + * @param resFiles 待压缩文件集合 + * @param zipFile 压缩文件 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 + */ + public static boolean zipFiles(final Collection resFiles, final File zipFile) throws Exception { + return zipFiles(resFiles, zipFile, null); + } + + /** + * 批量压缩文件 + * @param resFiles 待压缩文件集合 + * @param zipFile 压缩文件 + * @param comment 压缩文件的注释 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 + */ + public static boolean zipFiles(final Collection resFiles, final File zipFile, final String comment) throws Exception { + if (resFiles == null || zipFile == null) return false; + ZipOutputStream zos = null; + try { + zos = new ZipOutputStream(new FileOutputStream(zipFile)); + for (File resFile : resFiles) { + if (!zipFile(resFile, "", zos, comment)) return false; + } + return true; + } finally { + if (zos != null) { + zos.finish(); + closeIOQuietly(zos); + } + } + } + + /** + * 压缩文件 + * @param resFilePath 待压缩文件路径 + * @param zipFilePath 压缩文件路径 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 + */ + public static boolean zipFile(final String resFilePath, final String zipFilePath) throws Exception { + return zipFile(getFileByPath(resFilePath), getFileByPath(zipFilePath), null); + } + + /** + * 压缩文件 + * @param resFilePath 待压缩文件路径 + * @param zipFilePath 压缩文件路径 + * @param comment 压缩文件的注释 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 + */ + public static boolean zipFile(final String resFilePath, final String zipFilePath, final String comment) throws Exception { + return zipFile(getFileByPath(resFilePath), getFileByPath(zipFilePath), comment); + } + + /** + * 压缩文件 + * @param resFile 待压缩文件 + * @param zipFile 压缩文件 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 + */ + public static boolean zipFile(final File resFile, final File zipFile) throws Exception { + return zipFile(resFile, zipFile, null); + } + + /** + * 压缩文件 + * @param resFile 待压缩文件 + * @param zipFile 压缩文件 + * @param comment 压缩文件的注释 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 + */ + public static boolean zipFile(final File resFile, final File zipFile, final String comment) throws Exception { + if (resFile == null || zipFile == null) return false; + ZipOutputStream zos = null; + try { + zos = new ZipOutputStream(new FileOutputStream(zipFile)); + return zipFile(resFile, "", zos, comment); + } finally { + if (zos != null) { + closeIOQuietly(zos); + } + } + } + + /** + * 压缩文件 + * @param resFile 待压缩文件 + * @param rootPath 相对于压缩文件的路径 + * @param zos 压缩文件输出流 + * @param comment 压缩文件的注释 + * @return {@code true} 压缩成功, {@code false} 压缩失败 + * @throws Exception 异常时抛出 + */ + private static boolean zipFile(final File resFile, final String rootPath, final ZipOutputStream zos, final String comment) throws Exception { + // 处理后的文件路径 + String filePath = rootPath + (isSpace(rootPath) ? "" : File.separator) + resFile.getName(); + if (resFile.isDirectory()) { + File[] fileList = resFile.listFiles(); + // 如果是空文件夹那么创建它 + if (fileList == null || fileList.length == 0) { + ZipEntry entry = new ZipEntry(filePath + '/'); + entry.setComment(comment); + zos.putNextEntry(entry); + zos.closeEntry(); + } else { + for (File file : fileList) { + // 如果递归返回 false 则返回 false + if (!zipFile(file, filePath, zos, comment)) return false; + } + } + } else { + InputStream is = null; + try { + is = new BufferedInputStream(new FileInputStream(resFile)); + ZipEntry entry = new ZipEntry(filePath); + entry.setComment(comment); + zos.putNextEntry(entry); + byte[] buffer = new byte[BUFFER_LEN]; + int len; + while ((len = is.read(buffer, 0, BUFFER_LEN)) != -1) { + zos.write(buffer, 0, len); + } + zos.closeEntry(); + } finally { + closeIOQuietly(is); + } + } + return true; + } + + /** + * 解压文件 + * @param zipFilePath 待解压文件路径 + * @param destDirPath 目标目录路径 + * @return 文件链表 + * @throws Exception 异常时抛出 + */ + public static List unzipFile(final String zipFilePath, final String destDirPath) throws Exception { + return unzipFileByKeyword(zipFilePath, destDirPath, null); + } + + /** + * 解压文件 + * @param zipFile 待解压文件 + * @param destDir 目标目录 + * @return 文件链表 + * @throws Exception 异常时抛出 + */ + public static List unzipFile(final File zipFile, final File destDir) throws Exception { + return unzipFileByKeyword(zipFile, destDir, null); + } + + /** + * 解压带有关键字的文件 + * @param zipFilePath 待解压文件路径 + * @param destDirPath 目标目录路径 + * @param keyword 关键字 + * @return 带有关键字的文件链表 + * @throws Exception 异常时抛出 + */ + public static List unzipFileByKeyword(final String zipFilePath, final String destDirPath, final String keyword) throws Exception { + return unzipFileByKeyword(getFileByPath(zipFilePath), getFileByPath(destDirPath), keyword); + } + + /** + * 解压带有关键字的文件 + * @param zipFile 待解压文件 + * @param destDir 目标目录 + * @param keyword 关键字 + * @return 带有关键字的文件链表 + * @throws Exception 异常时抛出 + */ + public static List unzipFileByKeyword(final File zipFile, final File destDir, final String keyword) throws Exception { + if (zipFile == null || destDir == null) return null; + List files = new ArrayList<>(); + ZipFile zip = new ZipFile(zipFile); + Enumeration entries = zip.entries(); + if (isSpace(keyword)) { + while (entries.hasMoreElements()) { + ZipEntry entry = ((ZipEntry) entries.nextElement()); + String entryName = entry.getName(); + if (entryName.contains("../")) { + JCLogUtils.dTag(TAG, "entryName: %s is dangerous!", entryName); + continue; + } + if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files; + } + } else { + while (entries.hasMoreElements()) { + ZipEntry entry = ((ZipEntry) entries.nextElement()); + String entryName = entry.getName(); + if (entryName.contains("../")) { + JCLogUtils.dTag(TAG, "entryName: %s is dangerous!", entryName); + continue; + } + if (entryName.contains(keyword)) { + if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files; + } + } + } + return files; + } + + /** + * 解压文件 + * @param destDir 目标目录 + * @param files 解压文件链表 + * @param zf 压缩文件条目 + * @param entry 压缩文件信息 + * @param entryName 文件名 + * @return {@code true} success, {@code false} fail + * @throws Exception 异常时抛出 + */ + private static boolean unzipChildFile(final File destDir, final List files, final ZipFile zf, + final ZipEntry entry, final String entryName) throws Exception { + File file = new File(destDir, entryName); + files.add(file); + if (entry.isDirectory()) { + return createOrExistsDir(file); + } else { + if (!createOrExistsFile(file)) return false; + InputStream is = null; + OutputStream os = null; + try { + is = new BufferedInputStream(zf.getInputStream(entry)); + os = new BufferedOutputStream(new FileOutputStream(file)); + byte[] buffer = new byte[BUFFER_LEN]; + int len; + while ((len = is.read(buffer)) != -1) { + os.write(buffer, 0, len); + } + } finally { + closeIOQuietly(is, os); + } + } + return true; + } + + /** + * 获取压缩文件中的文件路径链表 + * @param zipFilePath 压缩文件路径 + * @return 压缩文件中的文件路径链表 + * @throws Exception 异常时抛出 + */ + public static List getFilesPath(final String zipFilePath) throws Exception { + return getFilesPath(getFileByPath(zipFilePath)); + } + + /** + * 获取压缩文件中的文件路径链表 + * @param zipFile 压缩文件 + * @return 压缩文件中的文件路径链表 + * @throws Exception 异常时抛出 + */ + public static List getFilesPath(final File zipFile) throws Exception { + if (zipFile == null) return null; + List paths = new ArrayList<>(); + Enumeration entries = new ZipFile(zipFile).entries(); + while (entries.hasMoreElements()) { + String entryName = ((ZipEntry) entries.nextElement()).getName(); + if (entryName.contains("../")) { + JCLogUtils.dTag(TAG, "entryName: %s is dangerous!", entryName); + paths.add(entryName); + } else { + paths.add(entryName); + } + } + return paths; + } + + /** + * 获取压缩文件中的注释链表 + * @param zipFilePath 压缩文件路径 + * @return 压缩文件中的注释链表 + * @throws Exception 异常时抛出 + */ + public static List getComments(final String zipFilePath) throws Exception { + return getComments(getFileByPath(zipFilePath)); + } + + /** + * 获取压缩文件中的注释链表 + * @param zipFile 压缩文件 + * @return 压缩文件中的注释链表 + * @throws Exception 异常时抛出 + */ + public static List getComments(final File zipFile) throws Exception { + if (zipFile == null) return null; + List comments = new ArrayList<>(); + Enumeration entries = new ZipFile(zipFile).entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = ((ZipEntry) entries.nextElement()); + comments.add(entry.getComment()); + } + return comments; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ============== + // = CloseUtils = + // ============== + + /** + * 安静关闭 IO + * @param closeables Closeable[] + */ + private static void closeIOQuietly(final Closeable... closeables) { + if (closeables == null) return; + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception ignore) { + } + } + } + } + + // ============= + // = FileUtils = + // ============= + + /** + * 获取文件 + * @param filePath 文件路径 + * @return 文件 {@link File} + */ + private static File getFileByPath(final String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 判断目录是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + private static boolean createOrExistsDir(final File file) { + // 如果存在, 是目录则返回 true, 是文件则返回 false, 不存在则返回是否创建成功 + return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); + } + + /** + * 判断文件是否存在, 不存在则判断是否创建成功 + * @param file 文件 + * @return {@code true} 存在或创建成功, {@code false} 不存在或创建失败 + */ + private static boolean createOrExistsFile(final File file) { + if (file == null) return false; + // 如果存在, 是文件则返回 true, 是目录则返回 false + if (file.exists()) return file.isFile(); + // 判断文件是否存在, 不存在则直接返回 + if (!createOrExistsDir(file.getParentFile())) return false; + try { + // 存在, 则返回新的路径 + return file.createNewFile(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "createOrExistsFile"); + return false; + } + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/assist/Averager.java b/lib/DevJava/src/main/java/dev/utils/common/assist/Averager.java new file mode 100644 index 0000000000..afe5fa47dd --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/assist/Averager.java @@ -0,0 +1,60 @@ +package dev.utils.common.assist; + +import java.util.ArrayList; +import java.util.List; + +/** + * detail: 均值计算 ( 用以统计平均数 ) 辅助类 + * @author Ttt + */ +public class Averager { + + private List mNumLists = new ArrayList<>(); + + /** + * 添加一个数字 + * @param number Number + */ + public synchronized void add(final Number number) { + mNumLists.add(number); + } + + /** + * 清除全部 + */ + public void clear() { + mNumLists.clear(); + } + + /** + * 获取参与均值计算的数字个数 + * @return 参与均值计算的数字个数 + */ + public Number size() { + return mNumLists.size(); + } + + /** + * 获取平均数 + * @return 全部数字平均数 + */ + public Number getAverage() { + if (mNumLists.size() == 0) { + return 0; + } else { + Float sum = 0f; + for (int i = 0, len = mNumLists.size(); i < len; i++) { + sum = sum.floatValue() + mNumLists.get(i).floatValue(); + } + return sum / mNumLists.size(); + } + } + + /** + * 输出参与均值计算的数字 + * @return 参与均值计算的数字 + */ + public String print() { + return "printList(" + size() + "): " + mNumLists; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/assist/TimeAverager.java b/lib/DevJava/src/main/java/dev/utils/common/assist/TimeAverager.java new file mode 100644 index 0000000000..dcbf78fa41 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/assist/TimeAverager.java @@ -0,0 +1,64 @@ +package dev.utils.common.assist; + +/** + * detail: 时间均值计算辅助类 + * @author Ttt + */ +public class TimeAverager { + + // 计时器 + private TimeCounter mTimeCounter = new TimeCounter(); + // 均值器 + private Averager mAverager = new Averager(); + + /** + * 开始计时 ( 毫秒 ) + * @return 开始时间 ( 毫秒 ) + */ + public long start() { + return mTimeCounter.start(); + } + + /** + * 结束计时 ( 毫秒 ) + * @return 结束时间 ( 毫秒 ) + */ + public long end() { + long time = mTimeCounter.duration(); + mAverager.add(time); + return time; + } + + /** + * 结束计时, 并重新启动新的计时 + * @return 距离上次计时的时间差 ( 毫秒 ) + */ + public long endAndRestart() { + long time = mTimeCounter.durationRestart(); + mAverager.add(time); + return time; + } + + /** + * 求全部计时均值 + * @return 全部计时时间均值 + */ + public Number average() { + return mAverager.getAverage(); + } + + /** + * 输出全部时间值 + * @return 计时信息 + */ + public String print() { + return mAverager.print(); + } + + /** + * 清除计时数据 + */ + public void clear() { + mAverager.clear(); + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/assist/TimeCounter.java b/lib/DevJava/src/main/java/dev/utils/common/assist/TimeCounter.java new file mode 100644 index 0000000000..e62605cf39 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/assist/TimeCounter.java @@ -0,0 +1,59 @@ +package dev.utils.common.assist; + +/** + * detail: 时间计时辅助类 + * @author Ttt + */ +public class TimeCounter { + + // 开始时间 + private long mStart; + + public TimeCounter() { + this(true); + } + + /** + * 构造函数 + * @param isStart 是否开始计时 + */ + public TimeCounter(final boolean isStart) { + if (isStart) start(); + } + + /** + * 开始计时 ( 毫秒 ) + * @return 开始时间 ( 毫秒 ) + */ + public long start() { + mStart = System.currentTimeMillis(); + return mStart; + } + + /** + * 获取持续的时间并重新启动 ( 毫秒 ) + * @return 距离上次开始时间的时间差 ( 毫秒 ) + */ + public long durationRestart() { + long now = System.currentTimeMillis(); + long diff = now - mStart; + mStart = now; + return diff; + } + + /** + * 获取持续的时间 ( 毫秒 ) + * @return 距离开始时间的时间差 ( 毫秒 ) + */ + public long duration() { + return System.currentTimeMillis() - mStart; + } + + /** + * 获取开始时间 ( 毫秒 ) + * @return 开始时间 ( 毫秒 ) + */ + public long getStartTime() { + return mStart; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/assist/TimeKeeper.java b/lib/DevJava/src/main/java/dev/utils/common/assist/TimeKeeper.java new file mode 100644 index 0000000000..d57836c190 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/assist/TimeKeeper.java @@ -0,0 +1,65 @@ +package dev.utils.common.assist; + +/** + * detail: 堵塞时间辅助类 + * @author Ttt + */ +public class TimeKeeper { + + /** + * 设置等待一段时间后, 通知方法 ( 异步 ) + * @param keepTimeMillis 堵塞时间 ( 毫秒 ) + * @param endCallback 结束回调通知 + */ + public void waitForEndAsyn(final long keepTimeMillis, final OnEndCallback endCallback) { + if (keepTimeMillis > 0L) { + new Thread(new Runnable() { + @Override + public void run() { + waitForEnd(keepTimeMillis, endCallback); + } + }).start(); + } + } + + /** + * 设置等待一段时间后, 通知方法 ( 同步 ) + * @param keepTimeMillis 堵塞时间 ( 毫秒 ) + * @param endCallback 结束回调通知 + */ + public void waitForEnd(final long keepTimeMillis, final OnEndCallback endCallback) { + if (keepTimeMillis > 0L) { + // 开始堵塞时间 + long startTime = System.currentTimeMillis(); + try { + // 进行堵塞 + Thread.sleep(keepTimeMillis); + // 触发回调 + if (endCallback != null) { + endCallback.onEnd(keepTimeMillis, startTime, System.currentTimeMillis(), false); + } + } catch (Exception e) { + // 触发回调 + if (endCallback != null) { + endCallback.onEnd(keepTimeMillis, startTime, System.currentTimeMillis(), true); + } + } + } + } + + /** + * detail: 结束通知回调 + * @author Ttt + */ + public interface OnEndCallback { + + /** + * 结束触发通知方法 + * @param keepTimeMillis 堵塞时间 ( 毫秒 ) + * @param startTimeMillis 开始堵塞时间 ( 毫秒 ) + * @param endTimeMillis 结束时间 ( 毫秒 ) + * @param isError 是否发生异常 + */ + void onEnd(long keepTimeMillis, long startTimeMillis, long endTimeMillis, boolean isError); + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java b/lib/DevJava/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java new file mode 100644 index 0000000000..a474a9b7a1 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java @@ -0,0 +1,413 @@ +package dev.utils.common.assist.search; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; + +import dev.utils.JCLogUtils; + +/** + * detail: 文件广度优先搜索算法 ( 多线程 + 队列, 搜索某个目录下的全部文件 ) + * @author Ttt + */ +public final class FileBreadthFirstSearchUtils { + + // 日志 TAG + private static final String TAG = FileBreadthFirstSearchUtils.class.getSimpleName(); + + // ============ + // = 构造函数 = + // ============ + + /** + * 构造函数 + */ + public FileBreadthFirstSearchUtils() { + } + + /** + * 构造函数 + * @param searchHandler 搜索处理接口 + */ + public FileBreadthFirstSearchUtils(final SearchHandler searchHandler) { + this.mSearchHandler = searchHandler; + } + + /** + * detail: 文件信息 Item + * @author Ttt + */ + public final class FileItem { + + public FileItem(final File file) { + this.file = file; + } + + // 文件 + public File file; + + // 子文件夹目录 + public Map mapChilds; + + /** + * 保存子文件信息 + * @param file 文件 + * @return 文件信息 {@link FileItem} + */ + private synchronized FileItem put(final File file) { + if (mapChilds == null) { + mapChilds = new HashMap<>(); + } + if (file != null) { + FileItem fileItem = new FileItem(file); + mapChilds.put(file.getAbsolutePath(), fileItem); + return fileItem; + } + return null; + } + } + + /** + * detail: 文件队列 + * @author Ttt + */ + private class FileQueue { + + public FileQueue(File file, FileItem fileItem) { + this.file = file; + this.fileItem = fileItem; + } + + // 当前准备处理文件夹 + private File file; + + // 上一级目录对象 + private FileItem fileItem; + } + + /** + * detail: 搜索处理接口 + * @author Ttt + */ + public interface SearchHandler { + + /** + * 判断是否处理该文件 + * @param file 文件 + * @return {@code true} 处理该文件, {@code false} 跳过该文件不处理 + */ + boolean isHandlerFile(File file); + + /** + * 是否添加到集合 + * @param file 文件 + * @return {@code true} 添加, {@code false} 不添加 + */ + boolean isAddToList(File file); + + /** + * 搜索结束监听 + * @param rootFileItem 根文件信息 {@link FileItem} + * @param startTime 开始扫描时间 + * @param endTime 扫描结束时间 + */ + void OnEndListener(FileItem rootFileItem, long startTime, long endTime); + } + + // 搜索处理接口 + private SearchHandler mSearchHandler; + + // 内部实现接口 + private SearchHandler mInsideHandler = new SearchHandler() { + @Override + public boolean isHandlerFile(File file) { + if (mSearchHandler != null) { + return mSearchHandler.isHandlerFile(file); + } + return true; + } + + @Override + public boolean isAddToList(File file) { + if (mSearchHandler != null) { + return mSearchHandler.isAddToList(file); + } + return true; + } + + @Override + public void OnEndListener(FileItem rootFileItem, long startTime, long endTime) { + // 表示非搜索中 + mIsRunning = false; + // 触发回调 + if (mSearchHandler != null) { + mSearchHandler.OnEndListener(rootFileItem, startTime, endTime); + } + } + }; + + /** + * 设置搜索处理接口 + * @param searchHandler 搜索处理接口 + * @return {@link FileBreadthFirstSearchUtils} + */ + public FileBreadthFirstSearchUtils setSearchHandler(final SearchHandler searchHandler) { + this.mSearchHandler = searchHandler; + return this; + } + + /** + * 获取任务队列同时进行数量 + * @return 队列数量 + */ + public int getQueueSameTimeNumber() { + return mQueueSameTimeNumber; + } + + /** + * 任务队列同时进行数量 + * @param queueSameTimeNumber 同一时间线程队列数量 + * @return {@link FileBreadthFirstSearchUtils} + */ + public synchronized FileBreadthFirstSearchUtils setQueueSameTimeNumber(final int queueSameTimeNumber) { + if (mIsRunning) { + return this; + } + this.mQueueSameTimeNumber = queueSameTimeNumber; + return this; + } + + /** + * 是否搜索中 + * @return {@code true} 搜索 / 运行中, {@code false} 非搜索 / 运行中 + */ + public boolean isRunning() { + return mIsRunning; + } + + /** + * 停止搜索 + */ + public void stop() { + mIsStop = true; + } + + /** + * 是否停止搜索 + * @return {@code true} 已停止搜索, {@code false} 搜索中 + */ + public boolean isStop() { + return mIsStop; + } + + /** + * 获取开始搜索时间 ( 毫秒 ) + * @return 开始搜索时间 ( 毫秒 ) + */ + public long getStartTime() { + return mStartTime; + } + + /** + * 获取结束搜索时间 ( 毫秒 ) + * @return 结束搜索时间 ( 毫秒 ) + */ + public long getEndTime() { + return mEndTime; + } + + /** + * 获取延迟校验时间 ( 毫秒 ) + * @return 延迟线程校验时间 ( 毫秒 ) + */ + public long getDelayTime() { + return mDelayTime; + } + + /** + * 设置延迟校验时间 ( 毫秒 ) + * @param delayTimeMillis 延迟校验时间 ( 毫秒 ) + * @return {@link FileBreadthFirstSearchUtils} + */ + public FileBreadthFirstSearchUtils setDelayTime(final long delayTimeMillis) { + this.mDelayTime = delayTimeMillis; + return this; + } + + // = + + // 根目录对象 + private FileItem mRootFileItem; + // 判断是否运行中 + private boolean mIsRunning = false; + // 是否停止搜索 + private boolean mIsStop = false; + // 开始搜索时间 + private long mStartTime = 0L; + // 结束搜索时间 + private long mEndTime = 0L; + // 延迟时间 + private long mDelayTime = 50L; + // 任务队列同时进行数量 + private int mQueueSameTimeNumber = 5; + // 线程池 + private ExecutorService mExecutor = Executors.newCachedThreadPool(); + // 任务队列 + private LinkedBlockingQueue mTaskQueue = new LinkedBlockingQueue<>(); + + /** + * 搜索目录 + * @param path 根目录路径 + */ + public synchronized void query(final String path) { + if (mIsRunning) { + return; + } else if (path == null || path.trim().length() == 0) { + // 触发结束回调 + mInsideHandler.OnEndListener(null, -1, -1); + return; + } + // 表示运行中 + mIsRunning = true; + mIsStop = false; + // 设置开始搜索时间 + mStartTime = System.currentTimeMillis(); + try { + // 获取根目录 File + File file = new File(path); + // 初始化根目录 + mRootFileItem = new FileItem(file); + // 判断是否文件 + if (file.isFile()) { + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(mRootFileItem, mStartTime, mEndTime); + return; + } + // 获取文件夹全部子文件 + String[] fileArys = file.list(); + // 获取文件总数 + if (fileArys != null && fileArys.length != 0) { + new Thread(new Runnable() { + @Override + public void run() { + // 查询文件 + queryFile(mRootFileItem.file, mRootFileItem); + // 循环队列 + whileQueue(); + } + }).start(); + } else { + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(mRootFileItem, mStartTime, mEndTime); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "query"); + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(mRootFileItem, mStartTime, mEndTime); + } + } + + /** + * 搜索文件 + * @param file 文件 + * @param fileItem 所在文件夹信息对象 ( 上一级目录 ) + */ + private void queryFile(final File file, final FileItem fileItem) { + try { + if (mIsStop) { + return; + } + if (file != null && file.exists()) { + // 判断是否处理 + if (mInsideHandler.isHandlerFile(file)) { + // 如果属于文件夹 + if (file.isDirectory()) { + // 获取文件夹全部子文件 + File[] files = file.listFiles(); + if (files == null) { + return; + } + // 循环处理 + for (File f : files) { + // 属于文件夹 + if (f.isDirectory()) { + if (mIsStop) { + return; + } + FileItem subFileItem = fileItem.put(f); + // 添加任务 + mTaskQueue.offer(new FileQueue(f, subFileItem)); + } else { // 属于文件 + if (!mIsStop && mInsideHandler.isAddToList(f)) { + // 属于文件则直接保存 + fileItem.put(f); + } + } + } + } else { // 属于文件 + if (!mIsStop && mInsideHandler.isAddToList(file)) { + // 属于文件则直接保存 + fileItem.put(file); + } + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "queryFile"); + } + } + + // ============== + // = 线程池处理 = + // ============== + + /** + * 循环队列 + */ + private void whileQueue() { + // 判断是否为 null + boolean isEmpty = mTaskQueue.isEmpty(); + // 循环则不处理 + while (!isEmpty) { + if (mIsStop) break; + // 获取线程活动数量 + int threadCount = ((ThreadPoolExecutor) mExecutor).getActiveCount(); + // 判断是否超过 + if (threadCount > mQueueSameTimeNumber) { + continue; + } + // 获取文件对象 + final FileQueue fileQueue = mTaskQueue.poll(); + // 判断是否为 null + if (fileQueue != null) { + mExecutor.execute(new Runnable() { + @Override + public void run() { + queryFile(fileQueue.file, fileQueue.fileItem); + } + }); + } + + // 判断是否存在队列数据 + isEmpty = (mTaskQueue.isEmpty() && threadCount == 0); + if (isEmpty) { // 如果不存在, 防止搜索过快, 延迟再次判断 + if (mIsStop) break; + try { + Thread.sleep(mDelayTime); + } catch (Exception e) { + } + isEmpty = (mTaskQueue.isEmpty() && threadCount == 0); + } + } + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(mRootFileItem, mStartTime, mEndTime); + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java b/lib/DevJava/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java new file mode 100644 index 0000000000..4a748045d1 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java @@ -0,0 +1,288 @@ +package dev.utils.common.assist.search; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 文件深度优先搜索算法 ( 递归搜索某个目录下的全部文件 ) + * @author Ttt + */ +public final class FileDepthFirstSearchUtils { + + // 日志 TAG + private static final String TAG = FileDepthFirstSearchUtils.class.getSimpleName(); + + // ============ + // = 构造函数 = + // ============ + + /** + * 构造函数 + */ + public FileDepthFirstSearchUtils() { + } + + /** + * 构造函数 + * @param searchHandler 搜索处理接口 + */ + public FileDepthFirstSearchUtils(final SearchHandler searchHandler) { + this.mSearchHandler = searchHandler; + } + + /** + * detail: 文件信息 Item + * @author Ttt + */ + public final class FileItem { + + public FileItem(final File file) { + this.file = file; + } + + // 文件 + public File file; + + // 子文件夹目录 + public List listChilds = null; + } + + /** + * detail: 搜索处理接口 + * @author Ttt + */ + public interface SearchHandler { + + /** + * 判断是否处理该文件 + * @param file 文件 + * @return {@code true} 处理该文件, {@code false} 跳过该文件不处理 + */ + boolean isHandlerFile(File file); + + /** + * 是否添加到集合 + * @param file 文件 + * @return {@code true} 添加, {@code false} 不添加 + */ + boolean isAddToList(File file); + + /** + * 搜索结束监听 + * @param lists 根目录的子文件目录集合 + * @param startTime 开始扫描时间 + * @param endTime 扫描结束时间 + */ + void OnEndListener(List lists, long startTime, long endTime); + } + + // 搜索处理接口 + private SearchHandler mSearchHandler; + + // 内部实现接口 + private SearchHandler mInsideHandler = new SearchHandler() { + @Override + public boolean isHandlerFile(File file) { + if (mSearchHandler != null) { + return mSearchHandler.isHandlerFile(file); + } + return true; + } + + @Override + public boolean isAddToList(File file) { + if (mSearchHandler != null) { + return mSearchHandler.isAddToList(file); + } + return true; + } + + @Override + public void OnEndListener(List lists, long startTime, long endTime) { + // 表示非搜索中 + mIsRunning = false; + // 触发回调 + if (mSearchHandler != null) { + mSearchHandler.OnEndListener(lists, startTime, endTime); + } + } + }; + + /** + * 设置搜索处理接口 + * @param searchHandler 搜索处理接口 + * @return {@link FileDepthFirstSearchUtils} + */ + public FileDepthFirstSearchUtils setSearchHandler(final SearchHandler searchHandler) { + this.mSearchHandler = searchHandler; + return this; + } + + /** + * 是否搜索中 + * @return {@code true} 搜索 / 运行中, {@code false} 非搜索 / 运行中 + */ + public boolean isRunning() { + return mIsRunning; + } + + /** + * 停止搜索 + */ + public void stop() { + mIsStop = true; + } + + /** + * 是否停止搜索 + * @return {@code true} 已停止搜索, {@code false} 搜索中 + */ + public boolean isStop() { + return mIsStop; + } + + /** + * 获取开始搜索时间 ( 毫秒 ) + * @return 开始搜索时间 ( 毫秒 ) + */ + public long getStartTime() { + return mStartTime; + } + + /** + * 获取结束搜索时间 ( 毫秒 ) + * @return 结束搜索时间 ( 毫秒 ) + */ + public long getEndTime() { + return mEndTime; + } + + // = + + // 判断是否运行中 + private boolean mIsRunning = false; + // 是否停止搜索 + private boolean mIsStop = false; + // 开始搜索时间 + private long mStartTime = 0L; + // 结束搜索时间 + private long mEndTime = 0L; + + /** + * 搜索目录 + * @param path 根目录路径 + * @param isRelation 是否关联到 Child List + */ + public synchronized void query(final String path, final boolean isRelation) { + if (mIsRunning) { + return; + } else if (path == null || path.trim().length() == 0) { + // 触发结束回调 + mInsideHandler.OnEndListener(null, -1, -1); + return; + } + // 表示运行中 + mIsRunning = true; + mIsStop = false; + // 设置开始搜索时间 + mStartTime = System.currentTimeMillis(); + try { + // 获取根目录 File + final File file = new File(path); + // 判断是否文件 + if (file.isFile()) { + List lists = new ArrayList<>(); + lists.add(new FileItem(file)); + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(lists, mStartTime, mEndTime); + return; + } + // 获取文件夹全部子文件 + String[] fileArys = file.list(); + // 获取文件总数 + if (fileArys != null && fileArys.length != 0) { + new Thread(new Runnable() { + @Override + public void run() { + List lists = new ArrayList<>(); + // 查询文件 + queryFile(file, lists, isRelation); + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(lists, mStartTime, mEndTime); + } + }).start(); + } else { + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(null, mStartTime, mEndTime); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "query"); + // 触发结束回调 + mEndTime = System.currentTimeMillis(); + mInsideHandler.OnEndListener(null, mStartTime, mEndTime); + } + } + + /** + * 搜索文件 + * @param file 文件 + * @param lists 保存数据源 + * @param isRelation 是否关联到 Child List + */ + private void queryFile(final File file, final List lists, final boolean isRelation) { + try { + if (mIsStop) { + return; + } + if (file != null && file.exists()) { + // 判断是否处理 + if (mInsideHandler.isHandlerFile(file)) { + // 如果属于文件夹 + if (file.isDirectory()) { + // 获取文件夹全部子文件 + File[] files = file.listFiles(); + if (files == null) { + return; + } + // 循环处理 + for (File f : files) { + if (isRelation) { + if (f.isDirectory()) { + List childs = new ArrayList<>(); + // 查找文件 + queryFile(f, childs, isRelation); + // 保存数据 + FileItem fileItem = new FileItem(f); + fileItem.listChilds = childs; + lists.add(fileItem); + } else { + // 属于文件 + if (mInsideHandler.isAddToList(f)) { + // 属于文件则直接保存 + lists.add(new FileItem(f)); + } + } + } else { + // 查找文件 + queryFile(f, lists, isRelation); + } + } + } else { // 属于文件 + if (mInsideHandler.isAddToList(file)) { + // 属于文件则直接保存 + lists.add(new FileItem(file)); + } + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "queryFile"); + } + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/cipher/Base64.java b/lib/DevJava/src/main/java/dev/utils/common/cipher/Base64.java new file mode 100644 index 0000000000..650618bd0a --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/cipher/Base64.java @@ -0,0 +1,735 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * 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 dev.utils.common.cipher; + +import java.io.UnsupportedEncodingException; + +/** + * detail: Base64 工具类 + * @author Android + *

+ *     Utilities for encoding and decoding the Base64 representation of
+ *     binary data.  See RFCs 2045 and 3548.
+ * 
+ */ +public final class Base64 { + /** + * Default values for encoder/decoder flags. + */ + public static final int DEFAULT = 0; + + /** + * Encoder flag bit to omit the padding '=' characters at the end + * of the output (if any). + */ + public static final int NO_PADDING = 1; + + /** + * Encoder flag bit to omit all line terminators (i.e., the output + * will be on one long line). + */ + public static final int NO_WRAP = 2; + + /** + * Encoder flag bit to indicate lines should be terminated with a + * CRLF pair instead of just an LF. Has no effect if {@code + * NO_WRAP} is specified as well. + */ + public static final int CRLF = 4; + + /** + * Encoder/decoder flag bit to indicate using the "URL and + * filename safe" variant of Base64 (see RFC 3548 section 4) where + * {@code -} and {@code _} are used in place of {@code +} and + * {@code /}. + */ + public static final int URL_SAFE = 8; + + /** + * Flag to pass to {@link Base64OutputStream} to indicate that it + * should not close the output stream it is wrapping when it + * itself is closed. + */ + public static final int NO_CLOSE = 16; + + // -------------------------------------------------------- + // shared code + // -------------------------------------------------------- + + /* package */ static abstract class Coder { + public byte[] output; + public int op; + + /** + * Encode/decode another block of input data. this.output is + * provided by the caller, and must be big enough to hold all + * the coded data. On exit, this.opwill be set to the length + * of the coded data. + * @param finish true if this is the final call to process for + * this object. Will finalize the coder state and + * include any final bytes in the output. + * @return true if the input so far is good; false if some + * error has been detected in the input stream.. + */ + public abstract boolean process(byte[] input, int offset, int len, boolean finish); + + /** + * @return the maximum number of bytes a call to process() + * could produce for the given number of input bytes. This may + * be an overestimate. + */ + public abstract int maxOutputSize(int len); + } + + // -------------------------------------------------------- + // decoding + // -------------------------------------------------------- + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + *

+ * The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * @param str the input String to decode, which is converted to + * bytes using the default charset + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(String str, int flags) { + return decode(str.getBytes(), flags); + } + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + *

+ * The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * @param input the input array to decode + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(byte[] input, int flags) { + return decode(input, 0, input.length, flags); + } + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + *

+ * The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * @param input the data to decode + * @param offset the position within the input array at which to start + * @param len the number of bytes of input to decode + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(byte[] input, int offset, int len, int flags) { + // Allocate space for the most data the input could represent. + // (It could contain less if it contains whitespace, etc.) + Decoder decoder = new Decoder(flags, new byte[len * 3 / 4]); + + if (!decoder.process(input, offset, len, true)) { + throw new IllegalArgumentException("bad base-64"); + } + + // Maybe we got lucky and allocated exactly enough output space. + if (decoder.op == decoder.output.length) { + return decoder.output; + } + + // Need to shorten the array, so allocate a new one of the + // right size and copy. + byte[] temp = new byte[decoder.op]; + System.arraycopy(decoder.output, 0, temp, 0, decoder.op); + return temp; + } + + /* package */ static class Decoder extends Coder { + /** + * Lookup table for turning bytes into their position in the + * Base64 alphabet. + */ + private static final int[] DECODE = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /** + * Decode lookup table for the "web safe" variant (RFC 3548 + * sec. 4) where - and _ replace + and /. + */ + private static final int[] DECODE_WEBSAFE = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /** + * Non-data values in the DECODE arrays. + */ + private static final int SKIP = -1; + private static final int EQUALS = -2; + + /** + * States 0-3 are reading through the next input tuple. + * State 4 is having read one '=' and expecting exactly + * one more. + * State 5 is expecting no more data or padding characters + * in the input. + * State 6 is the error state; an error has been detected + * in the input and no future input can "fix" it. + */ + private int state; // state number (0 to 6) + private int value; + + final private int[] alphabet; + + public Decoder(int flags, byte[] output) { + this.output = output; + + alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE; + state = 0; + value = 0; + } + + /** + * @return an overestimate for the number of bytes {@code + * len} bytes could decode to. + */ + public int maxOutputSize(int len) { + return len * 3 / 4 + 10; + } + + /** + * Decode another block of input data. + * @return true if the state machine is still healthy. false if + * bad base-64 data has been detected in the input stream. + */ + public boolean process(byte[] input, int offset, int len, boolean finish) { + if (this.state == 6) return false; + + int p = offset; + len += offset; + + // Using local variables makes the decoder about 12% + // faster than if we manipulate the member variables in + // the loop. (Even alphabet makes a measurable + // difference, which is somewhat surprising to me since + // the member variable is final.) + int state = this.state; + int value = this.value; + int op = 0; + final byte[] output = this.output; + final int[] alphabet = this.alphabet; + + while (p < len) { + // Try the fast path: we're starting a new tuple and the + // next four bytes of the input stream are all data + // bytes. This corresponds to going through states + // 0-1-2-3-0. We expect to use this method for most of + // the data. + // + // If any of the next four bytes of input are non-data + // (whitespace, etc.), value will end up negative. (All + // the non-data values in decode are small negative + // numbers, so shifting any of them up and or'ing them + // together will result in a value with its top bit set.) + // + // You can remove this whole block and the output should + // be the same, just slower. + if (state == 0) { + while (p + 4 <= len && + (value = ((alphabet[input[p] & 0xff] << 18) | + (alphabet[input[p + 1] & 0xff] << 12) | + (alphabet[input[p + 2] & 0xff] << 6) | + (alphabet[input[p + 3] & 0xff]))) >= 0) { + output[op + 2] = (byte) value; + output[op + 1] = (byte) (value >> 8); + output[op] = (byte) (value >> 16); + op += 3; + p += 4; + } + if (p >= len) break; + } + + // The fast path isn't available either we've read a + // partial tuple, or the next four input bytes aren't all + // data, or whatever. Fall back to the slower state + // machine implementation. + + int d = alphabet[input[p++] & 0xff]; + + switch (state) { + case 0: + if (d >= 0) { + value = d; + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 1: + if (d >= 0) { + value = (value << 6) | d; + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 2: + if (d >= 0) { + value = (value << 6) | d; + ++state; + } else if (d == EQUALS) { + // Emit the last (partial) output tuple; + // expect exactly one more padding character. + output[op++] = (byte) (value >> 4); + state = 4; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 3: + if (d >= 0) { + // Emit the output triple and return to state 0. + value = (value << 6) | d; + output[op + 2] = (byte) value; + output[op + 1] = (byte) (value >> 8); + output[op] = (byte) (value >> 16); + op += 3; + state = 0; + } else if (d == EQUALS) { + // Emit the last (partial) output tuple; + // expect no further data or padding characters. + output[op + 1] = (byte) (value >> 2); + output[op] = (byte) (value >> 10); + op += 2; + state = 5; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 4: + if (d == EQUALS) { + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 5: + if (d != SKIP) { + this.state = 6; + return false; + } + break; + } + } + + if (!finish) { + // We're out of input, but a future call could provide + // more. + this.state = state; + this.value = value; + this.op = op; + return true; + } + + // Done reading input. Now figure out where we are left in + // the state machine and finish up. + + switch (state) { + case 0: + // Output length is a multiple of three. Fine. + break; + case 1: + // Read one extra input byte, which isn't enough to + // make another output byte. Illegal. + this.state = 6; + return false; + case 2: + // Read two extra input bytes, enough to emit 1 more + // output byte. Fine. + output[op++] = (byte) (value >> 4); + break; + case 3: + // Read three extra input bytes, enough to emit 2 more + // output bytes. Fine. + output[op++] = (byte) (value >> 10); + output[op++] = (byte) (value >> 2); + break; + case 4: + // Read one padding '=' when we expected 2. Illegal. + this.state = 6; + return false; + case 5: + // Read all the padding '='s we expected and no more. + // Fine. + break; + } + + this.state = state; + this.op = op; + return true; + } + } + + // -------------------------------------------------------- + // encoding + // -------------------------------------------------------- + + /** + * Base64-encode the given data and return a newly allocated + * String with the result. + * @param input the data to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static String encodeToString(byte[] input, int flags) { + try { + return new String(encode(input, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } + } + + /** + * Base64-encode the given data and return a newly allocated + * String with the result. + * @param input the data to encode + * @param offset the position within the input array at which to + * start + * @param len the number of bytes of input to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static String encodeToString(byte[] input, int offset, int len, int flags) { + try { + return new String(encode(input, offset, len, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } + } + + /** + * Base64-encode the given data and return a newly allocated + * byte[] with the result. + * @param input the data to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static byte[] encode(byte[] input, int flags) { + return encode(input, 0, input.length, flags); + } + + /** + * Base64-encode the given data and return a newly allocated + * byte[] with the result. + * @param input the data to encode + * @param offset the position within the input array at which to + * start + * @param len the number of bytes of input to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static byte[] encode(byte[] input, int offset, int len, int flags) { + Encoder encoder = new Encoder(flags, null); + + // Compute the exact length of the array we will produce. + int output_len = len / 3 * 4; + + // Account for the tail of the data and the padding bytes, if any. + if (encoder.do_padding) { + if (len % 3 > 0) { + output_len += 4; + } + } else { + switch (len % 3) { + case 0: + break; + case 1: + output_len += 2; + break; + case 2: + output_len += 3; + break; + } + } + + // Account for the newlines, if any. + if (encoder.do_newline && len > 0) { + output_len += (((len - 1) / (3 * Encoder.LINE_GROUPS)) + 1) * + (encoder.do_cr ? 2 : 1); + } + + encoder.output = new byte[output_len]; + encoder.process(input, offset, len, true); + + assert encoder.op == output_len; + + return encoder.output; + } + + /* package */ static class Encoder extends Coder { + /** + * Emit a new line every this many output tuples. Corresponds to + * a 76-character line length (the maximum allowable according to + * RFC 2045). + */ + public static final int LINE_GROUPS = 19; + + /** + * Lookup table for turning Base64 alphabet positions (6 bits) + * into output bytes. + */ + private static final byte[] ENCODE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', + }; + + /** + * Lookup table for turning Base64 alphabet positions (6 bits) + * into output bytes. + */ + private static final byte[] ENCODE_WEBSAFE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', + }; + + final private byte[] tail; + /* package */ int tailLen; + private int count; + + final public boolean do_padding; + final public boolean do_newline; + final public boolean do_cr; + final private byte[] alphabet; + + public Encoder(int flags, byte[] output) { + this.output = output; + + do_padding = (flags & NO_PADDING) == 0; + do_newline = (flags & NO_WRAP) == 0; + do_cr = (flags & CRLF) != 0; + alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE; + + tail = new byte[2]; + tailLen = 0; + + count = do_newline ? LINE_GROUPS : -1; + } + + /** + * @return an overestimate for the number of bytes {@code + * len} bytes could encode to. + */ + public int maxOutputSize(int len) { + return len * 8 / 5 + 10; + } + + public boolean process(byte[] input, int offset, int len, boolean finish) { + // Using local variables makes the encoder about 9% faster. + final byte[] alphabet = this.alphabet; + final byte[] output = this.output; + int op = 0; + int count = this.count; + + int p = offset; + len += offset; + int v = -1; + + // First we need to concatenate the tail of the previous call + // with any input bytes available now and see if we can empty + // the tail. + + switch (tailLen) { + case 0: + // There was no tail. + break; + + case 1: + if (p + 2 <= len) { + // A 1-byte tail with at least 2 bytes of + // input available now. + v = ((tail[0] & 0xff) << 16) | + ((input[p++] & 0xff) << 8) | + (input[p++] & 0xff); + tailLen = 0; + } + ; + break; + + case 2: + if (p + 1 <= len) { + // A 2-byte tail with at least 1 byte of input. + v = ((tail[0] & 0xff) << 16) | + ((tail[1] & 0xff) << 8) | + (input[p++] & 0xff); + tailLen = 0; + } + break; + } + + if (v != -1) { + output[op++] = alphabet[(v >> 18) & 0x3f]; + output[op++] = alphabet[(v >> 12) & 0x3f]; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (--count == 0) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + count = LINE_GROUPS; + } + } + + // At this point either there is no tail, or there are fewer + // than 3 bytes of input available. + + // The main loop, turning 3 input bytes into 4 output bytes on + // each iteration. + while (p + 3 <= len) { + v = ((input[p] & 0xff) << 16) | + ((input[p + 1] & 0xff) << 8) | + (input[p + 2] & 0xff); + output[op] = alphabet[(v >> 18) & 0x3f]; + output[op + 1] = alphabet[(v >> 12) & 0x3f]; + output[op + 2] = alphabet[(v >> 6) & 0x3f]; + output[op + 3] = alphabet[v & 0x3f]; + p += 3; + op += 4; + if (--count == 0) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + count = LINE_GROUPS; + } + } + + if (finish) { + // Finish up the tail of the input. Note that we need to + // consume any bytes in tail before any bytes + // remaining in input; there should be at most two bytes + // total. + + if (p - tailLen == len - 1) { + int t = 0; + v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4; + tailLen -= t; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (do_padding) { + output[op++] = '='; + output[op++] = '='; + } + if (do_newline) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + } else if (p - tailLen == len - 2) { + int t = 0; + v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) | + (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2); + tailLen -= t; + output[op++] = alphabet[(v >> 12) & 0x3f]; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (do_padding) { + output[op++] = '='; + } + if (do_newline) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + } else if (do_newline && op > 0 && count != LINE_GROUPS) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + + assert tailLen == 0; + assert p == len; + } else { + // Save the leftovers in tail to be consumed on the next + // call to encodeInternal. + + if (p == len - 1) { + tail[tailLen++] = input[p]; + } else if (p == len - 2) { + tail[tailLen++] = input[p]; + tail[tailLen++] = input[p + 1]; + } + } + + this.op = op; + this.count = count; + + return true; + } + } + + private Base64() { + } // don't instantiate +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/cipher/Base64Cipher.java b/lib/DevJava/src/main/java/dev/utils/common/cipher/Base64Cipher.java new file mode 100644 index 0000000000..92b89082b1 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/cipher/Base64Cipher.java @@ -0,0 +1,75 @@ +package dev.utils.common.cipher; + +/** + * detail: Base64 编解码 ( 并进行 ) 加解密 + * @author Ttt + */ +public class Base64Cipher implements Cipher { + + // 中间加密层 + private Cipher mCipher; + // Base64 编解码 flags + private int mFlags = Base64.DEFAULT; + + /** + * 构造函数 + * @param flags Base64 编解码 flags + */ + public Base64Cipher(final int flags) { + this.mFlags = flags; + } + + /** + * 构造函数 + * @param cipher 加解密中间层 + */ + public Base64Cipher(final Cipher cipher) { + this.mCipher = cipher; + } + + /** + * 构造函数 + * @param cipher 加解密中间层 + * @param flags Base64 编解码 flags + */ + public Base64Cipher(final Cipher cipher, final int flags) { + this.mCipher = cipher; + this.mFlags = flags; + } + + // = + + /** + * 解码 + * @param data 待解码数据 + * @return 解码后的 byte[] + */ + @Override + public byte[] decrypt(byte[] data) { + if (data == null) return null; + // 先解码 + data = Base64.decode(data, mFlags); + // 再解密 + if (mCipher != null) { + data = mCipher.decrypt(data); + } + return data; + } + + /** + * 编码 + * @param data 待编码数据 + * @return 编码后的 byte[] + */ + @Override + public byte[] encrypt(byte[] data) { + if (data == null) return null; + // 先加密 + if (mCipher != null) { + data = mCipher.encrypt(data); + } + if (data == null) return null; + // 再编码 + return Base64.encode(data, mFlags); + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/cipher/Cipher.java b/lib/DevJava/src/main/java/dev/utils/common/cipher/Cipher.java new file mode 100644 index 0000000000..81d77b0ac2 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/cipher/Cipher.java @@ -0,0 +1,8 @@ +package dev.utils.common.cipher; + +/** + * detail: 通用加解密中间层 + * @author Ttt + */ +public interface Cipher extends Decrypt, Encrypt { +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/cipher/CipherUtils.java b/lib/DevJava/src/main/java/dev/utils/common/cipher/CipherUtils.java new file mode 100644 index 0000000000..33b31519c2 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/cipher/CipherUtils.java @@ -0,0 +1,199 @@ +package dev.utils.common.cipher; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import dev.utils.JCLogUtils; + +/** + * detail: 加密工具类 + * @author Ttt + */ +public final class CipherUtils { + + private CipherUtils() { + } + + // 日志 TAG + private static final String TAG = CipherUtils.class.getSimpleName(); + + /** + * 加密方法 + * @param object 待加密对象 + * @return 加密后的十六进制字符串 + */ + public static String encrypt(final Object object) { + return encrypt(object, null); + } + + /** + * 加密方法 + * @param object 待加密对象 + * @param cipher 加解密中间层 + * @return 加密后的十六进制字符串 + */ + public static String encrypt(final Object object, final Cipher cipher) { + if (object == null) return null; + byte[] bytes = objectToBytes(object); + if (cipher != null) bytes = cipher.encrypt(bytes); + return toHexString(bytes); + } + + // = + + /** + * 解密方法 + * @param hex 十六进制字符串 + * @return 解密后的对象 + */ + public static Object decrypt(final String hex) { + return decrypt(hex, null); + } + + /** + * 解密方法 + * @param hex 十六进制字符串 + * @param cipher 加解密中间层 + * @return 解密后的对象 + */ + public static Object decrypt(final String hex, final Cipher cipher) { + if (hex == null) return null; + byte[] bytes = decodeHex(hex.toCharArray()); + if (cipher != null) bytes = cipher.decrypt(bytes); + return bytesToObject(bytes); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data) { + return toHexString(data, HEX_DIGITS); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + /** + * 将十六进制字符数组解码 + * @param data 十六进制 char[] + * @return 十六进制转 ( 解 ) 码后的数据 + */ + private static byte[] decodeHex(final char[] data) { + if (data == null) return null; + try { + int len = data.length; + byte[] out = new byte[len >> 1]; + // 十六进制由两个字符组成 + for (int i = 0, j = 0; j < len; i++) { + int d = toDigit(data[j], j) << 4; + j++; + d = d | toDigit(data[j], j); + j++; + out[i] = (byte) (d & 0xFF); + } + return out; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "decodeHex"); + } + return null; + } + + /** + * 将十六进制字符转换成一个整数 + * @param ch 十六进制 char + * @param index 十六进制字符在字符数组中的位置 + * @return 一个整数 + * @throws Exception 当 ch 不是一个合法的十六进制字符时, 抛出运行时异常 + */ + private static int toDigit(final char ch, final int index) throws Exception { + int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new Exception(String.format("Illegal hexadecimal character %s at index %s", ch, index)); + } + return digit; + } + + /** + * byte[] 转为 Object + * @param bytes byte[] + * @return {@link Object} + */ + private static Object bytesToObject(final byte[] bytes) { + if (bytes == null) return null; + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); + return ois.readObject(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "bytesToObject"); + } finally { + if (ois != null) { + try { + ois.close(); + } catch (Exception e) { + } + } + } + return null; + } + + /** + * Object 转为 byte[] + * @param object Object + * @return byte[] + */ + private static byte[] objectToBytes(final Object object) { + if (object == null) return null; + ObjectOutputStream oos = null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(baos); + oos.writeObject(object); + return baos.toByteArray(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "objectToBytes"); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (Exception e) { + } + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/cipher/Decrypt.java b/lib/DevJava/src/main/java/dev/utils/common/cipher/Decrypt.java new file mode 100644 index 0000000000..ab696494a6 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/cipher/Decrypt.java @@ -0,0 +1,15 @@ +package dev.utils.common.cipher; + +/** + * detail: 解密 ( 解码 ) 接口 + * @author Ttt + */ +public interface Decrypt { + + /** + * 解密 ( 解码 ) 方法 + * @param data 待解码数据 + * @return 解码后的 byte[] + */ + byte[] decrypt(byte[] data); +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/cipher/Encrypt.java b/lib/DevJava/src/main/java/dev/utils/common/cipher/Encrypt.java new file mode 100644 index 0000000000..d802d33e72 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/cipher/Encrypt.java @@ -0,0 +1,15 @@ +package dev.utils.common.cipher; + +/** + * detail: 加密 ( 编码 ) 接口 + * @author Ttt + */ +public interface Encrypt { + + /** + * 加密 ( 编码 ) 方法 + * @param data 待编码数据 + * @return 编码后的 byte[] + */ + byte[] encrypt(byte[] data); +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/encrypt/AESUtils.java b/lib/DevJava/src/main/java/dev/utils/common/encrypt/AESUtils.java new file mode 100644 index 0000000000..e6549af1ae --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/encrypt/AESUtils.java @@ -0,0 +1,79 @@ +package dev.utils.common.encrypt; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import dev.utils.JCLogUtils; + +/** + * detail: AES 对称加密工具类 + * @author Ttt + *

+ *     Advanced Encryption Standard 高级数据加密标准 ( 对称加密算法 )
+ *     AES 算法可以有效抵制针对 DES 的攻击算法
+ * 
+ */ +public final class AESUtils { + + private AESUtils() { + } + + // 日志 TAG + private static final String TAG = AESUtils.class.getSimpleName(); + + /** + * 生成密钥 + * @return 密钥 byte[] + */ + public static byte[] initKey() { + try { + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); // 192 256 + SecretKey secretKey = keyGen.generateKey(); + return secretKey.getEncoded(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "initKey"); + } + return null; + } + + /** + * AES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return 加密后的 byte[] + */ + public static byte[] encrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; + try { + SecretKey secretKey = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + return cipher.doFinal(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "encrypt"); + } + return null; + } + + /** + * AES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @return 解密后的 byte[] + */ + public static byte[] decrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; + try { + SecretKey secretKey = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return cipher.doFinal(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "decrypt"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/encrypt/CRCUtils.java b/lib/DevJava/src/main/java/dev/utils/common/encrypt/CRCUtils.java new file mode 100644 index 0000000000..68ca4f1505 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/encrypt/CRCUtils.java @@ -0,0 +1,88 @@ +package dev.utils.common.encrypt; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.zip.CRC32; + +import dev.utils.JCLogUtils; + +/** + * detail: CRC 工具类 + * @author Ttt + *
+ *     Cyclic Redundancy Check 循环冗余校验
+ *     CRC 是一种根据网络数据包或电脑文件等数据产生简短固定位数校验码的一种散列函数
+ * 
+ */ +public final class CRCUtils { + + private CRCUtils() { + } + + // 日志 TAG + private static final String TAG = CRCUtils.class.getSimpleName(); + + /** + * 获取 CRC32 值 + * @param data 字符串数据 + * @return CRC32 long 值 + */ + public static long getCRC32(final String data) { + if (data == null) return -1L; + try { + CRC32 crc32 = new CRC32(); + crc32.update(data.getBytes()); + return crc32.getValue(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getCRC32"); + } + return -1L; + } + + /** + * 获取 CRC32 值 + * @param data 字符串数据 + * @return CRC32 字符串 + */ + public static String getCRC32ToHexString(final String data) { + if (data == null) return null; + try { + CRC32 crc32 = new CRC32(); + crc32.update(data.getBytes()); + return Long.toHexString(crc32.getValue()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getCRC32ToHexString"); + } + return null; + } + + /** + * 获取文件 CRC32 值 + * @param filePath 文件路径 + * @return 文件 CRC32 值 + */ + public static String getFileCRC32(final String filePath) { + if (filePath == null) return null; + InputStream is = null; + try { + is = new FileInputStream(filePath); + byte[] buffer = new byte[1024]; + CRC32 crc32 = new CRC32(); + int numRead; + while ((numRead = is.read(buffer)) > 0) { + crc32.update(buffer, 0, numRead); + } + return Long.toHexString(crc32.getValue()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileCRC32"); + } finally { + if (is != null) { + try { + is.close(); + } catch (Exception e) { + } + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/encrypt/DESUtils.java b/lib/DevJava/src/main/java/dev/utils/common/encrypt/DESUtils.java new file mode 100644 index 0000000000..68898c8fbc --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/encrypt/DESUtils.java @@ -0,0 +1,82 @@ +package dev.utils.common.encrypt; + +import java.security.Key; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.SecretKeySpec; + +import dev.utils.JCLogUtils; + +/** + * detail: DES 对称加密工具类 + * @author Ttt + *
+ *     Data Encryption Standard 数据加密标准 ( 对称加密算法 )
+ * 
+ */ +public final class DESUtils { + + private DESUtils() { + } + + // 日志 TAG + private static final String TAG = DESUtils.class.getSimpleName(); + + /** + * 获取可逆算法 DES 的密钥 + * @param key 前八个字节将被用来生成密钥 + * @return 可逆算法 DES 的密钥 + */ + public static Key getDESKey(final byte[] key) { + if (key == null) return null; + try { + DESKeySpec desKey = new DESKeySpec(key); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + return keyFactory.generateSecret(desKey); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDESKey"); + } + return null; + } + + /** + * DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return 加密后的 byte[] + */ + public static byte[] encrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; + try { + SecretKey secretKey = new SecretKeySpec(key, "DES"); + Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + return cipher.doFinal(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "encrypt"); + } + return null; + } + + /** + * DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @return 解密后的 byte[] + */ + public static byte[] decrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; + try { + SecretKey secretKey = new SecretKeySpec(key, "DES"); + Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return cipher.doFinal(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "decrypt"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/encrypt/EncryptUtils.java b/lib/DevJava/src/main/java/dev/utils/common/encrypt/EncryptUtils.java new file mode 100644 index 0000000000..42fd1250b2 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/encrypt/EncryptUtils.java @@ -0,0 +1,1137 @@ +package dev.utils.common.encrypt; + +import java.io.File; +import java.io.FileInputStream; +import java.security.DigestInputStream; +import java.security.Key; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import dev.utils.JCLogUtils; +import dev.utils.common.cipher.Base64; + +/** + * detail: 加解密通用工具类 + * @author Blankj + * @author Ttt ( 重写、规范注释、逻辑判断等 ) + */ +public final class EncryptUtils { + + private EncryptUtils() { + } + + // 日志 TAG + private static final String TAG = EncryptUtils.class.getSimpleName(); + + /** + * MD2 加密 + * @param data 待加密数据 + * @return MD2 加密后的数据 + */ + public static byte[] encryptMD2(final byte[] data) { + return hashTemplate(data, "MD2"); + } + + /** + * MD2 加密 + * @param data 待加密数据 + * @return MD2 加密后的十六进制字符串 + */ + public static String encryptMD2ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptMD2ToHexString(data.getBytes()); + } + + /** + * MD2 加密 + * @param data 待加密数据 + * @return MD2 加密后的十六进制字符串 + */ + public static String encryptMD2ToHexString(final byte[] data) { + return toHexString(encryptMD2(data)); + } + + // = + + /** + * MD5 加密 + * @param data 待加密数据 + * @return MD5 加密后的数据 + */ + public static byte[] encryptMD5(final byte[] data) { + return hashTemplate(data, "MD5"); + } + + /** + * MD5 加密 + * @param data 待加密数据 + * @return MD5 加密后的十六进制字符串 + */ + public static String encryptMD5ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptMD5ToHexString(data.getBytes()); + } + + /** + * MD5 加密 + * @param data 待加密数据 + * @param salt salt + * @return MD5 加密后的十六进制字符串 + */ + public static String encryptMD5ToHexString(final String data, final String salt) { + if (data == null && salt == null) return null; + if (salt == null) return toHexString(encryptMD5(data.getBytes())); + if (data == null) return toHexString(encryptMD5(salt.getBytes())); + return toHexString(encryptMD5((data + salt).getBytes())); + } + + // = + + /** + * MD5 加密 + * @param data 待加密数据 + * @return MD5 加密后的十六进制字符串 + */ + public static String encryptMD5ToHexString(final byte[] data) { + return toHexString(encryptMD5(data)); + } + + /** + * MD5 加密 + * @param data 待加密数据 + * @param salt salt + * @return MD5 加密后的十六进制字符串 + */ + public static String encryptMD5ToHexString(final byte[] data, final byte[] salt) { + if (data == null && salt == null) return null; + if (salt == null) return toHexString(encryptMD5(data)); + if (data == null) return toHexString(encryptMD5(salt)); + // 拼接数据 + byte[] bytes = arraycopy(data, salt); + return toHexString(encryptMD5(bytes)); + } + + // = + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值 + */ + public static byte[] encryptMD5File(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return encryptMD5File(file); + } + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String encryptMD5FileToHexString(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return encryptMD5FileToHexString(file); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String encryptMD5FileToHexString(final File file) { + return toHexString(encryptMD5File(file)); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值 byte[] + */ + public static byte[] encryptMD5File(final File file) { + if (file == null) return null; + DigestInputStream dis = null; + try { + FileInputStream fis = new FileInputStream(file); + MessageDigest digest = MessageDigest.getInstance("MD5"); + dis = new DigestInputStream(fis, digest); + byte[] buffer = new byte[256 * 1024]; + while (true) { + if (!(dis.read(buffer) > 0)) break; + } + digest = dis.getMessageDigest(); + return digest.digest(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "encryptMD5File"); + return null; + } finally { + if (dis != null) { + try { + dis.close(); + } catch (Exception e) { + } + } + } + } + + // = + + /** + * SHA1 加密 + * @param data 待加密数据 + * @return SHA1 加密后的数据 + */ + public static byte[] encryptSHA1(final byte[] data) { + return hashTemplate(data, "SHA-1"); + } + + /** + * SHA1 加密 + * @param data 待加密数据 + * @return SHA1 加密后的数据转十六进制字符串 + */ + public static String encryptSHA1ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptSHA1ToHexString(data.getBytes()); + } + + /** + * SHA1 加密 + * @param data 待加密数据 + * @return SHA1 加密后的数据转十六进制字符串 + */ + public static String encryptSHA1ToHexString(final byte[] data) { + return toHexString(encryptSHA1(data)); + } + + // = + + /** + * SHA224 加密 + * @param data 待加密数据 + * @return SHA224 加密后的数据 + */ + public static byte[] encryptSHA224(final byte[] data) { + return hashTemplate(data, "SHA224"); + } + + /** + * SHA224 加密 + * @param data 待加密数据 + * @return SHA224 加密后的数据转十六进制字符串 + */ + public static String encryptSHA224ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptSHA224ToHexString(data.getBytes()); + } + + /** + * SHA224 加密 + * @param data 待加密数据 + * @return SHA224 加密后的数据转十六进制字符串 + */ + public static String encryptSHA224ToHexString(final byte[] data) { + return toHexString(encryptSHA224(data)); + } + + // = + + /** + * SHA256 加密 + * @param data 待加密数据 + * @return SHA256 加密后的数据 + */ + public static byte[] encryptSHA256(final byte[] data) { + return hashTemplate(data, "SHA-256"); + } + + /** + * SHA256 加密 + * @param data 待加密数据 + * @return SHA256 加密后的数据转十六进制字符串 + */ + public static String encryptSHA256ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptSHA256ToHexString(data.getBytes()); + } + + /** + * SHA256 加密 + * @param data 待加密数据 + * @return SHA256 加密后的数据转十六进制 + */ + public static String encryptSHA256ToHexString(final byte[] data) { + return toHexString(encryptSHA256(data)); + } + + // = + + /** + * SHA384 加密 + * @param data 待加密数据 + * @return SHA384 加密后的数据 + */ + public static byte[] encryptSHA384(final byte[] data) { + return hashTemplate(data, "SHA-384"); + } + + /** + * SHA384 加密 + * @param data 待加密数据 + * @return SHA384 加密后的数据转十六进制 + */ + public static String encryptSHA384ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptSHA384ToHexString(data.getBytes()); + } + + /** + * SHA384 加密 + * @param data 待加密数据 + * @return SHA384 加密后的数据转十六进制 + */ + public static String encryptSHA384ToHexString(final byte[] data) { + return toHexString(encryptSHA384(data)); + } + + // = + + /** + * SHA512 加密 + * @param data 待加密数据 + * @return SHA512 加密后的数据 + */ + public static byte[] encryptSHA512(final byte[] data) { + return hashTemplate(data, "SHA-512"); + } + + /** + * SHA512 加密 + * @param data 待加密数据 + * @return SHA512 加密后的数据转十六进制 + */ + public static String encryptSHA512ToHexString(final String data) { + if (data == null || data.length() == 0) return null; + return encryptSHA512ToHexString(data.getBytes()); + } + + /** + * SHA512 加密 + * @param data 待加密数据 + * @return SHA512 加密后的数据转十六进制 + */ + public static String encryptSHA512ToHexString(final byte[] data) { + return toHexString(encryptSHA512(data)); + } + + /** + * Hash 加密模版方法 + * @param data 待加密数据 + * @param algorithm 算法 + * @return 指定加密算法加密后的数据 + */ + private static byte[] hashTemplate(final byte[] data, final String algorithm) { + if (data == null || data.length == 0) return null; + try { + MessageDigest digest = MessageDigest.getInstance(algorithm); + digest.update(data); + return digest.digest(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "hashTemplate"); + return null; + } + } + + // = + + /** + * HmacMD5 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacMD5 加密后的数据 + */ + public static byte[] encryptHmacMD5(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacMD5"); + } + + /** + * HmacMD5 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacMD5 加密后的数据转十六进制 + */ + public static String encryptHmacMD5ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacMD5ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacMD5 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacMD5 加密后的数据转十六进制 + */ + public static String encryptHmacMD5ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacMD5(data, key)); + } + + // = + + /** + * HmacSHA1 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA1 加密后的数据 + */ + public static byte[] encryptHmacSHA1(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacSHA1"); + } + + /** + * HmacSHA1 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA1 加密后的数据转十六进制 + */ + public static String encryptHmacSHA1ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacSHA1ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacSHA1 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA1 加密后的数据转十六进制 + */ + public static String encryptHmacSHA1ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacSHA1(data, key)); + } + + // = + + /** + * HmacSHA224 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA224 加密后的数据 + */ + public static byte[] encryptHmacSHA224(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacSHA224"); + } + + /** + * HmacSHA224 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA224 加密后的数据转十六进制 + */ + public static String encryptHmacSHA224ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacSHA224ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacSHA224 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA224 加密后的数据转十六进制 + */ + public static String encryptHmacSHA224ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacSHA224(data, key)); + } + + // = + + /** + * HmacSHA256 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA256 加密后的数据 + */ + public static byte[] encryptHmacSHA256(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacSHA256"); + } + + /** + * HmacSHA256 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA256 加密后的数据转十六进制 + */ + public static String encryptHmacSHA256ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacSHA256ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacSHA256 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA256 加密后的数据转十六进制 + */ + public static String encryptHmacSHA256ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacSHA256(data, key)); + } + + // = + + /** + * HmacSHA384 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA384 加密后的数据 + */ + public static byte[] encryptHmacSHA384(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacSHA384"); + } + + /** + * HmacSHA384 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA384 加密后的数据转十六进制 + */ + public static String encryptHmacSHA384ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacSHA384ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacSHA384 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA384 加密后的数据转十六进制 + */ + public static String encryptHmacSHA384ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacSHA384(data, key)); + } + + // = + + /** + * HmacSHA512 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA512 加密后的数据 + */ + public static byte[] encryptHmacSHA512(final byte[] data, final byte[] key) { + return hmacTemplate(data, key, "HmacSHA512"); + } + + /** + * HmacSHA512 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA512 加密后的数据转十六进制 + */ + public static String encryptHmacSHA512ToHexString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return null; + return encryptHmacSHA512ToHexString(data.getBytes(), key.getBytes()); + } + + /** + * HmacSHA512 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return HmacSHA512 加密后的数据转十六进制 + */ + public static String encryptHmacSHA512ToHexString(final byte[] data, final byte[] key) { + return toHexString(encryptHmacSHA512(data, key)); + } + + /** + * Hmac 加密模版方法 + * @param data 待加密数据 + * @param key 密钥 + * @param algorithm 算法 + * @return 指定加密算法和密钥, 加密后的数据 + */ + private static byte[] hmacTemplate(final byte[] data, final byte[] key, final String algorithm) { + if (data == null || data.length == 0 || key == null || key.length == 0) return null; + try { + SecretKeySpec secretKey = new SecretKeySpec(key, algorithm); + Mac mac = Mac.getInstance(algorithm); + mac.init(secretKey); + return mac.doFinal(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "hmacTemplate"); + return null; + } + } + + // = + + /** + * DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return DES 加密后的数据 + */ + public static byte[] encryptDES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "DES", transformation, iv, true); + } + + /** + * DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return DES 加密后的数据转 Base64 + */ + public static byte[] encryptDESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return base64Encode(encryptDES(data, key, transformation, iv)); + } + + /** + * DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return DES 加密后的数据转十六进制 + */ + public static String encryptDESToHexString(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return toHexString(encryptDES(data, key, transformation, iv)); + } + + // = + + /** + * DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return DES 解密后的数据 + */ + public static byte[] decryptDES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "DES", transformation, iv, false); + } + + /** + * DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return Base64 解码后, 在进行 DES 解密后的数据 + */ + public static byte[] decryptDESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return decryptDES(base64Decode(data), key, transformation, iv); + } + + /** + * DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 十六进制转换后, 在进行 DES 解密后的数据 + */ + public static byte[] decryptDESToHexString(final String data, final byte[] key, final String transformation, final byte[] iv) { + return decryptDES(decodeHex(data), key, transformation, iv); + } + + // = + + /** + * 3DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 3DES 加密后的数据 + */ + public static byte[] encrypt3DES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "DESede", transformation, iv, true); + } + + /** + * 3DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 3DES 加密后的数据转 Base64 + */ + public static byte[] encrypt3DESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return base64Encode(encrypt3DES(data, key, transformation, iv)); + } + + /** + * 3DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 3DES 加密后的数据转十六进制 + */ + public static String encrypt3DESToHexString(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return toHexString(encrypt3DES(data, key, transformation, iv)); + } + + // = + + /** + * 3DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 3DES 解密后的数据 + */ + public static byte[] decrypt3DES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "DESede", transformation, iv, false); + } + + /** + * 3DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return Base64 解码后, 在进行 3DES 解密后的数据 + */ + public static byte[] decrypt3DESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return decrypt3DES(base64Decode(data), key, transformation, iv); + } + + /** + * 3DES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 十六进制转换后, 在进行 3DES 解密后的数据 + */ + public static byte[] decrypt3DESToHexString(final String data, final byte[] key, final String transformation, final byte[] iv) { + return decrypt3DES(decodeHex(data), key, transformation, iv); + } + + // = + + /** + * AES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return AES 加密后的数据 + */ + public static byte[] encryptAES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "AES", transformation, iv, true); + } + + /** + * AES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return AES 加密后的数据转 Base64 + */ + public static byte[] encryptAESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return base64Encode(encryptAES(data, key, transformation, iv)); + } + + /** + * AES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return AES 加密后的数据转十六进制 + */ + public static String encryptAESToHexString(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return toHexString(encryptAES(data, key, transformation, iv)); + } + + // = + + /** + * AES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return AES 解密后的数据 + */ + public static byte[] decryptAES(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return symmetricTemplate(data, key, "AES", transformation, iv, false); + } + + /** + * AES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return Base64 解码后, 在进行 AES 解密后的数据 + */ + public static byte[] decryptAESToBase64(final byte[] data, final byte[] key, final String transformation, final byte[] iv) { + return decryptAES(base64Decode(data), key, transformation, iv); + } + + /** + * AES 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @return 十六进制转换后, 在进行 AES 解密后的数据 + */ + public static byte[] decryptAESToHexString(final String data, final byte[] key, final String transformation, final byte[] iv) { + return decryptAES(decodeHex(data), key, transformation, iv); + } + + /** + * 对称加密模版方法 + * @param data 待加解密数据 + * @param key 密钥 + * @param algorithm 算法 + * @param transformation {@link Cipher#getInstance} transformation + * @param iv 算法参数 {@link AlgorithmParameterSpec} + * @param isEncrypt 是否加密处理 + * @return 指定加密算法, 加解密后的数据 + */ + private static byte[] symmetricTemplate(final byte[] data, final byte[] key, final String algorithm, + final String transformation, final byte[] iv, final boolean isEncrypt) { + if (data == null || data.length == 0 || key == null || key.length == 0) return null; + try { + SecretKey secretKey; + if ("DES".equals(algorithm)) { + DESKeySpec desKey = new DESKeySpec(key); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); + secretKey = keyFactory.generateSecret(desKey); + } else { + secretKey = new SecretKeySpec(key, algorithm); + } + Cipher cipher = Cipher.getInstance(transformation); + if (iv == null || iv.length == 0) { + cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKey); + } else { + AlgorithmParameterSpec params = new IvParameterSpec(iv); + cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKey, params); + } + return cipher.doFinal(data); + } catch (Throwable e) { + JCLogUtils.eTag(TAG, e, "symmetricTemplate"); + return null; + } + } + + // = + + /** + * RSA 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return RSA 加密后的数据 + */ + public static byte[] encryptRSA(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return rsaTemplate(data, key, isPublicKey, transformation, true); + } + + /** + * RSA 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return RSA 加密后的数据转 Base64 + */ + public static byte[] encryptRSAToBase64(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return base64Encode(encryptRSA(data, key, isPublicKey, transformation)); + } + + /** + * RSA 加密 + * @param data 待加密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return RSA 加密后的数据转十六进制 + */ + public static String encryptRSAToHexString(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return toHexString(encryptRSA(data, key, isPublicKey, transformation)); + } + + // = + + /** + * RSA 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return RSA 解密后的数据 + */ + public static byte[] decryptRSA(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return rsaTemplate(data, key, isPublicKey, transformation, false); + } + + /** + * RSA 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return Base64 解码后, 在进行 RSA 解密后的数据 + */ + public static byte[] decryptRSAToBase64(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return decryptRSA(base64Decode(data), key, isPublicKey, transformation); + } + + /** + * RSA 解密 + * @param data 待解密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @return 十六进制转换后, 在进行 RSA 解密后的数据 + */ + public static byte[] decryptRSAToHexString(final String data, final byte[] key, final boolean isPublicKey, final String transformation) { + return decryptRSA(decodeHex(data), key, isPublicKey, transformation); + } + + /** + * RSA 加解密模版方法 + * @param data 待加解密数据 + * @param key 密钥 + * @param isPublicKey {@code true} {@link X509EncodedKeySpec}, {@code false} {@link PKCS8EncodedKeySpec} + * @param transformation {@link Cipher#getInstance} transformation + * @param isEncrypt 是否加密处理 + * @return 指定加密算法, 加解密后的数据 + */ + private static byte[] rsaTemplate(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation, final boolean isEncrypt) { + if (data == null || key == null) return null; + try { + int dataLength = data.length; + int keyLength = key.length; + if (dataLength == 0 || keyLength == 0) return null; + + Key rsaKey; + if (isPublicKey) { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key); + rsaKey = KeyFactory.getInstance("RSA").generatePublic(keySpec); + } else { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key); + rsaKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec); + } + if (rsaKey == null) return null; + Cipher cipher = Cipher.getInstance(transformation); + cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, rsaKey); + int maxLen = isEncrypt ? 117 : 128; + int count = dataLength / maxLen; + if (count > 0) { + byte[] ret = new byte[0]; + byte[] buffer = new byte[maxLen]; + int index = 0; + for (int i = 0; i < count; i++) { + System.arraycopy(data, index, buffer, 0, maxLen); + ret = arraycopy(ret, cipher.doFinal(buffer)); + index += maxLen; + } + if (index != dataLength) { + int restLen = dataLength - index; + buffer = new byte[restLen]; + System.arraycopy(data, index, buffer, 0, restLen); + ret = arraycopy(ret, cipher.doFinal(buffer)); + } + return ret; + } else { + return cipher.doFinal(data); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "rsaTemplate"); + } + return null; + } + + // ============ + // = 私有方法 = + // ============ + + /** + * Base64 编码 + * @param input 待编码数据 + * @return Base64 编码后的 byte[] + */ + private static byte[] base64Encode(final byte[] input) { + if (input == null) return null; + return Base64.encode(input, Base64.NO_WRAP); + } + + /** + * Base64 解码 + * @param input 待解码数据 + * @return Base64 解码后的 byte[] + */ + private static byte[] base64Decode(final byte[] input) { + if (input == null) return null; + return Base64.decode(input, Base64.NO_WRAP); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制 String + */ + private static String toHexString(final byte[] data) { + return toHexString(data, HEX_DIGITS); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + /** + * 将十六进制字符串解码 + * @param hex 十六进制字符串 + * @return 十六进制转 ( 解 ) 码后的数据 + */ + private static byte[] decodeHex(final String hex) { + return decodeHex(isEmpty(hex) ? null : hex.toCharArray()); + } + + /** + * 将十六进制字符数组解码 + * @param data 待加密数据 十六进制 char[] + * @return 十六进制转 ( 解 ) 码后的数据 + */ + private static byte[] decodeHex(final char[] data) { + if (data == null) return null; + try { + int len = data.length; + byte[] out = new byte[len >> 1]; + // 十六进制由两个字符组成 + for (int i = 0, j = 0; j < len; i++) { + int d = toDigit(data[j], j) << 4; + j++; + d = d | toDigit(data[j], j); + j++; + out[i] = (byte) (d & 0xFF); + } + return out; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "decodeHex"); + } + return null; + } + + /** + * 将十六进制字符转换成一个整数 + * @param ch 十六进制 char + * @param index 十六进制字符在字符数组中的位置 + * @return 十六进制 转 int 整数 + * @throws Exception 当 ch 不是一个合法的十六进制字符时, 抛出运行时异常 + */ + private static int toDigit(final char ch, final int index) throws Exception { + int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new Exception(String.format("Illegal hexadecimal character %s at index %s", ch, index)); + } + return digit; + } + + // ============== + // = ArrayUtils = + // ============== + + /** + * 拼接数组 + * @param prefix 第一个数组 + * @param suffix 第二个数组 + * @return 拼接后的数组 + */ + private static byte[] arraycopy(final byte[] prefix, final byte[] suffix) { + // 获取数据长度 + int prefixLength = (prefix != null) ? prefix.length : 0; + int suffixLength = (suffix != null) ? suffix.length : 0; + // 数据都为 null, 则直接跳过 + if (prefixLength + suffixLength == 0) return null; + // 创建数组 + byte[] arrays = new byte[prefixLength + suffixLength]; + // 进行判断处理 + if (prefixLength != 0) { + System.arraycopy(prefix, 0, arrays, 0, prefixLength); + } + if (suffixLength != 0) { + System.arraycopy(suffix, 0, arrays, prefixLength, suffixLength); + } + return arrays; + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/encrypt/EscapeUtils.java b/lib/DevJava/src/main/java/dev/utils/common/encrypt/EscapeUtils.java new file mode 100644 index 0000000000..c5a60ac72c --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/encrypt/EscapeUtils.java @@ -0,0 +1,141 @@ +package dev.utils.common.encrypt; + +/** + * detail: 字符串 ( 编解码 ) 工具类 + * @author Ttt + */ +public final class EscapeUtils { + + private EscapeUtils() { + } + + /** + * 编码 + * @param data 待编码数据 + * @return 编码后的字符串 + */ + public static String escape(final String data) { + if (data == null) return null; + StringBuilder builder = new StringBuilder(); + for (int i = 0, len = data.length(); i < len; i++) { + int ch = data.charAt(i); + if ('A' <= ch && ch <= 'Z') { + builder.append((char) ch); + } else if ('a' <= ch && ch <= 'z') { + builder.append((char) ch); + } else if ('0' <= ch && ch <= '9') { + builder.append((char) ch); + } else if (ch == '-' || ch == '_' || ch == '.' || ch == '!' + || ch == '~' || ch == '*' || ch == '\'' || ch == '(' + || ch == ')') { + builder.append((char) ch); + } else if (ch <= 0x007F) { + builder.append('%'); + builder.append(HEX[ch]); + } else { + builder.append('%'); + builder.append('u'); + builder.append(HEX[(ch >>> 8)]); + builder.append(HEX[(0x00FF & ch)]); + } + } + return builder.toString(); + } + + /** + * 解码 - 本方法不论参数 data 是否经过 escape() 编码, 均能获取正确的 ( 解码 ) 结果 + * @param data 待解码数据 + * @return 解码后的字符串 + */ + public static String unescape(final String data) { + if (data == null) return null; + StringBuilder builder = new StringBuilder(); + int i = 0; + int len = data.length(); + while (i < len) { + int ch = data.charAt(i); + if ('A' <= ch && ch <= 'Z') { + builder.append((char) ch); + } else if ('a' <= ch && ch <= 'z') { + builder.append((char) ch); + } else if ('0' <= ch && ch <= '9') { + builder.append((char) ch); + } else if (ch == '-' || ch == '_' || ch == '.' || ch == '!' + || ch == '~' || ch == '*' || ch == '\'' || ch == '(' + || ch == ')') { + builder.append((char) ch); + } else if (ch == '%') { + int cint = 0; + if ('u' != data.charAt(i + 1)) { + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 1)]; + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 2)]; + i += 2; + } else { + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 2)]; + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 3)]; + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 4)]; + cint = (cint << 4) | BYTE_VALUES[data.charAt(i + 5)]; + i += 5; + } + builder.append((char) cint); + } else { + builder.append((char) ch); + } + i++; + } + return builder.toString(); + } + + // = + + // 十六进制 0-255 + private static final String[] HEX = {"00", "01", "02", "03", "04", "05", + "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", "10", + "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", + "1C", "1D", "1E", "1F", "20", "21", "22", "23", "24", "25", "26", + "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", "30", "31", + "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", + "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47", + "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", + "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", + "5E", "5F", "60", "61", "62", "63", "64", "65", "66", "67", "68", + "69", "6A", "6B", "6C", "6D", "6E", "6F", "70", "71", "72", "73", + "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", + "7F", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", + "8A", "8B", "8C", "8D", "8E", "8F", "90", "91", "92", "93", "94", + "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F", + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", + "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3", "B4", "B5", + "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF", "C0", + "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", + "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", + "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", + "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", + "ED", "EE", "EF", "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", + "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"}; + + private static final byte[] BYTE_VALUES = {0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F}; +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/encrypt/MD5Utils.java b/lib/DevJava/src/main/java/dev/utils/common/encrypt/MD5Utils.java new file mode 100644 index 0000000000..6177ca3055 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/encrypt/MD5Utils.java @@ -0,0 +1,219 @@ +package dev.utils.common.encrypt; + +import java.io.File; +import java.io.FileInputStream; +import java.security.DigestInputStream; +import java.security.MessageDigest; + +import dev.utils.JCLogUtils; + +/** + * detail: MD5 加密工具类 + * @author Ttt + *
+ *     Message Digest 消息摘要算法
+ * 
+ */ +public final class MD5Utils { + + private MD5Utils() { + } + + // 日志 TAG + private static final String TAG = MD5Utils.class.getSimpleName(); + + /** + * 加密内容 (32 位小写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + public static String md5(final String data) { + if (data == null) return null; + try { + return md5(data.getBytes()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5"); + } + return null; + } + + /** + * 加密内容 (32 位小写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + public static String md5(final byte[] data) { + if (data == null) return null; + try { + // 获取 MD5 摘要算法的 MessageDigest 对象 + MessageDigest digest = MessageDigest.getInstance("MD5"); + // 使用指定的字节更新摘要 + digest.update(data); + // 获取密文 + return toHexString(digest.digest(), HEX_DIGITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5"); + } + return null; + } + + /** + * 加密内容 (32 位大写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + public static String md5Upper(final String data) { + if (data == null) return null; + try { + return md5Upper(data.getBytes()); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5Upper"); + } + return null; + } + + /** + * 加密内容 (32 位大写 MD5) + * @param data 待加密数据 + * @return MD5 加密后的字符串 + */ + public static String md5Upper(final byte[] data) { + if (data == null) return null; + try { + // 获取 MD5 摘要算法的 MessageDigest 对象 + MessageDigest digest = MessageDigest.getInstance("MD5"); + // 使用指定的字节更新摘要 + digest.update(data); + // 获取密文 + return toHexString(digest.digest(), HEX_DIGITS_UPPER); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "md5Upper"); + } + return null; + } + + // = + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值 + */ + public static byte[] getFileMD5(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileMD5(file); + } + + /** + * 获取文件 MD5 值 + * @param filePath 文件路径 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String getFileMD5ToHexString(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileMD5ToHexString(file); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值转十六进制字符串 + */ + public static String getFileMD5ToHexString(final File file) { + return toHexString(getFileMD5(file)); + } + + /** + * 获取文件 MD5 值 + * @param file 文件 + * @return 文件 MD5 值 byte[] + */ + public static byte[] getFileMD5(final File file) { + if (file == null) return null; + DigestInputStream dis = null; + try { + FileInputStream fis = new FileInputStream(file); + MessageDigest digest = MessageDigest.getInstance("MD5"); + dis = new DigestInputStream(fis, digest); + byte[] buffer = new byte[256 * 1024]; + while (true) { + if (!(dis.read(buffer) > 0)) break; + } + digest = dis.getMessageDigest(); + return digest.digest(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileMD5"); + return null; + } finally { + if (dis != null) { + try { + dis.close(); + } catch (Exception e) { + } + } + } + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + // 用于建立十六进制字符的输出的大写字符数组 + private static final char[] HEX_DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @return 十六进制 String + */ + private static String toHexString(final byte[] data) { + return toHexString(data, HEX_DIGITS); + } + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS}、{@link #HEX_DIGITS_UPPER} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/encrypt/SHAUtils.java b/lib/DevJava/src/main/java/dev/utils/common/encrypt/SHAUtils.java new file mode 100644 index 0000000000..bf96349d5c --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/encrypt/SHAUtils.java @@ -0,0 +1,217 @@ +package dev.utils.common.encrypt; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.MessageDigest; + +import dev.utils.JCLogUtils; + +/** + * detail: SHA 加密工具类 + * @author Ttt + */ +public final class SHAUtils { + + private SHAUtils() { + } + + // 日志 TAG + private static final String TAG = SHAUtils.class.getSimpleName(); + + /** + * 加密内容 SHA1 + * @param data 待加密数据 + * @return SHA1 加密后的字符串 + */ + public static String sha1(final String data) { + return shaHex(data, "SHA-1"); + } + + /** + * 加密内容 SHA224 + * @param data 待加密数据 + * @return SHA224 加密后的字符串 + */ + public static String sha224(final String data) { + return shaHex(data, "SHA-224"); + } + + /** + * 加密内容 SHA256 + * @param data 待加密数据 + * @return SHA256 加密后的字符串 + */ + public static String sha256(final String data) { + return shaHex(data, "SHA-256"); + } + + /** + * 加密内容 SHA384 + * @param data 待加密数据 + * @return SHA384 加密后的字符串 + */ + public static String sha384(final String data) { + return shaHex(data, "SHA-384"); + } + + /** + * 加密内容 SHA512 + * @param data 待加密数据 + * @return SHA512 加密后的字符串 + */ + public static String sha512(final String data) { + return shaHex(data, "SHA-512"); + } + + // = + + /** + * 获取文件 SHA1 值 + * @param filePath 文件路径 + * @return 文件 SHA1 字符串信息 + */ + public static String getFileSHA1(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileSHA(file, "SHA-1"); + } + + /** + * 获取文件 SHA1 值 + * @param file 文件 + * @return 文件 SHA1 字符串信息 + */ + public static String getFileSHA1(final File file) { + return getFileSHA(file, "SHA-1"); + } + + /** + * 获取文件 SHA256 值 + * @param filePath 文件路径 + * @return 文件 SHA256 字符串信息 + */ + public static String getFileSHA256(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileSHA(file, "SHA-256"); + } + + /** + * 获取文件 SHA256 值 + * @param file 文件 + * @return 文件 SHA256 字符串信息 + */ + public static String getFileSHA256(final File file) { + return getFileSHA(file, "SHA-256"); + } + + // = + + /** + * 加密内容 SHA 模板 + * @param data 待加密数据 + * @param algorithm 算法 + * @return SHA 算法加密后的字符串 + */ + public static String shaHex(final String data, final String algorithm) { + if (data == null || algorithm == null) return null; + try { + byte[] bytes = data.getBytes(); + // 获取 SHA-1 摘要算法的 MessageDigest 对象 + MessageDigest digest = MessageDigest.getInstance(algorithm); + // 使用指定的字节更新摘要 + digest.update(bytes); + // 获取密文 + return toHexString(digest.digest(), HEX_DIGITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "shaHex"); + } + return null; + } + + /** + * 获取文件 SHA 值 + * @param file 文件 + * @param algorithm 算法 + * @return 文件指定 SHA 字符串信息 + */ + public static String getFileSHA(final File file, final String algorithm) { + if (file == null || algorithm == null) return null; + InputStream is = null; + try { + is = new FileInputStream(file); + byte[] buffer = new byte[1024]; + MessageDigest digest = MessageDigest.getInstance(algorithm); + int numRead = 0; + while ((numRead = is.read(buffer)) > 0) { + digest.update(buffer, 0, numRead); + } + return toHexString(digest.digest(), HEX_DIGITS); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFileSHA"); + } finally { + if (is != null) { + try { + is.close(); + } catch (Exception e) { + } + } + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // ================ + // = ConvertUtils = + // ================ + + // 用于建立十六进制字符的输出的小写字符数组 + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 将 byte[] 转换 十六进制字符串 + * @param data 待转换数据 + * @param hexDigits {@link #HEX_DIGITS} + * @return 十六进制字符串 + */ + private static String toHexString(final byte[] data, final char[] hexDigits) { + if (data == null || hexDigits == null) return null; + try { + int len = data.length; + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + builder.append(hexDigits[(data[i] & 0xf0) >>> 4]); + builder.append(hexDigits[data[i] & 0x0f]); + } + return builder.toString(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toHexString"); + } + return null; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isSpace(final String str) { + if (str == null) return true; + for (int i = 0, len = str.length(); i < len; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java b/lib/DevJava/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java new file mode 100644 index 0000000000..6e0ddf1ec0 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java @@ -0,0 +1,78 @@ +package dev.utils.common.encrypt; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import dev.utils.JCLogUtils; + +/** + * detail: 3DES 对称加密工具类 + * @author Ttt + *
+ *     Triple DES、DESede 进行了三重 DES 加密的算法 ( 对称加密算法 )
+ * 
+ */ +public final class TripleDESUtils { + + private TripleDESUtils() { + } + + // 日志 TAG + private static final String TAG = TripleDESUtils.class.getSimpleName(); + + /** + * 生成密钥 + * @return 密钥 byte[] + */ + public static byte[] initKey() { + try { + KeyGenerator keyGen = KeyGenerator.getInstance("DESede"); + keyGen.init(168); // 112 168 + SecretKey secretKey = keyGen.generateKey(); + return secretKey.getEncoded(); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "initKey"); + } + return null; + } + + /** + * 3DES 加密 + * @param data 待加密数据 + * @param key 密钥 + * @return 加密后的 byte[] + */ + public static byte[] encrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; + try { + SecretKey secretKey = new SecretKeySpec(key, "DESede"); + Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + return cipher.doFinal(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "encrypt"); + } + return null; + } + + /** + * 3DES 解密 + * @param data 待加密数据 + * @param key 密钥 + * @return 解密后的 byte[] + */ + public static byte[] decrypt(final byte[] data, final byte[] key) { + if (data == null || key == null) return null; + try { + SecretKey secretKey = new SecretKeySpec(key, "DESede"); + Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return cipher.doFinal(data); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "decrypt"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/encrypt/XorUtils.java b/lib/DevJava/src/main/java/dev/utils/common/encrypt/XorUtils.java new file mode 100644 index 0000000000..b3de44a8cb --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/encrypt/XorUtils.java @@ -0,0 +1,77 @@ +package dev.utils.common.encrypt; + +/** + * detail: 异或 ( 加密 ) 工具类 + * @author Ttt + *
+ *     位运算可以实现很多高级、高效的运算
+ *     可用于 IM 二进制数据包加密
+ *     1. 能够实现加密
+ *     2. 采用异或加密算法不会改变二进制数据的长度这对二进制数据包封包起到不小的好处
+ *     也可用于记事本等场景
+ *     

+ * 参考链接 + * @see + *
+ */ +public final class XorUtils { + + private XorUtils() { + } + + /** + * 加解密 ( 固定 Key 方式 ) 这种方式 加解密 方法共用 + *
+     *     加密: byte[] bytes = encryptAsFix("123".getBytes());
+     *     解密: String str = new String(encryptAsFix(bytes));
+     * 
+ * @param data 待加解密数据 + * @return 加解密后的数据 byte[] + */ + public static byte[] encryptAsFix(final byte[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + int key = 0x12; + for (int i = 0; i < len; i++) { + data[i] ^= key; + } + return data; + } + + // = + + /** + * 加密 ( 非固定 Key 方式 ) + * @param data 待加密数据 + * @return 加密后的数据 byte[] + */ + public static byte[] encrypt(final byte[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + int key = 0x12; + for (int i = 0; i < len; i++) { + data[i] = (byte) (data[i] ^ key); + key = data[i]; + } + return data; + } + + /** + * 解密 ( 非固定 Key 方式 ) + * @param data 待解密数据 + * @return 解密后的数据 byte[] + */ + public static byte[] decrypt(final byte[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + int key = 0x12; + for (int i = len - 1; i > 0; i--) { + data[i] = (byte) (data[i] ^ data[i - 1]); + } + data[0] = (byte) (data[0] ^ key); + return data; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/random/AliasMethod.java b/lib/DevJava/src/main/java/dev/utils/common/random/AliasMethod.java new file mode 100644 index 0000000000..97af6d609f --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/random/AliasMethod.java @@ -0,0 +1,168 @@ +package dev.utils.common.random; + +/** + * File: AliasMethod.java + * Author: Keith Schwarz (htiek@cs.stanford.edu) + * An implementation of the alias method implemented using Vose's algorithm. + * The alias method allows for efficient sampling of random values from a + * discrete probability distribution (i.e. rolling a loaded die) in O(1) time + * each after O(n) preprocessing time. + * For a complete writeup on the alias method, including the intuition and + * important proofs, please see the article "Darts, Dice, and Coins: Smpling + * from a Discrete Distribution" at + * http://www.keithschwarz.com/darts-dice-coins/ + */ + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.Random; + +/** + * detail: 随机概率采样算法 + * @author Keith Schwarz + *
+ *     @see 
+ *     @see 
+ * 
+ */ +public final class AliasMethod { + + /* The random number generator used to sample from the distribution. */ + private final Random random; + + /* The probability and alias tables. */ + private final int[] alias; + private final double[] probability; + + /** + * Constructs a new AliasMethod to sample from a discrete distribution and + * hand back outcomes based on the probability distribution. + *

+ * Given as input a list of probabilities corresponding to outcomes 0, 1, + * ..., n - 1, this constructor creates the probability and alias tables + * needed to efficiently sample from this distribution. + * @param probabilities The list of probabilities. + */ + public AliasMethod(List probabilities) { + this(probabilities, new Random()); + } + + /** + * Constructs a new AliasMethod to sample from a discrete distribution and + * hand back outcomes based on the probability distribution. + *

+ * Given as input a list of probabilities corresponding to outcomes 0, 1, + * ..., n - 1, along with the random number generator that should be used + * as the underlying generator, this constructor creates the probability + * and alias tables needed to efficiently sample from this distribution. + * @param probabilities The list of probabilities. + * @param random The random number generator + */ + public AliasMethod(List probabilities, Random random) { + /* Begin by doing basic structural checks on the inputs. */ + if (probabilities == null || random == null) + throw new NullPointerException(); + if (probabilities.size() == 0) + throw new IllegalArgumentException("Probability vector must be nonempty."); + + /* Allocate space for the probability and alias tables. */ + probability = new double[probabilities.size()]; + alias = new int[probabilities.size()]; + + /* Store the underlying generator. */ + this.random = random; + + /* Compute the average probability and cache it for later use. */ + final double average = 1.0 / probabilities.size(); + + /* Make a copy of the probabilities list, since we will be making + * changes to it. + */ + probabilities = new ArrayList(probabilities); + + /* Create two stacks to act as worklists as we populate the tables. */ + Deque small = new ArrayDeque<>(); + Deque large = new ArrayDeque<>(); + + /* Populate the stacks with the input probabilities. */ + for (int i = 0; i < probabilities.size(); ++i) { + /* If the probability is below the average probability, then we add + * it to the small list; otherwise we add it to the large list. + */ + if (probabilities.get(i) >= average) + large.add(i); + else + small.add(i); + } + + /* As a note: in the mathematical specification of the algorithm, we + * will always exhaust the small list before the big list. However, + * due to floating point inaccuracies, this is not necessarily true. + * Consequently, this inner loop (which tries to pair small and large + * elements) will have to check that both lists aren't empty. + */ + while (!small.isEmpty() && !large.isEmpty()) { + /* Get the index of the small and the large probabilities. */ + int less = small.removeLast(); + int more = large.removeLast(); + + /* These probabilities have not yet been scaled up to be such that + * 1/n is given weight 1.0. We do this here instead. + */ + probability[less] = probabilities.get(less) * probabilities.size(); + alias[less] = more; + + /* Decrease the probability of the larger one by the appropriate + * amount. + */ + probabilities.set(more, + (probabilities.get(more) + probabilities.get(less)) - average); + + /* If the new probability is less than the average, add it into the + * small list; otherwise add it to the large list. + */ + if (probabilities.get(more) >= 1.0 / probabilities.size()) + large.add(more); + else + small.add(more); + } + + /* At this point, everything is in one list, which means that the + * remaining probabilities should all be 1/n. Based on this, set them + * appropriately. Due to numerical issues, we can't be sure which + * stack will hold the entries, so we empty both. + */ + while (!small.isEmpty()) + probability[small.removeLast()] = 1.0; + while (!large.isEmpty()) + probability[large.removeLast()] = 1.0; + } + + /** + * 获取随机索引 ( 对应几率索引 ) + * Samples a value from the underlying distribution. + * @return A random value sampled from the underlying distribution. + */ + public int next() { + /* Generate a fair die roll to determine which column to inspect. */ + int column = random.nextInt(probability.length); + + /* Generate a biased coin toss to determine which option to pick. */ + boolean coinToss = random.nextDouble() < probability[column]; + + /* Based on the outcome, return either the column or its alias. */ + return coinToss ? column : alias[column]; + } + +// public static void main(String[] args) { +// // 使用方法 +// List lists = new ArrayList<>(); +// lists.add(0.15); // 0.15 几率 +// lists.add(0.85); // 0.85 几率 +// AliasMethod aliasMethod = new AliasMethod(lists); +// int result = aliasMethod.next(); +// // result 0 = 0.15, 1 = 0.85 +// } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/thread/DevThreadManager.java b/lib/DevJava/src/main/java/dev/utils/common/thread/DevThreadManager.java new file mode 100644 index 0000000000..7ad104f776 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/thread/DevThreadManager.java @@ -0,0 +1,102 @@ +package dev.utils.common.thread; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * detail: 线程池管理工具类 + * @author Ttt + */ +public final class DevThreadManager { + + private DevThreadManager() { + } + + // 默认通用线程池 ( 通过 CPU 自动处理 ) + private static final DevThreadPool sDevThreadPool = new DevThreadPool(DevThreadPool.DevThreadPoolType.CALC_CPU); + // 线程池数据 + private static final LinkedHashMap sThreadMaps = new LinkedHashMap<>(); + // 配置数据 + private static final Map sConfigMaps = new HashMap<>(); + + /** + * 获取 DevThreadManager 实例 + * @param threadNumber 线程数量 + * @return {@link DevThreadPool} + */ + public static synchronized DevThreadPool getInstance(final int threadNumber) { + // 初始化 key + String key = "n_" + threadNumber; + // 如果不为 null, 则直接返回 + DevThreadPool devThreadPool = sThreadMaps.get(key); + if (devThreadPool != null) { + return devThreadPool; + } + devThreadPool = new DevThreadPool(threadNumber); + sThreadMaps.put(key, devThreadPool); + return devThreadPool; + } + + /** + * 获取 DevThreadManager 实例 + * @param key 线程配置 key {@link DevThreadPool.DevThreadPoolType} or int-Integer + * @return {@link DevThreadPool} + */ + public static synchronized DevThreadPool getInstance(final String key) { + // 如果不为 null, 则直接返回 + DevThreadPool devThreadPool = sThreadMaps.get(key); + if (devThreadPool != null) { + return devThreadPool; + } + Object object = sConfigMaps.get(key); + if (object != null) { + try { + // 判断是否属于线程池类型 + if (object instanceof DevThreadPool.DevThreadPoolType) { + devThreadPool = new DevThreadPool((DevThreadPool.DevThreadPoolType) object); + } else if (object instanceof Integer) { + devThreadPool = new DevThreadPool((Integer) object); + } else { // 其他类型, 统一转换 Integer + devThreadPool = new DevThreadPool(Integer.parseInt((String) object)); + } + if (devThreadPool != null) { + sThreadMaps.put(key, devThreadPool); + return devThreadPool; + } + } catch (Exception e) { + return sDevThreadPool; + } + } + return sDevThreadPool; + } + + // = + + /** + * 初始化配置信息 + * @param mapConfigs 线程配置信息 Map + */ + public static void initConfig(final Map mapConfigs) { + if (mapConfigs != null) { + sConfigMaps.putAll(mapConfigs); + } + } + + /** + * 添加配置信息 + * @param key 线程配置 key + * @param value 线程配置 value + */ + public static void putConfig(final String key, final Object value) { + sConfigMaps.put(key, value); + } + + /** + * 移除配置信息 + * @param key 线程配置 key + */ + public static void removeConfig(final String key) { + sConfigMaps.remove(key); + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/thread/DevThreadPool.java b/lib/DevJava/src/main/java/dev/utils/common/thread/DevThreadPool.java new file mode 100644 index 0000000000..2a5e68812f --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/thread/DevThreadPool.java @@ -0,0 +1,456 @@ +package dev.utils.common.thread; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * detail: 线程池 ( 构建类 ) + * @author Ttt + *

+ *     @see 
+ *     

+ * 创建线程池 ( 参数 ) + * 1. 线程池里面管理多少个线程 + * 2. 如果排队满了, 额外的开的线程数 + * 3. 如果线程池没有要执行的任务存活多久 + * 4. 时间的单位 + * 5. 如果 线程池里管理的线程都已经用了, 剩下的任务临时存到 LinkedBlockingQueue 对象中排队 + * public ThreadPoolExecutor(int corePoolSize, + * int maximumPoolSize, + * long keepAliveTime, + * TimeUnit unit, + * BlockingQueue workQueue) { + * this (corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + * Executors.defaultThreadFactory(), defaultHandler); + * } + *
+ */ +public final class DevThreadPool { + + // 线程池对象 + private final ExecutorService mThreadPool; + // 定时任务线程池 + private ScheduledExecutorService mScheduleExec; + + /** + * 构造函数 + * @param threadNumber 线程数量 + */ + public DevThreadPool(int threadNumber) { + // 如果小于等于 0, 则默认使用 1 + if (threadNumber <= 0) { + threadNumber = 1; + } + this.mThreadPool = Executors.newFixedThreadPool(threadNumber); + // 初始化定时器任务 + this.mScheduleExec = Executors.newScheduledThreadPool(threadNumber); + } + + /** + * 构造函数 + * @param threadPool {@link ExecutorService} + */ + public DevThreadPool(final ExecutorService threadPool) { + this.mThreadPool = threadPool; + // 初始化定时器任务 + this.mScheduleExec = Executors.newScheduledThreadPool(getThreads()); + } + + /** + * 构造函数 + * @param devThreadPoolType 线程初始化类型 {@link DevThreadPoolType} + */ + public DevThreadPool(final DevThreadPoolType devThreadPoolType) { + // 初始化定时器任务 + this.mScheduleExec = Executors.newScheduledThreadPool(getThreads()); + // = + if (devThreadPoolType != null) { + switch (devThreadPoolType) { + case SINGLE: + mThreadPool = Executors.newSingleThreadExecutor(); + // 初始化定时器任务 + this.mScheduleExec = Executors.newScheduledThreadPool(1); + break; +// case AUTO_CPU: +// mThreadPool = Executors.newWorkStealingPool(); +// break; + case CALC_CPU: + mThreadPool = Executors.newFixedThreadPool(getThreads()); + break; + case CACHE: + mThreadPool = Executors.newCachedThreadPool(); + break; + default: + mThreadPool = Executors.newFixedThreadPool(getThreads()); + break; + } + } else { + mThreadPool = Executors.newFixedThreadPool(getThreads()); + } + } + + /** + * detail: 线程池初始化枚举类型 + * @author Ttt + *
+     *     @see 
+     *     @see 
+     * 
+ */ + public enum DevThreadPoolType { + + // 如果当前线程意外终止, 会创建一个新线程继续执行任务, 这和我们直接创建线程不同, 也和 newFixedThreadPool(1) 不同 + SINGLE, // newSingleThreadExecutor 获取的是一个单个的线程, 这个线程会保证你的任务执行完成 + + AUTO_CPU, // 根据 CPU 来创建 ( 自定义创建 ) + + CALC_CPU, // 手动计算 CPU 来创建 + + CACHE, // 可缓存线程池 + +// 1 newCachedThreadPool: 创建一个可缓存线程池, 如果线程池长度超过处理需要, 可灵活回收空闲线程, 若无可回收, 则新建线程 +// 2 newFixedThreadPool: 创建一个固定数目的、可重用的线程池 +// 3 newScheduledThreadPool: 创建一个定长线程池, 支持定时及周期性任务执行 +// 4 newSingleThreadExecutor: 创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序 (FIFO、LIFO) 优先级执行 +// 5 newSingleThreadScheduledExcutor: 创建一个单例线程池, 定期或延时执行任务 +// 6 newWorkStealingPool: 创建持有足够线程的线程池来支持给定的并行级别, 并通过使用多个队列, 减少竞争 +// 它需要穿一个并行级别的参数, 如果不传, 则被设定为默认的 CPU 数量 +// 7 ForkJoinPool: 支持大任务分解成小任务的线程池, 这是 Java8 新增线程池, 通常配合 ForkJoinTask 接口的子类 RecursiveAction 或 RecursiveTask 使用 + } + + // = + + /** + * 获取线程数 + * @return {@link DevThreadPool#getCalcThreads()} + */ + public final int getThreads() { + return getCalcThreads(); + } + + /** + * 获取线程数 + * @return 自动计算 CPU 核心数 + */ + public final int getCalcThreads() { + // 获取 CPU 核心数 + int cpuNumber = Runtime.getRuntime().availableProcessors(); + // 如果小于等于 5, 则返回 5 + if (cpuNumber <= 5) { + return 5; + } else { // 大于 5 的情况 + if (cpuNumber * 2 + 1 >= 10) { // 防止线程数量过大, 当大于 10 的时候, 返回 10 + return 10; + } else { // 不大于 10 的时候, 默认返回 支持的数量 * 2 + 1 + return cpuNumber * 2 + 1; + } + } + } + + // = + + /** + * 加入到线程池任务队列 + * @param runnable 线程 + */ + public void execute(final Runnable runnable) { + if (mThreadPool != null && runnable != null) { + mThreadPool.execute(runnable); + } + } + + /** + * 加入到线程池任务队列 + * @param runnables 线程集合 + */ + public void execute(final List runnables) { + if (mThreadPool != null && runnables != null) { + for (Runnable command : runnables) { + if (command != null) { + mThreadPool.execute(command); + } + } + } + } + + /** + * 通过反射, 调用某个类的方法 + * @param method 方法 + * @param object 对象 + */ + public void execute(final Method method, final Object object) { + if (mThreadPool != null && method != null && object != null) { + mThreadPool.execute(new Runnable() { + @Override + public void run() { + try { + method.invoke(object); + } catch (Exception ignore) { + } + } + }); + } + } + + // = + + /** + * shutdown 会等待所有提交的任务执行完成, 不管是正在执行还是保存在任务队列中的已提交任务 + * 待以前提交的任务执行完毕后关闭线程池 + * 启动一次顺序关闭, 执行以前提交的任务, 但不接受新任务 + * 如果已经关闭, 则调用没有作用 + */ + public void shutdown() { + if (mThreadPool != null) { + mThreadPool.shutdown(); + } + } + + /** + * shutdownNow 会尝试中断正在执行的任务 ( 其主要是中断一些指定方法如 sleep 方法 ), 并且停止执行等待队列中提交的任务 + * 试图停止所有正在执行的活动任务, 暂停处理正在等待的任务, 并返回等待执行的任务列表 + * 无法保证能够停止正在处理的活动执行任务, 但是会尽力尝试 + * @return {@link List} + */ + public List shutdownNow() { + if (mThreadPool != null) { + return mThreadPool.shutdownNow(); + } + return null; + } + + /** + * 判断线程池是否已关闭 - isShutDown 当调用 shutdown() 方法后返回为 true + * @return {@code true} yes, {@code false} no + */ + public boolean isShutdown() { + if (mThreadPool != null) { + return mThreadPool.isShutdown(); + } + return false; + } + + /** + * 若关闭后所有任务都已完成, 则返回 true + * 注意除非首先调用 shutdown 或 shutdownNow, 否则 isTerminated 永不为 true + * isTerminated 当调用 shutdown() 方法后, 并且所有提交的任务完成后返回为 true + * @return {@code true} yes, {@code false} no + */ + public boolean isTerminated() { + if (mThreadPool != null) { + return mThreadPool.isTerminated(); + } + return false; + } + + /** + * 请求关闭、发生超时或者当前线程中断 + * 无论哪一个首先发生之后, 都将导致阻塞, 直到所有任务完成执行 + * @param timeout 最长等待时间 + * @param unit 时间单位 + * @return {@code true} 请求成功, {@code false} 请求超时 + * @throws InterruptedException 终端异常 + */ + public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException { + if (mThreadPool != null && unit != null) { + return mThreadPool.awaitTermination(timeout, unit); + } + return false; + } + + /** + * 提交一个 Callable 任务用于执行 + * 如果想立即阻塞任务的等待, 则可以使用 {@code result = threadPool.submit(aCallable).get();} 形式的构造 + * @param task 任务 + * @param 泛型 + * @return 表示任务等待完成的 Future, 该 Future 的 {@code get} 方法在成功完成时将会返回该任务的结果 + */ + public Future submit(final Callable task) { + if (mThreadPool != null && task != null) { + return mThreadPool.submit(task); + } + return null; + } + + /** + * 提交一个 Runnable 任务用于执行 + * @param task 任务 + * @param result 返回的结果 + * @param 泛型 + * @return 表示任务等待完成的 Future, 该 Future 的 {@code get} 方法在成功完成时将会返回该任务的结果 + */ + public Future submit(final Runnable task, final T result) { + if (mThreadPool != null && task != null) { + return mThreadPool.submit(task, result); + } + return null; + } + + /** + * 提交一个 Runnable 任务用于执行 + * @param task 任务 + * @param 未知类型 + * @return 表示任务等待完成的 Future, 该 Future 的 {@code get} 方法在成功完成时将会返回 null 结果 + */ + public Future submit(final Runnable task) { + if (mThreadPool != null && task != null) { + return mThreadPool.submit(task); + } + return null; + } + + /** + * 执行给定的任务 + * 当所有任务完成时, 返回保持任务状态和结果的 Future 列表 + * 返回列表的所有元素的 {@link Future#isDone} 为 {@code true} + * 注意, 可以正常地或通过抛出异常来终止已完成任务 + * 如果正在进行此操作时修改了给定的 collection, 则此方法的结果是不确定的 + * @param tasks 任务集合 + * @param 泛型 + * @return 表示任务的 Future 列表, 列表顺序与给定任务列表的迭代器所生成的顺序相同, 每个任务都已完成 + * @throws InterruptedException 如果等待时发生中断, 在这种情况下取消尚未完成的任务 + */ + public List> invokeAll(final Collection> tasks) throws InterruptedException { + if (mThreadPool != null && tasks != null) { + return mThreadPool.invokeAll(tasks); + } + return null; + } + + /** + * 执行给定的任务 + * 当所有任务完成或超时期满时 ( 无论哪个首先发生 ), 返回保持任务状态和结果的 Future 列表 + * 返回列表的所有元素的 {@link Future#isDone} 为 {@code true} + * 一旦返回后, 即取消尚未完成的任务 + * 注意, 可以正常地或通过抛出异常来终止已完成任务 + * 如果此操作正在进行时修改了给定的 collection, 则此方法的结果是不确定的 + * @param tasks 任务集合 + * @param timeout 最长等待时间 + * @param unit 时间单位 + * @param 泛型 + * @return 表示任务的 Future 列表, 列表顺序与给定任务列表的迭代器所生成的顺序相同 + * 如果操作未超时, 则已完成所有任务, 如果确实超时了, 则某些任务尚未完成 + * @throws InterruptedException 如果等待时发生中断, 在这种情况下取消尚未完成的任务 + */ + public List> invokeAll(final Collection> tasks, final long timeout, + final TimeUnit unit) throws InterruptedException { + if (mThreadPool != null && tasks != null && unit != null) { + return mThreadPool.invokeAll(tasks, timeout, unit); + } + return null; + } + + /** + * 执行给定的任务 + * 如果某个任务已成功完成 ( 也就是未抛出异常 ), 则返回其结果 + * 一旦正常或异常返回后, 则取消尚未完成的任务 + * 如果此操作正在进行时修改了给定的 collection, 则此方法的结果是不确定的 + * @param tasks 任务集合 + * @param 泛型 + * @return 某个任务返回的结果 + * @throws InterruptedException 如果等待时发生中断 + * @throws ExecutionException 如果没有任务成功完成 + */ + public T invokeAny(final Collection> tasks) throws InterruptedException, ExecutionException { + if (mThreadPool != null && tasks != null) { + return mThreadPool.invokeAny(tasks); + } + return null; + } + + /** + * 执行给定的任务 + * 如果在给定的超时期满前某个任务已成功完成 ( 也就是未抛出异常 ), 则返回其结果 + * 一旦正常或异常返回后, 则取消尚未完成的任务 + * 如果此操作正在进行时修改了给定的 collection, 则此方法的结果是不确定的 + * @param tasks 任务集合 + * @param timeout 最长等待时间 + * @param unit 时间单位 + * @param 泛型 + * @return 某个任务返回的结果 + * @throws InterruptedException 如果等待时发生中断 + * @throws ExecutionException 如果没有任务成功完成 + * @throws TimeoutException 如果在所有任务成功完成之前给定的超时期满 + */ + public T invokeAny(final Collection> tasks, final long timeout, final TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + if (mThreadPool != null && tasks != null && unit != null) { + return mThreadPool.invokeAny(tasks, timeout, unit); + } + return null; + } + + // = + + /** + * 延迟执行 Runnable 命令 + * @param command 命令 + * @param delay 延迟时间 + * @param unit 单位 + * @param 未知类型 + * @return 表示挂起任务完成的 ScheduledFuture, 并且其 {@code get()} 方法在完成后将返回 {@code null} + */ + public ScheduledFuture schedule(final Runnable command, final long delay, final TimeUnit unit) { + if (mScheduleExec != null && command != null && unit != null) { + return mScheduleExec.schedule(command, delay, unit); + } + return null; + } + + /** + * 延迟执行 Callable 命令 + * @param callable 命令 + * @param delay 延迟时间 + * @param unit 时间单位 + * @param 泛型 + * @return 可用于提取结果或取消的 ScheduledFuture + */ + public ScheduledFuture schedule(final Callable callable, final long delay, final TimeUnit unit) { + if (mScheduleExec != null && callable != null && unit != null) { + return mScheduleExec.schedule(callable, delay, unit); + } + return null; + } + + /** + * 延迟并循环执行命令 + * @param command 命令 + * @param initialDelay 首次执行的延迟时间 + * @param period 连续执行之间的周期 + * @param unit 时间单位 + * @param 未知类型 + * @return 表示挂起任务完成的 ScheduledFuture, 并且其 {@code get()} 方法在取消后将抛出异常 + */ + public ScheduledFuture scheduleWithFixedRate(final Runnable command, final long initialDelay, final long period, final TimeUnit unit) { + if (mScheduleExec != null && command != null && unit != null) { + return mScheduleExec.scheduleAtFixedRate(command, initialDelay, period, unit); + } + return null; + } + + /** + * 延迟并以固定休息时间循环执行命令 + * @param command 命令 + * @param initialDelay 首次执行的延迟时间 + * @param delay 每一次执行终止和下一次执行开始之间的延迟 + * @param unit 时间单位 + * @param 未知类型 + * @return 表示挂起任务完成的 ScheduledFuture, 并且其 {@code get()} 方法在取消后将抛出异常 + */ + public ScheduledFuture scheduleWithFixedDelay(final Runnable command, final long initialDelay, final long delay, final TimeUnit unit) { + if (mScheduleExec != null && command != null && unit != null) { + return mScheduleExec.scheduleWithFixedDelay(command, initialDelay, delay, unit); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/validator/BankCheckUtils.java b/lib/DevJava/src/main/java/dev/utils/common/validator/BankCheckUtils.java new file mode 100644 index 0000000000..8f81721747 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/validator/BankCheckUtils.java @@ -0,0 +1,741 @@ +package dev.utils.common.validator; + +import dev.utils.JCLogUtils; + +/** + * detail: 银行卡管理工具类 + * @author AbrahamCaiJin + * @author Ttt ( 重写、规范注释、逻辑判断等 ) + *
+ *     当你输入信用卡号码的时候, 有没有担心输错了而造成损失呢, 其实可以不必这么担心
+ *     因为并不是一个随便的信用卡号码都是合法的, 它必须通过 Luhn 算法来验证通过
+ *     

+ * 该校验的过程: + * 1、从卡号最后一位数字开始, 逆向将奇数位 (1、3、5 等等 ) 相加 + * 2、从卡号最后一位数字开始, 逆向将偶数位数字, 先乘以 2 ( 如果乘积为两位数, 则将其减去 9), 再求和 + * 3、将奇数位总和加上偶数位总和, 结果应该可以被 10 整除 + *

+ * 例如, 卡号是: 5432123456788881 + * 则奇数、偶数位 ( 用红色标出 ) 分布: 5432123456788881 + * 奇数位和 = 35 + * 偶数位乘以 2 ( 有些要减去 9) 的结果: 1 6 2 6 1 5 7 7, 求和 = 35 + * 最后 35 + 35 = 70 可以被 10 整除, 认定校验通过 + *
+ */ +public final class BankCheckUtils { + + private BankCheckUtils() { + } + + // 日志 TAG + private static final String TAG = BankCheckUtils.class.getSimpleName(); + + /** + * 校验银行卡卡号是否合法 + * @param cardId 待校验银行卡号 + * @return {@code true} yes, {@code false} no + */ + public static boolean checkBankCard(final String cardId) { + if (cardId == null || cardId.trim().length() == 0) return false; + try { + char bit = getBankCardCheckCode(cardId.substring(0, cardId.length() - 1)); + if (bit == 'N') return false; + return (cardId.charAt(cardId.length() - 1) == bit); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "checkBankCard"); + return false; + } + } + + /** + * 从不含校验位的银行卡卡号采用 Luhm 校验算法获取校验位 + * @param nonCheckCodeCardId 待校验银行卡号 + * @return 银行卡校验位 + */ + public static char getBankCardCheckCode(final String nonCheckCodeCardId) { + try { + if (nonCheckCodeCardId == null + || nonCheckCodeCardId.trim().length() == 0 + || !nonCheckCodeCardId.matches("\\d+")) { + // 如果传的不是数据返回 N + return 'N'; + } + char[] chs = nonCheckCodeCardId.trim().toCharArray(); + int luhmSum = 0; + int len = chs.length; + for (int i = len - 1, j = 0; i >= 0; i--, j++) { + int k = chs[i] - '0'; + if (j % 2 == 0) { + k *= 2; + k = k / 10 + k % 10; + } + luhmSum += k; + } + return (luhmSum % 10 == 0) ? '0' : (char) ((10 - luhmSum % 10) + '0'); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getBankCardCheckCode"); + return 'N'; + } + } + + /** + * 通过银行卡的 前六位确定 判断银行开户行及卡种 + * @param cardBin 待校验银行卡号 + * @return 银行开户行及卡种 + */ + public static String getNameOfBank(final String cardBin) { + if (cardBin == null || cardBin.trim().length() < 6) return ""; + try { + // 通过银行卡的前六位确定 + String cardBin6 = cardBin.trim().substring(0, 6); + int index = -1; + for (int i = 0, len = BANK_BIN.length; i < len; i++) { + if (cardBin6.equals(BANK_BIN[i])) { + index = i; + } + } + if (index == -1) { + return ""; + } + return BANK_NAME[index]; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getNameOfBank"); + return ""; + } + } + + // = + + /* + * 银行卡是由「发卡行标识代码 + 自定义 + 校验码」等部分组成的 BIN 号 银联标准卡与以往发行的银行卡最直接的区别就是其卡号前六位数字的不同 + * 银行卡卡号的前六位是用来表示发卡银行或机构的, 称为「发卡行识别码」(Bank Identification Number, 缩写为「BIN」) + * 银联标准卡是由国内各家商业银行 ( 含邮储、信用社 ) 共同发行、符合银联业务规范和技术标准、卡正面右下角带有「银联」标识 + * ( 目前新发行的银联标准卡一定带有国际化的银联新标识, 新发的非银联标准卡使用旧的联网通用银联标识 )、卡号前六位为 622126 至 622925 之一的银行卡 + * 是中国银行卡产业共有的民族品牌 + */ + + // BIN 号 + private static final String[] BANK_BIN = {"621098", "622150", "622151", + "622181", "622188", "955100", "621095", "620062", "621285", + "621798", "621799", "621797", "620529", "622199", "621096", + "621622", "623219", "621674", "623218", "621599", "370246", + "370248", "370249", "427010", "427018", "427019", "427020", + "427029", "427030", "427039", "370247", "438125", "438126", + "451804", "451810", "451811", "458071", "489734", "489735", + "489736", "510529", "427062", "524091", "427064", "530970", + "530990", "558360", "620200", "620302", "620402", "620403", + "620404", "524047", "620406", "620407", "525498", "620409", + "620410", "620411", "620412", "620502", "620503", "620405", + "620408", "620512", "620602", "620604", "620607", "620611", + "620612", "620704", "620706", "620707", "620708", "620709", + "620710", "620609", "620712", "620713", "620714", "620802", + "620711", "620904", "620905", "621001", "620902", "621103", + "621105", "621106", "621107", "621102", "621203", "621204", + "621205", "621206", "621207", "621208", "621209", "621210", + "621302", "621303", "621202", "621305", "621306", "621307", + "621309", "621311", "621313", "621211", "621315", "621304", + "621402", "621404", "621405", "621406", "621407", "621408", + "621409", "621410", "621502", "621317", "621511", "621602", + "621603", "621604", "621605", "621608", "621609", "621610", + "621611", "621612", "621613", "621614", "621615", "621616", + "621617", "621607", "621606", "621804", "621807", "621813", + "621814", "621817", "621901", "621904", "621905", "621906", + "621907", "621908", "621909", "621910", "621911", "621912", + "621913", "621915", "622002", "621903", "622004", "622005", + "622006", "622007", "622008", "622010", "622011", "622012", + "621914", "622015", "622016", "622003", "622018", "622019", + "622020", "622102", "622103", "622104", "622105", "622013", + "622111", "622114", "622200", "622017", "622202", "622203", + "622208", "622210", "622211", "622212", "622213", "622214", + "622110", "622220", "622223", "622225", "622229", "622230", + "622231", "622232", "622233", "622234", "622235", "622237", + "622215", "622239", "622240", "622245", "622224", "622303", + "622304", "622305", "622306", "622307", "622308", "622309", + "622238", "622314", "622315", "622317", "622302", "622402", + "622403", "622404", "622313", "622504", "622505", "622509", + "622513", "622517", "622502", "622604", "622605", "622606", + "622510", "622703", "622715", "622806", "622902", "622903", + "622706", "623002", "623006", "623008", "623011", "623012", + "622904", "623015", "623100", "623202", "623301", "623400", + "623500", "623602", "623803", "623901", "623014", "624100", + "624200", "624301", "624402", "62451804", "62451810", "62451811", + "62458071", "623700", "628288", "624000", "628286", "622206", + "621225", "526836", "513685", "543098", "458441", "620058", + "621281", "622246", "900000", "544210", "548943", "370267", + "621558", "621559", "621722", "621723", "620086", "621226", + "402791", "427028", "427038", "548259", "356879", "356880", + "356881", "356882", "528856", "621618", "620516", "621227", + "621721", "900010", "625330", "625331", "625332", "623062", + "622236", "621670", "524374", "550213", "374738", "374739", + "621288", "625708", "625709", "622597", "622599", "360883", + "360884", "625865", "625866", "625899", "621376", "620054", + "620142", "621428", "625939", "621434", "625987", "621761", + "621749", "620184", "621300", "621378", "625114", "622159", + "621720", "625021", "625022", "621379", "620114", "620146", + "621724", "625918", "621371", "620143", "620149", "621414", + "625914", "621375", "620187", "621433", "625986", "621370", + "625925", "622926", "622927", "622928", "622929", "622930", + "622931", "620124", "620183", "620561", "625116", "622227", + "621372", "621464", "625942", "622158", "625917", "621765", + "620094", "620186", "621719", "621719", "621750", "621377", + "620148", "620185", "621374", "621731", "621781", "552599", + "623206", "621671", "620059", "403361", "404117", "404118", + "404119", "404120", "404121", "463758", "514027", "519412", + "519413", "520082", "520083", "558730", "621282", "621336", + "621619", "622821", "622822", "622823", "622824", "622825", + "622826", "622827", "622828", "622836", "622837", "622840", + "622841", "622843", "622844", "622845", "622846", "622847", + "622848", "622849", "623018", "625996", "625997", "625998", + "628268", "625826", "625827", "548478", "544243", "622820", + "622830", "622838", "625336", "628269", "620501", "621660", + "621661", "621662", "621663", "621665", "621667", "621668", + "621669", "621666", "625908", "625910", "625909", "356833", + "356835", "409665", "409666", "409668", "409669", "409670", + "409671", "409672", "456351", "512315", "512316", "512411", + "512412", "514957", "409667", "518378", "518379", "518474", + "518475", "518476", "438088", "524865", "525745", "525746", + "547766", "552742", "553131", "558868", "514958", "622752", + "622753", "622755", "524864", "622757", "622758", "622759", + "622760", "622761", "622762", "622763", "601382", "622756", + "628388", "621256", "621212", "620514", "622754", "622764", + "518377", "622765", "622788", "621283", "620061", "621725", + "620040", "558869", "621330", "621331", "621332", "621333", + "621297", "377677", "621568", "621569", "625905", "625906", + "625907", "628313", "625333", "628312", "623208", "621620", + "621756", "621757", "621758", "621759", "621785", "621786", + "621787", "621788", "621789", "621790", "621672", "625337", + "625338", "625568", "621648", "621248", "621249", "622750", + "622751", "622771", "622772", "622770", "625145", "620531", + "620210", "620211", "622479", "622480", "622273", "622274", + "621231", "621638", "621334", "625140", "621395", "622725", + "622728", "621284", "421349", "434061", "434062", "436728", + "436742", "453242", "491031", "524094", "526410", "544033", + "552245", "589970", "620060", "621080", "621081", "621466", + "621467", "621488", "621499", "621598", "621621", "621700", + "622280", "622700", "622707", "622966", "622988", "625955", + "625956", "553242", "621082", "621673", "623211", "356896", + "356899", "356895", "436718", "436738", "436745", "436748", + "489592", "531693", "532450", "532458", "544887", "552801", + "557080", "558895", "559051", "622166", "622168", "622708", + "625964", "625965", "625966", "628266", "628366", "625362", + "625363", "628316", "628317", "620021", "620521", "405512", + "601428", "405512", "434910", "458123", "458124", "520169", + "522964", "552853", "601428", "622250", "622251", "521899", + "622254", "622255", "622256", "622257", "622258", "622259", + "622253", "622261", "622284", "622656", "628216", "622252", + "66405512", "622260", "66601428", "955590", "955591", "955592", + "955593", "628218", "622262", "621069", "620013", "625028", + "625029", "621436", "621002", "621335", "433670", "433680", + "442729", "442730", "620082", "622690", "622691", "622692", + "622696", "622698", "622998", "622999", "433671", "968807", + "968808", "968809", "621771", "621767", "621768", "621770", + "621772", "621773", "620527", "356837", "356838", "486497", + "622660", "622662", "622663", "622664", "622665", "622666", + "622667", "622669", "622670", "622671", "622672", "622668", + "622661", "622674", "622673", "620518", "621489", "621492", + "620535", "623156", "621490", "621491", "620085", "623155", + "623157", "623158", "623159", "999999", "621222", "623020", + "623021", "623022", "623023", "622630", "622631", "622632", + "622633", "622615", "622616", "622618", "622622", "622617", + "622619", "415599", "421393", "421865", "427570", "427571", + "472067", "472068", "622620", "621691", "545392", "545393", + "545431", "545447", "356859", "356857", "407405", "421869", + "421870", "421871", "512466", "356856", "528948", "552288", + "622600", "622601", "622602", "517636", "622621", "628258", + "556610", "622603", "464580", "464581", "523952", "545217", + "553161", "356858", "622623", "625911", "377152", "377153", + "377158", "377155", "625912", "625913", "356885", "356886", + "356887", "356888", "356890", "402658", "410062", "439188", + "439227", "468203", "479228", "479229", "512425", "521302", + "524011", "356889", "545620", "545621", "545947", "545948", + "552534", "552587", "622575", "622576", "622577", "622579", + "622580", "545619", "622581", "622582", "622588", "622598", + "622609", "690755", "690755", "545623", "621286", "620520", + "621483", "621485", "621486", "628290", "622578", "370285", + "370286", "370287", "370289", "439225", "518710", "518718", + "628362", "439226", "628262", "625802", "625803", "621299", + "966666", "622909", "622908", "438588", "438589", "461982", + "486493", "486494", "486861", "523036", "451289", "527414", + "528057", "622901", "622902", "622922", "628212", "451290", + "524070", "625084", "625085", "625086", "625087", "548738", + "549633", "552398", "625082", "625083", "625960", "625961", + "625962", "625963", "356851", "356852", "404738", "404739", + "456418", "498451", "515672", "356850", "517650", "525998", + "622177", "622277", "622516", "622517", "622518", "622520", + "622521", "622522", "622523", "628222", "628221", "984301", + "984303", "622176", "622276", "622228", "621352", "621351", + "621390", "621792", "625957", "625958", "621791", "620530", + "625993", "622519", "621793", "621795", "621796", "622500", + "623078", "622384", "940034", "940015", "622886", "622391", + "940072", "622359", "940066", "622857", "940065", "621019", + "622309", "621268", "622884", "621453", "622684", "621016", + "621015", "622950", "622951", "621072", "623183", "623185", + "621005", "622172", "622985", "622987", "622267", "622278", + "622279", "622468", "622892", "940021", "621050", "620522", + "356827", "356828", "356830", "402673", "402674", "438600", + "486466", "519498", "520131", "524031", "548838", "622148", + "622149", "622268", "356829", "622300", "628230", "622269", + "625099", "625953", "625350", "625351", "625352", "519961", + "625839", "421317", "602969", "621030", "621420", "621468", + "623111", "422160", "422161", "622865", "940012", "623131", + "622178", "622179", "628358", "622394", "940025", "621279", + "622281", "622316", "940022", "621418", "512431", "520194", + "621626", "623058", "602907", "622986", "622989", "622298", + "622338", "940032", "623205", "621977", "990027", "622325", + "623029", "623105", "621244", "623081", "623108", "566666", + "622455", "940039", "622466", "628285", "622420", "940041", + "623118", "603708", "622993", "623070", "623069", "623172", + "623173", "622383", "622385", "628299", "603506", "603367", + "622878", "623061", "623209", "628242", "622595", "622303", + "622305", "621259", "622596", "622333", "940050", "621439", + "623010", "621751", "628278", "625502", "625503", "625135", + "622476", "621754", "622143", "940001", "623026", "623086", + "628291", "621532", "621482", "622135", "622152", "622153", + "622154", "622996", "622997", "940027", "623099", "623007", + "940055", "622397", "622398", "940054", "622331", "622426", + "625995", "621452", "628205", "628214", "625529", "622428", + "621529", "622429", "621417", "623089", "623200", "940057", + "622311", "623119", "622877", "622879", "621775", "623203", + "603601", "622137", "622327", "622340", "622366", "622134", + "940018", "623016", "623096", "940049", "622425", "622425", + "621577", "622485", "623098", "628329", "621538", "940006", + "621269", "622275", "621216", "622465", "940031", "621252", + "622146", "940061", "621419", "623170", "622440", "940047", + "940017", "622418", "623077", "622413", "940002", "623188", + "622310", "940068", "622321", "625001", "622427", "940069", + "623039", "628273", "622370", "683970", "940074", "621437", + "628319", "990871", "622308", "621415", "623166", "622132", + "621340", "621341", "622140", "623073", "622147", "621633", + "622301", "623171", "621422", "622335", "622336", "622165", + "622315", "628295", "625950", "621760", "622337", "622411", + "623102", "622342", "623048", "622367", "622392", "623085", + "622395", "622441", "622448", "621413", "622856", "621037", + "621097", "621588", "623032", "622644", "623518", "622870", + "622866", "623072", "622897", "628279", "622864", "621403", + "622561", "622562", "622563", "622167", "622777", "621497", + "622868", "622899", "628255", "625988", "622566", "622567", + "622625", "622626", "625946", "628200", "621076", "504923", + "622173", "622422", "622447", "622131", "940076", "621579", + "622876", "622873", "622962", "622936", "623060", "622937", + "623101", "621460", "622939", "622960", "623523", "621591", + "622961", "628210", "622283", "625902", "621010", "622980", + "623135", "621726", "621088", "620517", "622740", "625036", + "621014", "621004", "622972", "623196", "621028", "623083", + "628250", "623121", "621070", "628253", "622979", "621035", + "621038", "621086", "621498", "621296", "621448", "622945", + "621755", "622940", "623120", "628355", "621089", "623161", + "628339", "621074", "621515", "623030", "621345", "621090", + "623178", "621091", "623168", "621057", "623199", "621075", + "623037", "628303", "621233", "621235", "621223", "621780", + "621221", "623138", "628389", "621239", "623068", "621271", + "628315", "621272", "621738", "621273", "623079", "621263", + "621325", "623084", "621327", "621753", "628331", "623160", + "621366", "621388", "621348", "621359", "621360", "621217", + "622959", "621270", "622396", "622511", "623076", "621391", + "621339", "621469", "621625", "623688", "623113", "621601", + "621655", "621636", "623182", "623087", "621696", "622955", + "622478", "940013", "621495", "621688", "623162", "622462", + "628272", "625101", "622323", "623071", "603694", "622128", + "622129", "623035", "623186", "621522", "622271", "940037", + "940038", "985262", "622322", "628381", "622481", "622341", + "940058", "623115", "621258", "621465", "621528", "622328", + "940062", "625288", "623038", "625888", "622332", "940063", + "623123", "622138", "621066", "621560", "621068", "620088", + "621067", "622531", "622329", "623103", "622339", "620500", + "621024", "622289", "622389", "628300", "625516", "621516", + "622859", "622869", "623075", "622895", "623125", "622947", + "621561", "623095", "621073", "623109", "621361", "623033", + "623207", "622891", "621363", "623189", "623510", "622995", + "621053", "621230", "621229", "622218", "628267", "621392", + "621481", "621310", "621396", "623251", "628351"}; + + // 发卡行·卡种名称 + private static final String[] BANK_NAME = {"邮储银行·绿卡通", "邮储银行·绿卡银联标准卡", + "邮储银行·绿卡银联标准卡", "邮储银行·绿卡专用卡", "邮储银行·绿卡银联标准卡", "邮储银行·绿卡 (银联卡)", + "邮储银行·绿卡 VIP 卡", "邮储银行·银联标准卡", "邮储银行·中职学生资助卡", "邮政储蓄银行·IC 绿卡通 VIP 卡", + "邮政储蓄银行·IC 绿卡通", "邮政储蓄银行·IC 联名卡", "邮政储蓄银行·IC 预付费卡", "邮储银行·绿卡银联标准卡", + "邮储银行·绿卡通", "邮政储蓄银行·武警军人保障卡", "邮政储蓄银行·中国旅游卡 (金卡)", + "邮政储蓄银行·普通高中学生资助卡", "邮政储蓄银行·中国旅游卡 (普卡)", "邮政储蓄银行·福农卡", + "工商银行·牡丹运通卡金卡", "工商银行·牡丹运通卡金卡", "工商银行·牡丹运通卡金卡", + "工商银行·牡丹 VISA 卡 (单位卡)", "工商银行·牡丹 VISA 信用卡", "工商银行·牡丹 VISA 卡 (单位卡)", + "工商银行·牡丹 VISA 信用卡", "工商银行·牡丹 VISA 信用卡", "工商银行·牡丹 VISA 信用卡", + "工商银行·牡丹 VISA 信用卡", "工商银行·牡丹运通卡普通卡", "工商银行·牡丹 VISA 信用卡", + "工商银行·牡丹 VISA 白金卡", "工商银行·牡丹贷记卡 (银联卡)", "工商银行·牡丹贷记卡 (银联卡)", + "工商银行·牡丹贷记卡 (银联卡)", "工商银行·牡丹贷记卡 (银联卡)", "工商银行·牡丹欧元卡", "工商银行·牡丹欧元卡", + "工商银行·牡丹欧元卡", "工商银行·牡丹万事达国际借记卡", "工商银行·牡丹 VISA 信用卡", "工商银行·海航信用卡", + "工商银行·牡丹 VISA 信用卡", "工商银行·牡丹万事达信用卡", "工商银行·牡丹万事达信用卡", + "工商银行·牡丹万事达信用卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹万事达白金卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·海航信用卡个人普卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·灵通卡", "工商银行·牡丹灵通卡", "工商银行·E 时代卡", "工商银行·E 时代卡", + "工商银行·理财金卡", "工商银行·准贷记卡 (个普)", "工商银行·准贷记卡 (个普)", "工商银行·准贷记卡 (个普)", + "工商银行·准贷记卡 (个普)", "工商银行·准贷记卡 (个普)", "工商银行·牡丹灵通卡", "工商银行·准贷记卡 (商普)", + "工商银行·牡丹卡 (商务卡)", "工商银行·准贷记卡 (商金)", "工商银行·牡丹卡 (商务卡)", "工商银行·贷记卡 (个普)", + "工商银行·牡丹卡 (个人卡)", "工商银行·牡丹卡 (个人卡)", "工商银行·牡丹卡 (个人卡)", "工商银行·牡丹卡 (个人卡)", + "工商银行·贷记卡 (个金)", "工商银行·牡丹交通卡", "工商银行·准贷记卡 (个金)", "工商银行·牡丹交通卡", + "工商银行·贷记卡 (商普)", "工商银行·贷记卡 (商金)", "工商银行·牡丹卡 (商务卡)", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹交通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", + "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹灵通卡", "工商银行·牡丹贷记卡", + "工商银行·牡丹贷记卡", "工商银行·牡丹贷记卡", "工商银行·牡丹贷记卡", "工商银行·牡丹灵通卡", + "工商银行·中央预算单位公务卡", "工商银行·牡丹灵通卡", "工商银行·财政预算单位公务卡", "工商银行·牡丹卡白金卡", + "工商银行·牡丹卡普卡", "工商银行·国航知音牡丹信用卡", "工商银行·国航知音牡丹信用卡", "工商银行·国航知音牡丹信用卡", + "工商银行·国航知音牡丹信用卡", "工商银行·银联标准卡", "工商银行·中职学生资助卡", "工商银行·专用信用消费卡", + "工商银行·牡丹社会保障卡", "中国工商银行·牡丹东航联名卡", "中国工商银行·牡丹东航联名卡", + "中国工商银行·牡丹运通白金卡", "中国工商银行·福农灵通卡", "中国工商银行·福农灵通卡", "工商银行·灵通卡", + "工商银行·灵通卡", "中国工商银行·中国旅行卡", "工商银行·牡丹卡普卡", "工商银行·国际借记卡", + "工商银行·国际借记卡", "工商银行·国际借记卡", "工商银行·国际借记卡", "中国工商银行·牡丹 JCB 信用卡", + "中国工商银行·牡丹 JCB 信用卡", "中国工商银行·牡丹 JCB 信用卡", "中国工商银行·牡丹 JCB 信用卡", + "中国工商银行·牡丹多币种卡", "中国工商银行·武警军人保障卡", "工商银行·预付芯片卡", "工商银行·理财金账户金卡", + "工商银行·灵通卡", "工商银行·牡丹宁波市民卡", "中国工商银行·中国旅游卡", "中国工商银行·中国旅游卡", + "中国工商银行·中国旅游卡", "中国工商银行·借记卡", "中国工商银行·借贷合一卡", "中国工商银行·普通高中学生资助卡", + "中国工商银行·牡丹多币种卡", "中国工商银行·牡丹多币种卡", "中国工商银行·牡丹百夫长信用卡", + "中国工商银行·牡丹百夫长信用卡", "工商银行·工银财富卡", "中国工商银行·中小商户采购卡", + "中国工商银行·中小商户采购卡", "中国工商银行·环球旅行金卡", "中国工商银行·环球旅行白金卡", + "中国工商银行·牡丹工银大来卡", "中国工商银行·牡丹工银大莱卡", "中国工商银行·IC 金卡", "中国工商银行·IC 白金卡", + "中国工商银行·工行 IC 卡 (红卡)", "中国工商银行布鲁塞尔分行·借记卡", "中国工商银行布鲁塞尔分行·预付卡", + "中国工商银行布鲁塞尔分行·预付卡", "中国工商银行金边分行·借记卡", "中国工商银行金边分行·信用卡", + "中国工商银行金边分行·借记卡", "中国工商银行金边分行·信用卡", "中国工商银行加拿大分行·借记卡", + "中国工商银行加拿大分行·借记卡", "中国工商银行加拿大分行·预付卡", "中国工商银行巴黎分行·借记卡", + "中国工商银行巴黎分行·借记卡", "中国工商银行巴黎分行·贷记卡", "中国工商银行法兰克福分行·贷记卡", + "中国工商银行法兰克福分行·借记卡", "中国工商银行法兰克福分行·贷记卡", "中国工商银行法兰克福分行·贷记卡", + "中国工商银行法兰克福分行·借记卡", "中国工商银行法兰克福分行·预付卡", "中国工商银行法兰克福分行·预付卡", + "中国工商银行印尼分行·借记卡", "中国工商银行印尼分行·信用卡", "中国工商银行米兰分行·借记卡", + "中国工商银行米兰分行·预付卡", "中国工商银行米兰分行·预付卡", "中国工商银行阿拉木图子行·借记卡", + "中国工商银行阿拉木图子行·贷记卡", "中国工商银行阿拉木图子行·借记卡", "中国工商银行阿拉木图子行·预付卡", + "中国工商银行万象分行·借记卡", "中国工商银行万象分行·贷记卡", "中国工商银行卢森堡分行·借记卡", + "中国工商银行卢森堡分行·贷记卡", "中国工商银行澳门分行·E 时代卡", "中国工商银行澳门分行·E 时代卡", + "中国工商银行澳门分行·E 时代卡", "中国工商银行澳门分行·理财金账户", "中国工商银行澳门分行·理财金账户", + "中国工商银行澳门分行·理财金账户", "中国工商银行澳门分行·预付卡", "中国工商银行澳门分行·预付卡", + "中国工商银行澳门分行·工银闪付预付卡", "中国工商银行澳门分行·工银银联公司卡", "中国工商银行澳门分行·Diamond", + "中国工商银行阿姆斯特丹·借记卡", "中国工商银行卡拉奇分行·借记卡", "中国工商银行卡拉奇分行·贷记卡", + "中国工商银行新加坡分行·贷记卡", "中国工商银行新加坡分行·贷记卡", "中国工商银行新加坡分行·借记卡", + "中国工商银行新加坡分行·预付卡", "中国工商银行新加坡分行·预付卡", "中国工商银行新加坡分行·借记卡", + "中国工商银行新加坡分行·借记卡", "中国工商银行马德里分行·借记卡", "中国工商银行马德里分行·借记卡", + "中国工商银行马德里分行·预付卡", "中国工商银行马德里分行·预付卡", "中国工商银行伦敦子行·借记卡", + "中国工商银行伦敦子行·工银伦敦借记卡", "中国工商银行伦敦子行·借记卡", "农业银行·金穗贷记卡", "农业银行·中国旅游卡", + "农业银行·普通高中学生资助卡", "农业银行·银联标准卡", "农业银行·金穗贷记卡 (银联卡)", + "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡 (银联卡)", + "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡 (银联卡)", "农业银行·VISA 白金卡", + "农业银行·万事达白金卡", "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡 (银联卡)", + "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡 (银联卡)", "农业银行·金穗贷记卡", "农业银行·中职学生资助卡", + "农业银行·专用惠农卡", "农业银行·武警军人保障卡", "农业银行·金穗校园卡 (银联卡)", "农业银行·金穗星座卡 (银联卡)", + "农业银行·金穗社保卡 (银联卡)", "农业银行·金穗旅游卡 (银联卡)", "农业银行·金穗青年卡 (银联卡)", + "农业银行·复合介质金穗通宝卡", "农业银行·金穗海通卡", "农业银行·退役金卡", "农业银行·金穗贷记卡", + "农业银行·金穗贷记卡", "农业银行·金穗通宝卡 (银联卡)", "农业银行·金穗惠农卡", "农业银行·金穗通宝银卡", + "农业银行·金穗通宝卡 (银联卡)", "农业银行·金穗通宝卡 (银联卡)", "农业银行·金穗通宝卡", + "农业银行·金穗通宝卡 (银联卡)", "农业银行·金穗通宝卡 (银联卡)", "农业银行·金穗通宝钻石卡", "农业银行·掌尚钱包", + "农业银行·银联 IC 卡金卡", "农业银行·银联预算单位公务卡金卡", "农业银行·银联 IC 卡白金卡", "农业银行·金穗公务卡", + "中国农业银行贷记卡·IC 普卡", "中国农业银行贷记卡·IC 金卡", "中国农业银行贷记卡·澳元卡", + "中国农业银行贷记卡·欧元卡", "中国农业银行贷记卡·金穗通商卡", "中国农业银行贷记卡·金穗通商卡", + "中国农业银行贷记卡·银联白金卡", "中国农业银行贷记卡·中国旅游卡", "中国农业银行贷记卡·银联 IC 公务卡", + "宁波市农业银行·市民卡 B 卡", "中国银行·联名卡", "中国银行·个人普卡", "中国银行·个人金卡", "中国银行·员工普卡", + "中国银行·员工金卡", "中国银行·理财普卡", "中国银行·理财金卡", "中国银行·理财银卡", "中国银行·理财白金卡", + "中国银行·中行金融 IC 卡白金卡", "中国银行·中行金融 IC 卡普卡", "中国银行·中行金融 IC 卡金卡", + "中国银行·中银 JCB 卡金卡", "中国银行·中银 JCB 卡普卡", "中国银行·员工普卡", "中国银行·个人普卡", + "中国银行·中银威士信用卡员", "中国银行·中银威士信用卡员", "中国银行·个人白金卡", "中国银行·中银威士信用卡", + "中国银行·长城公务卡", "中国银行·长城电子借记卡", "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", + "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", "中国银行·中银威士信用卡员", + "中国银行·长城万事达信用卡", "中国银行·长城万事达信用卡", "中国银行·长城万事达信用卡", "中国银行·长城万事达信用卡", + "中国银行·长城万事达信用卡", "中国银行·中银奥运信用卡", "中国银行·长城信用卡", "中国银行·长城信用卡", + "中国银行·长城信用卡", "中国银行·长城万事达信用卡", "中国银行·长城公务卡", "中国银行·长城公务卡", + "中国银行·中银万事达信用卡", "中国银行·中银万事达信用卡", "中国银行·长城人民币信用卡", "中国银行·长城人民币信用卡", + "中国银行·长城人民币信用卡", "中国银行·长城信用卡", "中国银行·长城人民币信用卡", "中国银行·长城人民币信用卡", + "中国银行·长城信用卡", "中国银行·银联单币贷记卡", "中国银行·长城信用卡", "中国银行·长城信用卡", + "中国银行·长城信用卡", "中国银行·长城电子借记卡", "中国银行·长城人民币信用卡", "中国银行·银联标准公务卡", + "中国银行·一卡双账户普卡", "中国银行·财互通卡", "中国银行·电子现金卡", "中国银行·长城人民币信用卡", + "中国银行·长城单位信用卡普卡", "中国银行·中银女性主题信用卡", "中国银行·长城单位信用卡金卡", "中国银行·白金卡", + "中国银行·中职学生资助卡", "中国银行·银联标准卡", "中国银行·金融 IC 卡", "中国银行·长城社会保障卡", + "中国银行·世界卡", "中国银行·社保联名卡", "中国银行·社保联名卡", "中国银行·医保联名卡", "中国银行·医保联名卡", + "中国银行·公司借记卡", "中国银行·银联美运顶级卡", "中国银行·长城福农借记卡金卡", "中国银行·长城福农借记卡普卡", + "中国银行·中行金融 IC 卡普卡", "中国银行·中行金融 IC 卡金卡", "中国银行·中行金融 IC 卡白金卡", + "中国银行·长城银联公务 IC 卡白金卡", "中国银行·中银旅游信用卡", "中国银行·长城银联公务 IC 卡金卡", + "中国银行·中国旅游卡", "中国银行·武警军人保障卡", "中国银行·社保联名借记 IC 卡", "中国银行·社保联名借记 IC 卡", + "中国银行·医保联名借记 IC 卡", "中国银行·医保联名借记 IC 卡", "中国银行·借记 IC 个人普卡", + "中国银行·借记 IC 个人金卡", "中国银行·借记 IC 个人普卡", "中国银行·借记 IC 白金卡", "中国银行·借记 IC 钻石卡", + "中国银行·借记 IC 联名卡", "中国银行·普通高中学生资助卡", "中国银行·长城环球通港澳台旅游金卡", + "中国银行·长城环球通港澳台旅游白金卡", "中国银行·中银福农信用卡", "中国银行金边分行·借记卡", + "中国银行雅加达分行·借记卡", "中国银行首尔分行·借记卡", "中国银行澳门分行·人民币信用卡", + "中国银行澳门分行·人民币信用卡", "中国银行澳门分行·中银卡", "中国银行澳门分行·中银卡", "中国银行澳门分行·中银卡", + "中国银行澳门分行·中银银联双币商务卡", "中国银行澳门分行·预付卡", "中国银行澳门分行·澳门中国银行银联预付卡", + "中国银行澳门分行·澳门中国银行银联预付卡", "中国银行澳门分行·熊猫卡", "中国银行澳门分行·财富卡", + "中国银行澳门分行·银联港币卡", "中国银行澳门分行·银联澳门币卡", "中国银行马尼拉分行·双币种借记卡", + "中国银行胡志明分行·借记卡", "中国银行曼谷分行·借记卡", "中国银行曼谷分行·长城信用卡环球通", + "中国银行曼谷分行·借记卡", "建设银行·龙卡准贷记卡", "建设银行·龙卡准贷记卡金卡", "建设银行·中职学生资助卡", + "建设银行·乐当家银卡 VISA", "建设银行·乐当家金卡 VISA", "建设银行·乐当家白金卡", + "建设银行·龙卡普通卡 VISA", "建设银行·龙卡储蓄卡", "建设银行·VISA 准贷记卡 (银联卡)", + "建设银行·VISA 准贷记金卡", "建设银行·乐当家", "建设银行·乐当家", "建设银行·准贷记金卡", + "建设银行·乐当家白金卡", "建设银行·金融复合 IC 卡", "建设银行·银联标准卡", "建设银行·银联理财钻石卡", + "建设银行·金融 IC 卡", "建设银行·理财白金卡", "建设银行·社保 IC 卡", "建设银行·财富卡私人银行卡", + "建设银行·理财金卡", "建设银行·福农卡", "建设银行·武警军人保障卡", "建设银行·龙卡通", "建设银行·银联储蓄卡", + "建设银行·龙卡储蓄卡 (银联卡)", "建设银行·准贷记卡", "建设银行·理财白金卡", "建设银行·理财金卡", + "建设银行·准贷记卡普卡", "建设银行·准贷记卡金卡", "建设银行·龙卡信用卡", "建设银行·建行陆港通龙卡", + "中国建设银行·普通高中学生资助卡", "中国建设银行·中国旅游卡", "中国建设银行·龙卡 JCB 金卡", + "中国建设银行·龙卡 JCB 白金卡", "中国建设银行·龙卡 JCB 普卡", "中国建设银行·龙卡贷记卡公司卡", + "中国建设银行·龙卡贷记卡", "中国建设银行·龙卡国际普通卡 VISA", "中国建设银行·龙卡国际金卡 VISA", + "中国建设银行·VISA 白金信用卡", "中国建设银行·龙卡国际白金卡", "中国建设银行·龙卡国际普通卡 MASTER", + "中国建设银行·龙卡国际金卡 MASTER", "中国建设银行·龙卡万事达金卡", "中国建设银行·龙卡贷记卡", + "中国建设银行·龙卡万事达白金卡", "中国建设银行·龙卡贷记卡", "中国建设银行·龙卡万事达信用卡", + "中国建设银行·龙卡人民币信用卡", "中国建设银行·龙卡人民币信用金卡", "中国建设银行·龙卡人民币白金卡", + "中国建设银行·龙卡 IC 信用卡普卡", "中国建设银行·龙卡 IC 信用卡金卡", "中国建设银行·龙卡 IC 信用卡白金卡", + "中国建设银行·龙卡银联公务卡普卡", "中国建设银行·龙卡银联公务卡金卡", "中国建设银行·中国旅游卡", + "中国建设银行·中国旅游卡", "中国建设银行·龙卡 IC 公务卡", "中国建设银行·龙卡 IC 公务卡", "交通银行·交行预付卡", + "交通银行·世博预付 IC 卡", "交通银行·太平洋互连卡", "交通银行·太平洋万事顺卡", "交通银行·太平洋互连卡 (银联卡)", + "交通银行·太平洋白金信用卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋双币贷记卡", + "交通银行·太平洋白金信用卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋万事顺卡", "交通银行·太平洋人民币贷记卡", + "交通银行·太平洋人民币贷记卡", "交通银行·太平洋双币贷记卡", "交通银行·太平洋准贷记卡", "交通银行·太平洋准贷记卡", + "交通银行·太平洋准贷记卡", "交通银行·太平洋准贷记卡", "交通银行·太平洋借记卡", "交通银行·太平洋借记卡", + "交通银行·太平洋人民币贷记卡", "交通银行·太平洋借记卡", "交通银行·太平洋 MORE 卡", "交通银行·白金卡", + "交通银行·交通银行公务卡普卡", "交通银行·太平洋人民币贷记卡", "交通银行·太平洋互连卡", "交通银行·太平洋借记卡", + "交通银行·太平洋万事顺卡", "交通银行·太平洋贷记卡 (银联卡)", "交通银行·太平洋贷记卡 (银联卡)", + "交通银行·太平洋贷记卡 (银联卡)", "交通银行·太平洋贷记卡 (银联卡)", "交通银行·交通银行公务卡金卡", + "交通银行·交银 IC 卡", "交通银行香港分行·交通银行港币借记卡", "交通银行香港分行·港币礼物卡", + "交通银行香港分行·双币种信用卡", "交通银行香港分行·双币种信用卡", "交通银行香港分行·双币卡", + "交通银行香港分行·银联人民币卡", "交通银行澳门分行·银联借记卡", "中信银行·中信借记卡", "中信银行·中信借记卡", + "中信银行·中信国际借记卡", "中信银行·中信国际借记卡", "中信银行·中国旅行卡", "中信银行·中信借记卡 (银联卡)", + "中信银行·中信借记卡 (银联卡)", "中信银行·中信贵宾卡 (银联卡)", "中信银行·中信理财宝金卡", + "中信银行·中信理财宝白金卡", "中信银行·中信钻石卡", "中信银行·中信钻石卡", "中信银行·中信借记卡", + "中信银行·中信理财宝 (银联卡)", "中信银行·中信理财宝 (银联卡)", "中信银行·中信理财宝 (银联卡)", + "中信银行·借记卡", "中信银行·理财宝 IC 卡", "中信银行·理财宝 IC 卡", "中信银行·理财宝 IC 卡", + "中信银行·理财宝 IC 卡", "中信银行·理财宝 IC 卡", "中信银行·主账户复合电子现金卡", "光大银行·阳光商旅信用卡", + "光大银行·阳光商旅信用卡", "光大银行·阳光商旅信用卡", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", + "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", + "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", + "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", "光大银行·阳光卡 (银联卡)", + "光大银行·阳光卡 (银联卡)", "光大银行·借记卡普卡", "光大银行·社会保障 IC 卡", "光大银行·IC 借记卡普卡", + "光大银行·手机支付卡", "光大银行·联名 IC 卡普卡", "光大银行·借记 IC 卡白金卡", "光大银行·借记 IC 卡金卡", + "光大银行·阳光旅行卡", "光大银行·借记 IC 卡钻石卡", "光大银行·联名 IC 卡金卡", "光大银行·联名 IC 卡白金卡", + "光大银行·联名 IC 卡钻石卡", "华夏银行·华夏卡 (银联卡)", "华夏银行·华夏白金卡", "华夏银行·华夏普卡", + "华夏银行·华夏金卡", "华夏银行·华夏白金卡", "华夏银行·华夏钻石卡", "华夏银行·华夏卡 (银联卡)", + "华夏银行·华夏至尊金卡 (银联卡)", "华夏银行·华夏丽人卡 (银联卡)", "华夏银行·华夏万通卡", + "民生银行·民生借记卡 (银联卡)", "民生银行·民生银联借记卡-金卡", "民生银行·钻石卡", + "民生银行·民生借记卡 (银联卡)", "民生银行·民生借记卡 (银联卡)", "民生银行·民生借记卡 (银联卡)", + "民生银行·民生借记卡", "民生银行·民生国际卡", "民生银行·民生国际卡 (银卡)", "民生银行·民生国际卡 (欧元卡)", + "民生银行·民生国际卡 (澳元卡)", "民生银行·民生国际卡", "民生银行·民生国际卡", "民生银行·薪资理财卡", + "民生银行·借记卡普卡", "民生银行·民生 MasterCard", "民生银行·民生 MasterCard", + "民生银行·民生 MasterCard", "民生银行·民生 MasterCard", "民生银行·民生 JCB 信用卡", + "民生银行·民生 JCB 金卡", "民生银行·民生贷记卡 (银联卡)", "民生银行·民生贷记卡 (银联卡)", + "民生银行·民生贷记卡 (银联卡)", "民生银行·民生贷记卡 (银联卡)", "民生银行·民生贷记卡 (银联卡)", + "民生银行·民生 JCB 普卡", "民生银行·民生贷记卡 (银联卡)", "民生银行·民生贷记卡 (银联卡)", + "民生银行·民生信用卡 (银联卡)", "民生银行·民生信用卡 (银联卡)", "民生银行·民生银联白金信用卡", + "民生银行·民生贷记卡 (银联卡)", "民生银行·民生银联个人白金卡", "民生银行·公务卡金卡", + "民生银行·民生贷记卡 (银联卡)", "民生银行·民生银联商务信用卡", "民生银行·民 VISA 无限卡", + "民生银行·民生 VISA 商务白金卡", "民生银行·民生万事达钛金卡", "民生银行·民生万事达世界卡", + "民生银行·民生万事达白金公务卡", "民生银行·民生 JCB 白金卡", "民生银行·银联标准金卡", "民生银行·银联芯片普卡", + "民生银行·民生运通双币信用卡普卡", "民生银行·民生运通双币信用卡金卡", "民生银行·民生运通双币信用卡钻石卡", + "民生银行·民生运通双币标准信用卡白金卡", "民生银行·银联芯片金卡", "民生银行·银联芯片白金卡", + "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", + "招商银行·招商银行信用卡", "招商银行·两地一卡通", "招商银行·招行国际卡 (银联卡)", "招商银行·招商银行信用卡", + "招商银行·VISA 商务信用卡", "招商银行·招行国际卡 (银联卡)", "招商银行·招商银行信用卡", + "招商银行·招商银行信用卡", "招商银行·招行国际卡 (银联卡)", "招商银行·世纪金花联名信用卡", + "招商银行·招行国际卡 (银联卡)", "招商银行·招商银行信用卡", "招商银行·万事达信用卡", "招商银行·万事达信用卡", + "招商银行·万事达信用卡", "招商银行·万事达信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", + "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", + "招商银行·一卡通 (银联卡)", "招商银行·万事达信用卡", "招商银行·招商银行信用卡", "招商银行·招商银行信用卡", + "招商银行·一卡通 (银联卡)", "招商银行·公司卡 (银联卡)", "招商银行·金卡", "招商银行·招行一卡通", + "招商银行·招行一卡通", "招商银行·万事达信用卡", "招商银行·金葵花卡", "招商银行·电子现金卡", + "招商银行·银联 IC 普卡", "招商银行·银联 IC 金卡", "招商银行·银联金葵花 IC 卡", "招商银行·IC 公务卡", + "招商银行·招商银行信用卡", "招商银行信用卡中心·美国运通绿卡", "招商银行信用卡中心·美国运通金卡", + "招商银行信用卡中心·美国运通商务绿卡", "招商银行信用卡中心·美国运通商务金卡", "招商银行信用卡中心·VISA 信用卡", + "招商银行信用卡中心·MASTER 信用卡", "招商银行信用卡中心·MASTER 信用金卡", + "招商银行信用卡中心·银联标准公务卡 (金卡)", "招商银行信用卡中心·VISA 信用卡", + "招商银行信用卡中心·银联标准财政公务卡", "招商银行信用卡中心·芯片 IC 信用卡", "招商银行信用卡中心·芯片 IC 信用卡", + "招商银行香港分行·香港一卡通", "兴业银行·兴业卡 (银联卡)", "兴业银行·兴业卡 (银联标准卡)", + "兴业银行·兴业自然人生理财卡", "兴业银行·兴业智能卡 (银联卡)", "兴业银行·兴业智能卡", + "兴业银行·visa 标准双币个人普卡", "兴业银行·VISA 商务普卡", "兴业银行·VISA 商务金卡", + "兴业银行·VISA 运动白金信用卡", "兴业银行·万事达信用卡 (银联卡)", "兴业银行·VISA 信用卡 (银联卡)", + "兴业银行·加菲猫信用卡", "兴业银行·个人白金卡", "兴业银行·银联信用卡 (银联卡)", "兴业银行·银联信用卡 (银联卡)", + "兴业银行·银联白金信用卡", "兴业银行·银联标准公务卡", "兴业银行·VISA 信用卡 (银联卡)", + "兴业银行·万事达信用卡 (银联卡)", "兴业银行·银联标准贷记普卡", "兴业银行·银联标准贷记金卡", + "兴业银行·银联标准贷记金卡", "兴业银行·银联标准贷记金卡", "兴业银行·兴业信用卡", "兴业银行·兴业信用卡", + "兴业银行·兴业信用卡", "兴业银行·银联标准贷记普卡", "兴业银行·银联标准贷记普卡", "兴业银行·兴业芯片普卡", + "兴业银行·兴业芯片金卡", "兴业银行·兴业芯片白金卡", "兴业银行·兴业芯片钻石卡", "浦东发展银行·浦发 JCB 金卡", + "浦东发展银行·浦发 JCB 白金卡", "浦东发展银行·信用卡 VISA 普通", "浦东发展银行·信用卡 VISA 金卡", + "浦东发展银行·浦发银行 VISA 年青卡", "浦东发展银行·VISA 白金信用卡", "浦东发展银行·浦发万事达白金卡", + "浦东发展银行·浦发 JCB 普卡", "浦东发展银行·浦发万事达金卡", "浦东发展银行·浦发万事达普卡", + "浦东发展银行·浦发单币卡", "浦东发展银行·浦发银联单币麦兜普卡", "浦东发展银行·东方轻松理财卡", + "浦东发展银行·东方 - 轻松理财卡普卡", "浦东发展银行·东方轻松理财卡", "浦东发展银行·东方轻松理财智业金卡", + "浦东发展银行·东方卡 (银联卡)", "浦东发展银行·东方卡 (银联卡)", "浦东发展银行·东方卡 (银联卡)", + "浦东发展银行·公务卡金卡", "浦东发展银行·公务卡普卡", "浦东发展银行·东方卡", "浦东发展银行·东方卡", + "浦东发展银行·浦发单币卡", "浦东发展银行·浦发联名信用卡", "浦东发展银行·浦发银联白金卡", + "浦东发展银行·轻松理财普卡", "浦东发展银行·移动联名卡", "浦东发展银行·轻松理财消贷易卡", + "浦东发展银行·轻松理财普卡 (复合卡)", "浦东发展银行·贷记卡", "浦东发展银行·贷记卡", + "浦东发展银行·东方借记卡 (复合卡)", "浦东发展银行·电子现金卡 (IC 卡)", "浦东发展银行·移动浦发联名卡", + "浦东发展银行·东方 - 标准准贷记卡", "浦东发展银行·轻松理财金卡 (复合卡)", "浦东发展银行·轻松理财白金卡 (复合卡)", + "浦东发展银行·轻松理财钻石卡 (复合卡)", "浦东发展银行·东方卡", "恒丰银行·九州 IC 卡", + "恒丰银行·九州借记卡 (银联卡)", "恒丰银行·九州借记卡 (银联卡)", "天津市商业银行·银联卡 (银联卡)", + "烟台商业银行·金通卡", "潍坊银行·鸢都卡 (银联卡)", "潍坊银行·鸳都卡 (银联卡)", "临沂商业银行·沂蒙卡 (银联卡)", + "临沂商业银行·沂蒙卡 (银联卡)", "日照市商业银行·黄海卡", "日照市商业银行·黄海卡 (银联卡)", "浙商银行·商卡", + "浙商银行·商卡", "渤海银行·浩瀚金卡", "渤海银行·渤海银行借记卡", "渤海银行·金融 IC 卡", + "渤海银行·渤海银行公司借记卡", "星展银行·星展银行借记卡", "星展银行·星展银行借记卡", "恒生银行·恒生通财卡", + "恒生银行·恒生优越通财卡", "新韩银行·新韩卡", "上海银行·慧通钻石卡", "上海银行·慧通金卡", + "上海银行·私人银行卡", "上海银行·综合保险卡", "上海银行·申卡社保副卡 (有折)", "上海银行·申卡社保副卡 (无折)", + "上海银行·白金 IC 借记卡", "上海银行·慧通白金卡 (配折)", "上海银行·慧通白金卡 (不配折)", + "上海银行·申卡 (银联卡)", "上海银行·申卡借记卡", "上海银行·银联申卡 (银联卡)", "上海银行·单位借记卡", + "上海银行·首发纪念版 IC 卡", "上海银行·申卡贷记卡", "上海银行·申卡贷记卡", "上海银行·J 分期付款信用卡", + "上海银行·申卡贷记卡", "上海银行·申卡贷记卡", "上海银行·上海申卡 IC", "上海银行·申卡贷记卡", + "上海银行·申卡贷记卡普通卡", "上海银行·申卡贷记卡金卡", "上海银行·万事达白金卡", "上海银行·万事达星运卡", + "上海银行·申卡贷记卡金卡", "上海银行·申卡贷记卡普通卡", "上海银行·安融卡", "上海银行·分期付款信用卡", + "上海银行·信用卡", "上海银行·个人公务卡", "上海银行·安融卡", "上海银行·上海银行银联白金卡", + "上海银行·贷记 IC 卡", "上海银行·中国旅游卡 (IC 普卡)", "上海银行·中国旅游卡 (IC 金卡)", + "上海银行·中国旅游卡 (IC 白金卡)", "上海银行·万事达钻石卡", "上海银行·淘宝 IC 普卡", "北京银行·京卡借记卡", + "北京银行·京卡 (银联卡)", "北京银行·京卡借记卡", "北京银行·京卡", "北京银行·京卡", "北京银行·借记 IC 卡", + "北京银行·京卡贵宾金卡", "北京银行·京卡贵宾白金卡", "吉林银行·君子兰一卡通 (银联卡)", + "吉林银行·君子兰卡 (银联卡)", "吉林银行·长白山金融 IC 卡", "吉林银行·信用卡", "吉林银行·信用卡", + "吉林银行·公务卡", "镇江市商业银行·金山灵通卡 (银联卡)", "镇江市商业银行·金山灵通卡 (银联卡)", + "宁波银行·银联标准卡", "宁波银行·汇通借记卡", "宁波银行·汇通卡 (银联卡)", "宁波银行·明州卡", + "宁波银行·汇通借记卡", "宁波银行·汇通国际卡银联双币卡", "宁波银行·汇通国际卡银联双币卡", "平安银行·新磁条借记卡", + "平安银行·平安银行 IC 借记卡", "平安银行·万事顺卡", "平安银行·平安银行借记卡", "平安银行·平安银行借记卡", + "平安银行·万事顺借记卡", "焦作市商业银行·月季借记卡 (银联卡)", "焦作市商业银行·月季城市通 (银联卡)", + "焦作市商业银行·中国旅游卡", "温州银行·金鹿卡", "汉口银行·九通卡 (银联卡)", "汉口银行·九通卡", + "汉口银行·借记卡", "汉口银行·借记卡", "盛京银行·玫瑰卡", "盛京银行·玫瑰 IC 卡", "盛京银行·玫瑰 IC 卡", + "盛京银行·玫瑰卡", "盛京银行·玫瑰卡", "盛京银行·玫瑰卡 (银联卡)", "盛京银行·玫瑰卡 (银联卡)", + "盛京银行·盛京银行公务卡", "洛阳银行·都市一卡通 (银联卡)", "洛阳银行·都市一卡通 (银联卡)", "洛阳银行·--", + "大连银行·北方明珠卡", "大连银行·人民币借记卡", "大连银行·金融 IC 借记卡", "大连银行·大连市社会保障卡", + "大连银行·借记 IC 卡", "大连银行·借记 IC 卡", "大连银行·大连市商业银行贷记卡", "大连银行·大连市商业银行贷记卡", + "大连银行·银联标准公务卡", "苏州市商业银行·姑苏卡", "杭州商业银行·西湖卡", "杭州商业银行·西湖卡", + "杭州商业银行·借记 IC 卡", "杭州商业银行·", "南京银行·梅花信用卡公务卡", "南京银行·梅花信用卡商务卡", + "南京银行·梅花贷记卡 (银联卡)", "南京银行·梅花借记卡 (银联卡)", "南京银行·白金卡", "南京银行·商务卡", + "东莞市商业银行·万顺通卡 (银联卡)", "东莞市商业银行·万顺通卡 (银联卡)", "东莞市商业银行·万顺通借记卡", + "东莞市商业银行·社会保障卡", "乌鲁木齐市商业银行·雪莲借记 IC 卡", "乌鲁木齐市商业银行·乌鲁木齐市公务卡", + "乌鲁木齐市商业银行·福农卡贷记卡", "乌鲁木齐市商业银行·福农卡准贷记卡", "乌鲁木齐市商业银行·雪莲准贷记卡", + "乌鲁木齐市商业银行·雪莲贷记卡 (银联卡)", "乌鲁木齐市商业银行·雪莲借记 IC 卡", + "乌鲁木齐市商业银行·雪莲借记卡 (银联卡)", "乌鲁木齐市商业银行·雪莲卡 (银联卡)", "绍兴银行·兰花 IC 借记卡", + "绍兴银行·社保 IC 借记卡", "绍兴银行·兰花公务卡", "成都商业银行·芙蓉锦程福农卡", "成都商业银行·芙蓉锦程天府通卡", + "成都商业银行·锦程卡 (银联卡)", "成都商业银行·锦程卡金卡", "成都商业银行·锦程卡定活一卡通金卡", + "成都商业银行·锦程卡定活一卡通", "成都商业银行·锦程力诚联名卡", "成都商业银行·锦程力诚联名卡", + "成都商业银行·锦程卡 (银联卡)", "抚顺银行·借记 IC 卡", "临商银行·借记卡", "宜昌市商业银行·三峡卡 (银联卡)", + "宜昌市商业银行·信用卡 (银联卡)", "葫芦岛市商业银行·一通卡", "葫芦岛市商业银行·一卡通 (银联卡)", + "天津市商业银行·津卡", "天津市商业银行·津卡贷记卡 (银联卡)", "天津市商业银行·贷记 IC 卡", "天津市商业银行·--", + "天津银行·商务卡", "宁夏银行·宁夏银行公务卡", "宁夏银行·宁夏银行福农贷记卡", "宁夏银行·如意卡 (银联卡)", + "宁夏银行·宁夏银行福农借记卡", "宁夏银行·如意借记卡", "宁夏银行·如意 IC 卡", "宁夏银行·宁夏银行如意借记卡", + "宁夏银行·中国旅游卡", "齐商银行·金达卡 (银联卡)", "齐商银行·金达借记卡 (银联卡)", "齐商银行·金达 IC 卡", + "徽商银行·黄山卡", "徽商银行·黄山卡", "徽商银行·借记卡", "徽商银行·徽商银行中国旅游卡 (安徽)", + "徽商银行合肥分行·黄山卡", "徽商银行芜湖分行·黄山卡 (银联卡)", "徽商银行马鞍山分行·黄山卡 (银联卡)", + "徽商银行淮北分行·黄山卡 (银联卡)", "徽商银行安庆分行·黄山卡 (银联卡)", "重庆银行·长江卡 (银联卡)", + "重庆银行·长江卡 (银联卡)", "重庆银行·长江卡", "重庆银行·借记 IC 卡", "哈尔滨银行·丁香一卡通 (银联卡)", + "哈尔滨银行·丁香借记卡 (银联卡)", "哈尔滨银行·丁香卡", "哈尔滨银行·福农借记卡", + "无锡市商业银行·太湖金保卡 (银联卡)", "丹东银行·借记 IC 卡", "丹东银行·丹东银行公务卡", "兰州银行·敦煌卡", + "南昌银行·金瑞卡 (银联卡)", "南昌银行·南昌银行借记卡", "南昌银行·金瑞卡", "晋商银行·晋龙一卡通", + "晋商银行·晋龙一卡通", "晋商银行·晋龙卡 (银联卡)", "青岛银行·金桥通卡", "青岛银行·金桥卡 (银联卡)", + "青岛银行·金桥卡 (银联卡)", "青岛银行·金桥卡", "青岛银行·借记 IC 卡", "吉林银行·雾凇卡 (银联卡)", + "吉林银行·雾凇卡 (银联卡)", "南通商业银行·金桥卡 (银联卡)", "南通商业银行·金桥卡 (银联卡)", + "日照银行·黄海卡、财富卡借记卡", "鞍山银行·千山卡 (银联卡)", "鞍山银行·千山卡 (银联卡)", "鞍山银行·千山卡", + "青海银行·三江银行卡 (银联卡)", "青海银行·三江卡", "台州银行·大唐贷记卡", "台州银行·大唐准贷记卡", + "台州银行·大唐卡 (银联卡)", "台州银行·大唐卡", "台州银行·借记卡", "台州银行·公务卡", + "泉州银行·海峡银联卡 (银联卡)", "泉州银行·海峡储蓄卡", "泉州银行·海峡银联卡 (银联卡)", "泉州银行·海峡卡", + "泉州银行·公务卡", "昆明商业银行·春城卡 (银联卡)", "昆明商业银行·春城卡 (银联卡)", + "昆明商业银行·富滇 IC 卡 (复合卡)", "阜新银行·借记 IC 卡", "嘉兴银行·南湖借记卡 (银联卡)", "廊坊银行·白金卡", + "廊坊银行·金卡", "廊坊银行·银星卡 (银联卡)", "廊坊银行·龙凤呈祥卡", "内蒙古银行·百灵卡 (银联卡)", + "内蒙古银行·成吉思汗卡", "湖州市商业银行·百合卡", "湖州市商业银行·", "沧州银行·狮城卡", + "南宁市商业银行·桂花卡 (银联卡)", "包商银行·雄鹰卡 (银联卡)", "包商银行·包头市商业银行借记卡", + "包商银行·雄鹰贷记卡", "包商银行·包商银行内蒙古自治区公务卡", "包商银行·贷记卡", "包商银行·借记卡", + "连云港市商业银行·金猴神通借记卡", "威海商业银行·通达卡 (银联卡)", "威海市商业银行·通达借记 IC 卡", + "攀枝花市商业银行·攀枝花卡 (银联卡)", "攀枝花市商业银行·攀枝花卡", "绵阳市商业银行·科技城卡 (银联卡)", + "泸州市商业银行·酒城卡 (银联卡)", "泸州市商业银行·酒城 IC 卡", "大同市商业银行·云冈卡 (银联卡)", + "三门峡银行·天鹅卡 (银联卡)", "广东南粤银行·南珠卡 (银联卡)", "张家口市商业银行·好运 IC 借记卡", + "桂林市商业银行·漓江卡 (银联卡)", "龙江银行·福农借记卡", "龙江银行·联名借记卡", "龙江银行·福农借记卡", + "龙江银行·龙江 IC 卡", "龙江银行·社会保障卡", "龙江银行·--", "江苏长江商业银行·长江卡", + "徐州市商业银行·彭城借记卡 (银联卡)", "南充市商业银行·借记 IC 卡", "南充市商业银行·熊猫团团卡", + "莱商银行·银联标准卡", "莱芜银行·金凤卡", "莱商银行·借记 IC 卡", "德阳银行·锦程卡定活一卡通", + "德阳银行·锦程卡定活一卡通金卡", "德阳银行·锦程卡定活一卡通", "唐山市商业银行·唐山市城通卡", + "曲靖市商业银行·珠江源卡", "曲靖市商业银行·珠江源 IC 卡", "温州银行·金鹿信用卡", "温州银行·金鹿信用卡", + "温州银行·金鹿公务卡", "温州银行·贷记 IC 卡", "汉口银行·汉口银行贷记卡", "汉口银行·汉口银行贷记卡", + "汉口银行·九通香港旅游贷记普卡", "汉口银行·九通香港旅游贷记金卡", "汉口银行·贷记卡", "汉口银行·九通公务卡", + "江苏银行·聚宝借记卡", "江苏银行·月季卡", "江苏银行·紫金卡", "江苏银行·绿扬卡 (银联卡)", + "江苏银行·月季卡 (银联卡)", "江苏银行·九州借记卡 (银联卡)", "江苏银行·月季卡 (银联卡)", + "江苏银行·聚宝惠民福农卡", "江苏银行·江苏银行聚宝 IC 借记卡", "江苏银行·聚宝 IC 借记卡 VIP 卡", + "长治市商业银行·长治商行银联晋龙卡", "承德市商业银行·热河卡", "承德银行·借记 IC 卡", "德州银行·长河借记卡", + "德州银行·--", "遵义市商业银行·社保卡", "遵义市商业银行·尊卡", "邯郸市商业银行·邯银卡", + "邯郸市商业银行·邯郸银行贵宾 IC 借记卡", "安顺市商业银行·黄果树福农卡", "安顺市商业银行·黄果树借记卡", + "江苏银行·紫金信用卡 (公务卡)", "江苏银行·紫金信用卡", "江苏银行·天翼联名信用卡", "平凉市商业银行·广成卡", + "玉溪市商业银行·红塔卡", "玉溪市商业银行·红塔卡", "浙江民泰商业银行·金融 IC 卡", "浙江民泰商业银行·民泰借记卡", + "浙江民泰商业银行·金融 IC 卡 C 卡", "浙江民泰商业银行·银联标准普卡金卡", "浙江民泰商业银行·商惠通", + "上饶市商业银行·三清山卡", "东营银行·胜利卡", "泰安市商业银行·岱宗卡", "泰安市商业银行·市民一卡通", + "浙江稠州商业银行·义卡", "浙江稠州商业银行·义卡借记 IC 卡", "浙江稠州商业银行·公务卡", "自贡市商业银行·借记 IC 卡", + "自贡市商业银行·锦程卡", "鄂尔多斯银行·天骄公务卡", "鹤壁银行·鹤卡", "许昌银行·连城卡", "铁岭银行·龙凤卡", + "乐山市商业银行·大福卡", "乐山市商业银行·--", "长安银行·长长卡", "长安银行·借记 IC 卡", + "重庆三峡银行·财富人生卡", "重庆三峡银行·借记卡", "石嘴山银行·麒麟借记卡", "石嘴山银行·麒麟借记卡", + "石嘴山银行·麒麟公务卡", "盘锦市商业银行·鹤卡", "盘锦市商业银行·盘锦市商业银行鹤卡", "平顶山银行·平顶山银行公务卡", + "朝阳银行·鑫鑫通卡", "朝阳银行·朝阳银行福农卡", "朝阳银行·红山卡", "宁波东海银行·绿叶卡", + "遂宁市商业银行·锦程卡", "遂宁是商业银行·金荷卡", "保定银行·直隶卡", "保定银行·直隶卡", + "凉山州商业银行·锦程卡", "凉山州商业银行·金凉山卡", "漯河银行·福卡", "漯河银行·福源卡", "漯河银行·福源公务卡", + "达州市商业银行·锦程卡", "新乡市商业银行·新卡", "晋中银行·九州方圆借记卡", "晋中银行·九州方圆卡", + "驻马店银行·驿站卡", "驻马店银行·驿站卡", "驻马店银行·公务卡", "衡水银行·金鼎卡", "衡水银行·借记 IC 卡", + "周口银行·如愿卡", "周口银行·公务卡", "阳泉市商业银行·金鼎卡", "阳泉市商业银行·金鼎卡", + "宜宾市商业银行·锦程卡", "宜宾市商业银行·借记 IC 卡", "库尔勒市商业银行·孔雀胡杨卡", "雅安市商业银行·锦城卡", + "雅安市商业银行·--", "安阳银行·安鼎卡", "信阳银行·信阳卡", "信阳银行·公务卡", "信阳银行·信阳卡", + "华融湘江银行·华融卡", "华融湘江银行·华融卡", "营口沿海银行·祥云借记卡", "景德镇商业银行·瓷都卡", + "哈密市商业银行·瓜香借记卡", "湖北银行·金牛卡", "湖北银行·汉江卡", "湖北银行·借记卡", "湖北银行·三峡卡", + "湖北银行·至尊卡", "湖北银行·金融 IC 卡", "西藏银行·借记 IC 卡", "新疆汇和银行·汇和卡", "广东华兴银行·借记卡", + "广东华兴银行·华兴银联公司卡", "广东华兴银行·华兴联名 IC 卡", "广东华兴银行·华兴金融 IC 借记卡", "濮阳银行·龙翔卡", + "宁波通商银行·借记卡", "甘肃银行·神舟兴陇借记卡", "甘肃银行·甘肃银行神州兴陇 IC 卡", "枣庄银行·借记 IC 卡", + "本溪市商业银行·借记卡", "盛京银行·医保卡", "上海农商银行·如意卡 (银联卡)", "上海农商银行·如意卡 (银联卡)", + "上海农商银行·鑫通卡", "上海农商银行·国际如意卡", "上海农商银行·借记 IC 卡", + "常熟市农村商业银行·粒金贷记卡 (银联卡)", "常熟市农村商业银行·公务卡", "常熟市农村商业银行·粒金准贷卡", + "常熟农村商业银行·粒金借记卡 (银联卡)", "常熟农村商业银行·粒金 IC 卡", "常熟农村商业银行·粒金卡", + "深圳农村商业银行·信通卡 (银联卡)", "深圳农村商业银行·信通商务卡 (银联卡)", "深圳农村商业银行·信通卡", + "深圳农村商业银行·信通商务卡", "广州农村商业银行·福农太阳卡", "广东南海农村商业银行·盛通卡", + "广东南海农村商业银行·盛通卡 (银联卡)", "佛山顺德农村商业银行·恒通卡 (银联卡)", "佛山顺德农村商业银行·恒通卡", + "佛山顺德农村商业银行·恒通卡 (银联卡)", "江阴农村商业银行·暨阳公务卡", "江阴市农村商业银行·合作贷记卡 (银联卡)", + "江阴农村商业银行·合作借记卡", "江阴农村商业银行·合作卡 (银联卡)", "江阴农村商业银行·暨阳卡", + "重庆农村商业银行·江渝借记卡 VIP 卡", "重庆农村商业银行·江渝 IC 借记卡", "重庆农村商业银行·江渝乡情福农卡", + "东莞农村商业银行·信通卡 (银联卡)", "东莞农村商业银行·信通卡 (银联卡)", "东莞农村商业银行·信通信用卡", + "东莞农村商业银行·信通借记卡", "东莞农村商业银行·贷记 IC 卡", "张家港农村商业银行·一卡通 (银联卡)", + "张家港农村商业银行·一卡通 (银联卡)", "张家港农村商业银行·", "北京农村商业银行·信通卡", "北京农村商业银行·惠通卡", + "北京农村商业银行·凤凰福农卡", "北京农村商业银行·惠通卡", "北京农村商业银行·中国旅行卡", "北京农村商业银行·凤凰卡", + "天津农村商业银行·吉祥商联 IC 卡", "天津农村商业银行·信通借记卡 (银联卡)", "天津农村商业银行·借记 IC 卡", + "鄞州农村合作银行·蜜蜂借记卡 (银联卡)", "宁波鄞州农村合作银行·蜜蜂电子钱包 (IC)", + "宁波鄞州农村合作银行·蜜蜂 IC 借记卡", "宁波鄞州农村合作银行·蜜蜂贷记 IC 卡", "宁波鄞州农村合作银行·蜜蜂贷记卡", + "宁波鄞州农村合作银行·公务卡", "成都农村商业银行·福农卡", "成都农村商业银行·福农卡", + "珠海农村商业银行·信通卡 (银联卡)", "太仓农村商业银行·郑和卡 (银联卡)", "太仓农村商业银行·郑和 IC 借记卡", + "无锡农村商业银行·金阿福", "无锡农村商业银行·借记 IC 卡", "黄河农村商业银行·黄河卡", + "黄河农村商业银行·黄河富农卡福农卡", "黄河农村商业银行·借记 IC 卡", "天津滨海农村商业银行·四海通卡", + "天津滨海农村商业银行·四海通 e 芯卡", "武汉农村商业银行·汉卡", "武汉农村商业银行·汉卡", + "武汉农村商业银行·中国旅游卡", "江南农村商业银行·阳湖卡 (银联卡)", "江南农村商业银行·天天红火卡", + "江南农村商业银行·借记 IC 卡", "海口联合农村商业银行·海口联合农村商业银行合卡", "湖北嘉鱼吴江村镇银行·垂虹卡", + "福建建瓯石狮村镇银行·玉竹卡", "浙江平湖工银村镇银行·金平卡", "重庆璧山工银村镇银行·翡翠卡", + "重庆农村商业银行·银联标准贷记卡", "重庆农村商业银行·公务卡", "南阳村镇银行·玉都卡", + "晋中市榆次融信村镇银行·魏榆卡", "三水珠江村镇银行·珠江太阳卡", "东营莱商村镇银行·绿洲卡", "建设银行·单位结算卡", + "玉溪市商业银行·红塔卡"}; +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/validator/IDCardUtils.java b/lib/DevJava/src/main/java/dev/utils/common/validator/IDCardUtils.java new file mode 100644 index 0000000000..f740bdc3f4 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/validator/IDCardUtils.java @@ -0,0 +1,660 @@ +package dev.utils.common.validator; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import dev.utils.JCLogUtils; + +/** + * detail: 居民身份证工具类 + * @author AbrahamCaiJin + * @author Ttt ( 重写、规范注释、逻辑判断等 ) + */ +public final class IDCardUtils { + + private IDCardUtils() { + } + + // 日志 TAG + private static final String TAG = IDCardUtils.class.getSimpleName(); + // 加权因子 + private static final int[] POWER = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; + // 身份证最少位数 + public static final int CHINA_ID_MIN_LENGTH = 15; + // 身份证最大位数 + public static final int CHINA_ID_MAX_LENGTH = 18; + // 省份编码 + private static Map sCityCodeMaps = new HashMap<>(); + // 台湾身份首字母对应数字 + private static Map sTWFirstCodeMaps = new HashMap<>(); + // 香港身份首字母对应数字 + private static Map sHKFirstCodeMaps = new HashMap<>(); + + static { + sCityCodeMaps.put("11", "北京"); + sCityCodeMaps.put("12", "天津"); + sCityCodeMaps.put("13", "河北"); + sCityCodeMaps.put("14", "山西"); + sCityCodeMaps.put("15", "内蒙古"); + sCityCodeMaps.put("21", "辽宁"); + sCityCodeMaps.put("22", "吉林"); + sCityCodeMaps.put("23", "黑龙江"); + sCityCodeMaps.put("31", "上海"); + sCityCodeMaps.put("32", "江苏"); + sCityCodeMaps.put("33", "浙江"); + sCityCodeMaps.put("34", "安徽"); + sCityCodeMaps.put("35", "福建"); + sCityCodeMaps.put("36", "江西"); + sCityCodeMaps.put("37", "山东"); + sCityCodeMaps.put("41", "河南"); + sCityCodeMaps.put("42", "湖北"); + sCityCodeMaps.put("43", "湖南"); + sCityCodeMaps.put("44", "广东"); + sCityCodeMaps.put("45", "广西"); + sCityCodeMaps.put("46", "海南"); + sCityCodeMaps.put("50", "重庆"); + sCityCodeMaps.put("51", "四川"); + sCityCodeMaps.put("52", "贵州"); + sCityCodeMaps.put("53", "云南"); + sCityCodeMaps.put("54", "西藏"); + sCityCodeMaps.put("61", "陕西"); + sCityCodeMaps.put("62", "甘肃"); + sCityCodeMaps.put("63", "青海"); + sCityCodeMaps.put("64", "宁夏"); + sCityCodeMaps.put("65", "新疆"); + sCityCodeMaps.put("71", "台湾"); + sCityCodeMaps.put("81", "香港"); + sCityCodeMaps.put("82", "澳门"); + sCityCodeMaps.put("91", "国外"); + sTWFirstCodeMaps.put("A", 10); + sTWFirstCodeMaps.put("B", 11); + sTWFirstCodeMaps.put("C", 12); + sTWFirstCodeMaps.put("D", 13); + sTWFirstCodeMaps.put("E", 14); + sTWFirstCodeMaps.put("F", 15); + sTWFirstCodeMaps.put("G", 16); + sTWFirstCodeMaps.put("H", 17); + sTWFirstCodeMaps.put("J", 18); + sTWFirstCodeMaps.put("K", 19); + sTWFirstCodeMaps.put("L", 20); + sTWFirstCodeMaps.put("M", 21); + sTWFirstCodeMaps.put("N", 22); + sTWFirstCodeMaps.put("P", 23); + sTWFirstCodeMaps.put("Q", 24); + sTWFirstCodeMaps.put("R", 25); + sTWFirstCodeMaps.put("S", 26); + sTWFirstCodeMaps.put("T", 27); + sTWFirstCodeMaps.put("U", 28); + sTWFirstCodeMaps.put("V", 29); + sTWFirstCodeMaps.put("X", 30); + sTWFirstCodeMaps.put("Y", 31); + sTWFirstCodeMaps.put("W", 32); + sTWFirstCodeMaps.put("Z", 33); + sTWFirstCodeMaps.put("I", 34); + sTWFirstCodeMaps.put("O", 35); + sHKFirstCodeMaps.put("A", 1); + sHKFirstCodeMaps.put("B", 2); + sHKFirstCodeMaps.put("C", 3); + sHKFirstCodeMaps.put("R", 18); + sHKFirstCodeMaps.put("U", 21); + sHKFirstCodeMaps.put("Z", 26); + sHKFirstCodeMaps.put("X", 24); + sHKFirstCodeMaps.put("W", 23); + sHKFirstCodeMaps.put("O", 15); + sHKFirstCodeMaps.put("N", 14); + } + + /** + * 身份证校验规则, 验证 15 位身份编码是否合法 + * @param idCard 待验证身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean validateIdCard15(final String idCard) { + // 属于数字, 并且长度为 15 位数 + if (isNumber(idCard) && idCard.length() == CHINA_ID_MIN_LENGTH) { + // 获取省份编码 + String provinceCode = idCard.substring(0, 2); + if (sCityCodeMaps.get(provinceCode) == null) return false; + // 获取出生日期 + String birthCode = idCard.substring(6, 12); + Date birthDate = null; + try { + birthDate = new SimpleDateFormat("yy").parse(birthCode.substring(0, 2)); + } catch (ParseException e) { + JCLogUtils.eTag(TAG, e, "validateIdCard15"); + } + Calendar calendar = Calendar.getInstance(); + if (birthDate != null) calendar.setTime(birthDate); + // 判断是否有效日期 + return validateDateSmllerThenNow(calendar.get(Calendar.YEAR), + Integer.valueOf(birthCode.substring(2, 4)), + Integer.valueOf(birthCode.substring(4, 6))); + } + return false; + } + + /** + * 身份证校验规则, 验证 18 位身份编码是否合法 + * @param idCard 待验证身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean validateIdCard18(final String idCard) { + if (idCard != null && idCard.length() == CHINA_ID_MAX_LENGTH) { + // 前 17 位 + String code17 = idCard.substring(0, 17); + // 第 18 位 + String code18 = idCard.substring(17, CHINA_ID_MAX_LENGTH); + // 判断前 17 位是否数字 + if (isNumber(code17)) { + try { + int[] cardArys = converCharToInt(code17.toCharArray()); + int sum17 = getPowerSum(cardArys); + // 获取校验位 + String str = getCheckCode18(sum17); + // 判断最后一位是否一样 + if (str.length() > 0 && str.equalsIgnoreCase(code18)) { + return true; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "validateIdCard18"); + } + } + } + return false; + } + + /** + * 将 15 位身份证号码转换为 18 位 + * @param idCard 15 位身份编码 + * @return 18 位身份编码 + */ + public static String convert15CardTo18(final String idCard) { + // 属于数字, 并且长度为 15 位数 + if (isNumber(idCard) && idCard.length() == CHINA_ID_MIN_LENGTH) { + String idCard18; + Date birthDate = null; + // 获取出生日期 + String birthday = idCard.substring(6, 12); + try { + birthDate = new SimpleDateFormat("yyMMdd").parse(birthday); + } catch (ParseException e) { + JCLogUtils.eTag(TAG, e, "convert15CardTo18"); + } + Calendar calendar = Calendar.getInstance(); + if (birthDate != null) calendar.setTime(birthDate); + try { + // 获取出生年 ( 完全表现形式, 如: 2010) + String year = String.valueOf(calendar.get(Calendar.YEAR)); + // 保存省市区信息 + 年 + 月日 + 后续信息 ( 顺序位、性别等 ) + idCard18 = idCard.substring(0, 6) + year + idCard.substring(8); + // 转换字符数组 + int[] cardArys = converCharToInt(idCard18.toCharArray()); + int sum17 = getPowerSum(cardArys); + // 获取校验位 + String str = getCheckCode18(sum17); + // 判断长度, 拼接校验位 + return (str.length() > 0) ? (idCard18 + str) : null; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "convert15CardTo18"); + } + } + return null; + } + + /** + * 验证台湾身份证号码 + * @param idCard 身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean validateTWCard(final String idCard) { + // 台湾身份证 10 位 + if (idCard == null || idCard.length() != 10) return false; + try { + // 第一位英文 不同县市 + String start = idCard.substring(0, 1); + String mid = idCard.substring(1, 9); + String end = idCard.substring(9, 10); + Integer iStart = sTWFirstCodeMaps.get(start); + Integer sum = iStart / 10 + (iStart % 10) * 9; + char[] chars = mid.toCharArray(); + Integer iflag = 8; + for (char c : chars) { + sum = sum + Integer.valueOf(c + "") * iflag; + iflag--; + } + return (sum % 10 == 0 ? 0 : 10 - sum % 10) == Integer.valueOf(end); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "validateTWCard"); + } + return false; + } + + /** + * 验证香港身份证号码 ( 部份特殊身份证无法检查 ) + * 身份证前 2 位为英文字符, 如果只出现一个英文字符则表示第一位是空格, 对应数字 58 前 2 位英文字符 A-Z 分别对应数字 10-35 + * 最后一位校验码为 0-9 的数字加上字符 "A", "A" 代表 10 + * 将身份证号码全部转换为数字, 分别对应乘 9-1 相加的总和, 整除 11 则证件号码有效 + * @param idCard 身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean validateHKCard(final String idCard) { + if (isEmpty(idCard)) return false; + try { + String card = idCard.replaceAll("[\\(|\\)]", ""); + Integer sum = 0; + if (card.length() == 9) { + sum = ((int) card.substring(0, 1).toUpperCase().toCharArray()[0] - 55) * 9 + ((int) card.substring(1, 2).toUpperCase().toCharArray()[0] - 55) * 8; + card = card.substring(1, 9); + } else { + sum = 522 + ((int) card.substring(0, 1).toUpperCase().toCharArray()[0] - 55) * 8; + } + String mid = card.substring(1, 7); + String end = card.substring(7, 8); + char[] chars = mid.toCharArray(); + Integer iflag = 7; + for (char c : chars) { + sum = sum + Integer.valueOf(c + "") * iflag; + iflag--; + } + if (end.toUpperCase().equals("A")) { + sum = sum + 10; + } else { + sum = sum + Integer.valueOf(end); + } + return (sum % 11 == 0); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "validateHKCard"); + } + return false; + } + + /** + * 判断 10 位数的身份证号, 是否合法 + * @param idCard 身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static String[] validateIdCard10(final String idCard) { + if (isEmpty(idCard)) return null; + String[] info = new String[3]; + info[0] = "N"; // 默认未知地区 + info[1] = "N"; // 默认未知性别 + info[2] = "false"; // 默认非法 + try { + String card = idCard.replaceAll("[\\(|\\)]", ""); + // 获取身份证长度 + int cardLength = card.length(); + // 属于 8, 9, 10 长度范围内 + if (cardLength >= 8 || cardLength <= 10) { + if (idCard.matches("^[a-zA-Z][0-9]{9}$")) { // 台湾 + info[0] = "台湾"; + String char2 = idCard.substring(1, 2); + if (char2.equals("1")) { + info[1] = "M"; + } else if (char2.equals("2")) { + info[1] = "F"; + } else { + info[1] = "N"; + info[2] = "false"; + return info; + } + info[2] = validateTWCard(idCard) ? "true" : "false"; + } else if (idCard.matches("^[1|5|7][0-9]{6}\\(?[0-9A-Z]\\)?$")) { // 澳门 + info[0] = "澳门"; + info[1] = "N"; + // TODO + } else if (idCard.matches("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?$")) { // 香港 + info[0] = "香港"; + info[1] = "N"; + info[2] = validateHKCard(idCard) ? "true" : "false"; + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "validateIdCard10"); + } + return info; + } + + /** + * 验证身份证是否合法 + * @param idCard 身份证号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean validateCard(final String idCard) { + if (isEmpty(idCard)) return false; + String card = idCard.trim(); + if (validateIdCard18(card)) return true; + if (validateIdCard15(card)) return true; + String[] cardStrs = validateIdCard10(card); + return (cardStrs != null && "true".equals(cardStrs[2])); + } + + /** + * 根据身份编号获取年龄 + * @param idCard 身份编号 + * @return 年龄 + */ + public static int getAgeByIdCard(final String idCard) { + if (isEmpty(idCard)) return 0; + try { + String idCardStr = idCard; + // 属于 15 位身份证, 则转换为 18 位 + if (idCardStr.length() == CHINA_ID_MIN_LENGTH) { + idCardStr = convert15CardTo18(idCard); + } + // 属于 18 位身份证才处理 + if (idCardStr.length() == CHINA_ID_MAX_LENGTH) { + String year = idCardStr.substring(6, 10); + // 获取当前年份 + int currentYear = Calendar.getInstance().get(Calendar.YEAR); + // 当前年份 - 出生年份 + return currentYear - Integer.valueOf(year); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getAgeByIdCard"); + } + return 0; + } + + /** + * 根据身份编号获取生日 + * @param idCard 身份编号 + * @return 生日 (yyyyMMdd) + */ + public static String getBirthByIdCard(final String idCard) { + if (isEmpty(idCard)) return null; + try { + String idCardStr = idCard; + // 属于 15 位身份证, 则转换为 18 位 + if (idCardStr.length() == CHINA_ID_MIN_LENGTH) { + idCardStr = convert15CardTo18(idCard); + } + // 属于 18 位身份证才处理 + if (idCardStr.length() == CHINA_ID_MAX_LENGTH) { + return idCardStr.substring(6, 14); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getBirthByIdCard"); + } + return null; + } + + /** + * 根据身份编号获取生日 + * @param idCard 身份编号 + * @return 生日 (yyyyMMdd) + */ + public static String getBirthdayByIdCard(final String idCard) { + // 获取生日 + String birth = getBirthByIdCard(idCard); + // 进行处理 + if (birth != null) { + try { + return birth.replaceAll("(\\d{4})(\\d{2})(\\d{2})", "$1-$2-$3"); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getBirthdayByIdCard"); + } + } + return null; + } + + /** + * 根据身份编号获取生日 ( 年份 ) + * @param idCard 身份编号 + * @return 生日 (yyyy) + */ + public static String getYearByIdCard(final String idCard) { + // 获取生日 + String birth = getBirthByIdCard(idCard); + // 进行处理 + if (birth != null) { + try { + return birth.substring(0, 4); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getYearByIdCard"); + } + } + return null; + } + + /** + * 根据身份编号获取生日 ( 月份 ) + * @param idCard 身份编号 + * @return 生日 (MM) + */ + public static String getMonthByIdCard(final String idCard) { + // 获取生日 + String birth = getBirthByIdCard(idCard); + // 进行处理 + if (birth != null) { + try { + return birth.substring(4, 6); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getMonthByIdCard"); + } + } + return null; + } + + /** + * 根据身份编号获取生日 ( 天数 ) + * @param idCard 身份编号 + * @return 生日 (dd) + */ + public static String getDateByIdCard(final String idCard) { + // 获取生日 + String birth = getBirthByIdCard(idCard); + // 进行处理 + if (birth != null) { + try { + return birth.substring(6, 8); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getDateByIdCard"); + } + } + return null; + } + + /** + * 根据身份编号获取性别 + * @param idCard 身份编号 + * @return 性别 男 (M)、女 (F)、未知 (N) + */ + public static String getGenderByIdCard(final String idCard) { + if (isEmpty(idCard)) return null; + try { + String idCardStr = idCard; + // 属于 15 位身份证, 则转换为 18 位 + if (idCardStr.length() == CHINA_ID_MIN_LENGTH) { + idCardStr = convert15CardTo18(idCard); + } + // 属于 18 位身份证才处理 + if (idCardStr.length() == CHINA_ID_MAX_LENGTH) { + // 获取第 17 位性别信息 + String cardNumber = idCardStr.substring(16, 17); + // 奇数为男, 偶数为女 + return (Integer.parseInt(cardNumber) % 2 == 0) ? "F" : "M"; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getGenderByIdCard"); + } + // 默认未知 + return "N"; + } + + /** + * 根据身份编号获取户籍省份 + * @param idCard 身份编码 + * @return 省级编码 + */ + public static String getProvinceByIdCard(final String idCard) { + if (isEmpty(idCard)) return null; + try { + // 身份证长度 + int idCardLength = idCard.length(); + // 属于 15 位身份证、或 18 位身份证 + if (idCardLength == CHINA_ID_MIN_LENGTH || idCardLength == CHINA_ID_MAX_LENGTH) { + return sCityCodeMaps.get(idCard.substring(0, 2)); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getProvinceByIdCard"); + } + return null; + } + + /** + * 将身份证的每位和对应位的加权因子相乘之后, 再获取和值 + * @param data byte[] 数据 + * @return 身份证编码, 加权引子 + */ + public static int getPowerSum(final int[] data) { + if (data == null) return 0; + int len = data.length; + if (len == 0) return 0; + int powerLength = POWER.length; + int sum = 0; + if (powerLength == len) { + for (int i = 0; i < len; i++) { + for (int j = 0; j < powerLength; j++) { + if (i == j) { + sum = sum + data[i] * POWER[j]; + } + } + } + } + return sum; + } + + /** + * 将 POWER 和值与 11 取模获取余数进行校验码判断 + * @param sum {@link IDCardUtils#getPowerSum} + * @return 校验位 + */ + public static String getCheckCode18(final int sum) { + String code = ""; + switch (sum % 11) { + case 10: + code = "2"; + break; + case 9: + code = "3"; + break; + case 8: + code = "4"; + break; + case 7: + code = "5"; + break; + case 6: + code = "6"; + break; + case 5: + code = "7"; + break; + case 4: + code = "8"; + break; + case 3: + code = "9"; + break; + case 2: + code = "x"; + break; + case 1: + code = "0"; + break; + case 0: + code = "1"; + break; + } + return code; + } + + // ============ + // = 私有方法 = + // ============ + + /** + * 将字符数组转换成数字数组 + * @param data char[] + * @return int[] + */ + private static int[] converCharToInt(final char[] data) { + if (data == null) return null; + int len = data.length; + if (len == 0) return null; + try { + int[] arrays = new int[len]; + for (int i = 0; i < len; i++) { + arrays[i] = Integer.parseInt(String.valueOf(data[i])); + } + return arrays; + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "converCharToInt"); + } + return null; + } + + /** + * 验证小于当前日期 是否有效 + * @param yearData 待校验的日期 ( 年 ) + * @param monthData 待校验的日期 ( 月 1-12) + * @param dayData 待校验的日期 ( 日 ) + * @return {@code true} yes, {@code false} no + */ + private static boolean validateDateSmllerThenNow(final int yearData, final int monthData, final int dayData) { + int year = Calendar.getInstance().get(Calendar.YEAR); + int datePerMonth; + int MIN = 1930; + if (yearData < MIN || yearData >= year) { + return false; + } + if (monthData < 1 || monthData > 12) { + return false; + } + switch (monthData) { + case 4: + case 6: + case 9: + case 11: + datePerMonth = 30; + break; + case 2: + boolean dm = ((yearData % 4 == 0 && yearData % 100 != 0) || (yearData % 400 == 0)) && (yearData > MIN && yearData < year); + datePerMonth = dm ? 29 : 28; + break; + default: + datePerMonth = 31; + } + return (dayData >= 1) && (dayData <= datePerMonth); + } + + /** + * 检验数字 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean isNumber(final String str) { + return !isEmpty(str) && str.matches("^[0-9]*$"); + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java b/lib/DevJava/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java new file mode 100644 index 0000000000..5f53c67399 --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java @@ -0,0 +1,236 @@ +package dev.utils.common.validator; + +import java.util.regex.Pattern; + +import dev.utils.JCLogUtils; + +/** + * detail: 检验联系 ( 手机号, 座机 ) 工具类 + * @author Ttt + *
+ *     @see 
+ *     

+ * 验证手机号是否正确 + * 移动: 134 135 136 137 138 139 147 148 150 151 152 157 158 159 165 172 178 182 183 184 187 188 198 + * 联通: 130 131 132 145 146 155 156 166 171 175 176 185 186 + * 电信: 133 149 153 173 174 177 180 181 189 191 199 + * 卫星通信: 1349 + * 虚拟运营商: 170 + *
+ */ +public final class ValiToPhoneUtils { + + private ValiToPhoneUtils() { + } + + // 日志 TAG + private static final String TAG = ValiToPhoneUtils.class.getSimpleName(); + + /** + * 中国手机号格式验证, 在输入可以调用该方法, 点击发送验证码, 使用 isPhone + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneCheck(final String phone) { + return match(CHAIN_PHONE_FORMAT_CHECK, phone); + } + + /** + * 是否中国手机号 + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhone(final String phone) { + return match(CHINA_PHONE_PATTERN, phone); + } + + /** + * 是否中国电信手机号码 + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneToChinaTelecom(final String phone) { + return match(CHINA_TELECOM_PATTERN, phone); + } + + /** + * 是否中国联通手机号码 + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneToChinaUnicom(final String phone) { + return match(CHINA_UNICOM_PATTERN, phone); + } + + /** + * 是否中国移动手机号码 + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneToChinaMobile(final String phone) { + return match(CHINA_MOBILE_PATTERN, phone); + } + + /** + * 判断是否香港手机号 + * @param phone 待校验的手机号 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneToHkMobile(final String phone) { + return match(HK_PHONE_PATTERN, phone); + } + + /** + * 验证电话号码的格式 + * @param phone 待校验的号码 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPhoneCallNum(final String phone) { + return match(PHONE_CALL_PATTERN, phone); + } + + // ============== + // = 手机号判断 = + // ============== + + // 简单手机号码校验 校验手机号码的长度和 1 开头 ( 是否 11 位 ) + public static final String CHAIN_PHONE_FORMAT_CHECK = "^(?:\\+86)?1\\d{10}$"; + + // 中国手机号正则 + public static final String CHINA_PHONE_PATTERN; + + // 中国电信号码正则 + public static final String CHINA_TELECOM_PATTERN; + + // 中国联通号码正则 + public static final String CHINA_UNICOM_PATTERN; + + // 中国移动号码正则 + public static final String CHINA_MOBILE_PATTERN; + + // 香港手机号码正则 香港手机号码 8 位数, 5|6|8|9 开头 + 7 位任意数 + public static final String HK_PHONE_PATTERN = "^(5|6|8|9)\\d{7}$"; + + // ============ + // = 座机判断 = + // ============ + + // 座机电话格式验证 + public static final String PHONE_CALL_PATTERN = "^(?:\\(\\d{3,4}\\)|\\d{3,4}-)?\\d{7,8}(?:-\\d{1,4})?$"; + + static { + + // ============ + // = 中国电信 = + // ============ + + // 电信: 133 149 153 173 174 177 180 181 189 191 199 + // 进行拼接字符串, 便于理解 + StringBuilder builder = new StringBuilder(); + builder.append("^13[3]{1}\\d{8}$"); // 13 开头 + builder.append("|"); // 或 + builder.append("^14[9]{1}\\d{8}$"); // 14 开头 + builder.append("|"); + builder.append("^15[3]{1}\\d{8}$"); // 15 开头 + builder.append("|"); + builder.append("^17[3,4,7]{1}\\d{8}$"); // 17 开头 + builder.append("|"); + builder.append("^18[0,1,9]{1}\\d{8}$"); // 18 开头 + builder.append("|"); + builder.append("^19[1,9]{1}\\d{8}$"); // 19 开头 + // 手机正则 + CHINA_TELECOM_PATTERN = builder.toString(); + + // ============ + // = 中国联通 = + // ============ + + // 联通: 130 131 132 145 146 155 156 166 171 175 176 185 186 + // 进行拼接字符串, 便于理解 + builder = new StringBuilder(); + builder.append("^13[0,1,2]{1}\\d{8}$"); // 13 开头 + builder.append("|"); // 或 + builder.append("^14[5,6]{1}\\d{8}$"); // 14 开头 + builder.append("|"); + builder.append("^15[5,6]{1}\\d{8}$"); // 15 开头 + builder.append("|"); + builder.append("^16[6]{1}\\d{8}$"); // 16 开头 + builder.append("|"); + builder.append("^17[1,5,6]{1}\\d{8}$"); // 17 开头 + builder.append("|"); + builder.append("^18[5,6]{1}\\d{8}$"); // 18 开头 + // 手机正则 + CHINA_UNICOM_PATTERN = builder.toString(); + + // ============ + // = 中国移动 = + // ============ + + // 移动: 134 135 136 137 138 139 147 148 150 151 152 157 158 159 165 172 178 182 183 184 187 188 198 + // 进行拼接字符串, 便于理解 + builder = new StringBuilder(); + builder.append("^13[4,5,6,7,8,9]{1}\\d{8}$"); // 13 开头 + builder.append("|"); // 或 + builder.append("^14[7,8]{1}\\d{8}$"); // 14 开头 + builder.append("|"); + builder.append("^15[0,1,2,7,8,9]{1}\\d{8}$"); // 15 开头 + builder.append("|"); + builder.append("^16[5]{1}\\d{8}$"); // 16 开头 + builder.append("|"); + builder.append("^17[2,8]{1}\\d{8}$"); // 17 开头 + builder.append("|"); + builder.append("^18[2,3,4,7,8]{1}\\d{8}$"); // 18 开头 + builder.append("|"); + builder.append("^19[8]{1}\\d{8}$"); // 19 开头 + // 手机正则 + CHINA_MOBILE_PATTERN = builder.toString(); + + /** + * 验证手机号是否正确 + * 移动: 134 135 136 137 138 139 147 148 150 151 152 157 158 159 165 172 178 182 183 184 187 188 198 + * 联通: 130 131 132 145 146 155 156 166 171 175 176 185 186 + * 电信: 133 149 153 173 174 177 180 181 189 191 199 + * 卫星通信: 1349 + * 虚拟运营商: 170 + */ + CHINA_PHONE_PATTERN = "^13[\\d]{9}$|^14[5,6,7,8,9]{1}\\d{8}$|^15[^4]{1}\\d{8}$|^16[5,6]{1}\\d{8}$|^17[0,1,2,3,4,5,6,7,8]{1}\\d{8}$|^18[\\d]{9}$|^19[1,8,9]{1}\\d{8}$"; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } + + // ================== + // = ValidatorUtils = + // ================== + + /** + * 通用匹配函数 + * @param regex 正则表达式 + * @param input 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + private static boolean match(final String regex, final String input) { + if (!isEmpty(input)) { + try { + return Pattern.matches(regex, input); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "match"); + } + } + return false; + } +} \ No newline at end of file diff --git a/lib/DevJava/src/main/java/dev/utils/common/validator/ValidatorUtils.java b/lib/DevJava/src/main/java/dev/utils/common/validator/ValidatorUtils.java new file mode 100644 index 0000000000..2ea3a9f38e --- /dev/null +++ b/lib/DevJava/src/main/java/dev/utils/common/validator/ValidatorUtils.java @@ -0,0 +1,284 @@ +package dev.utils.common.validator; + +import java.util.regex.Pattern; + +import dev.utils.JCLogUtils; + +/** + * detail: 校验工具类 + * @author Ttt + */ +public final class ValidatorUtils { + + private ValidatorUtils() { + } + + // 日志 TAG + private static final String TAG = ValidatorUtils.class.getSimpleName(); + + // 正则表达式: 空格 + public static final String REGEX_SPACE = "\\s"; + + // 正则表达式: 验证数字 + public static final String REGEX_NUMBER = "^[0-9]*$"; + + // 正则表达式: 验证数字或包含小数点 + public static final String REGEX_NUMBER_OR_DECIMAL = "^[0-9]*[.]?[0-9]*$"; + + // 正则表达式: 验证是否包含数字 + public static final String REGEX_CONTAIN_NUMBER = ".*\\d+.*"; + + // 正则表达式: 验证是否数字或者字母 + public static final String REGEX_NUMBER_OR_LETTER = "^[A-Za-z0-9]+$"; + + // 正则表达式: 验证是否全是字母 + public static final String REGEX_LETTER = "^[A-Za-z]+$"; + + // 正则表达式: 不能输入特殊字符 ^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$ + public static final String REGEX_SPECIAL = "^[\\u4E00-\\u9FA5A-Za-z0-9]+$"; + + // 正则表达式: 验证微信号 ^[a-zA-Z]{1}[-_a-zA-Z0-9]{5,19}+$ + public static final String REGEX_WX = "^[a-zA-Z]{1}[-_a-zA-Z0-9]{5,19}+$"; + + // 正则表达式: 验证真实姓名 ^[\u4e00-\u9fa5]+(·[\u4e00-\u9fa5]+)*$ + public static final String REGEX_REALNAME = "^[\\u4e00-\\u9fa5]+(•[\\u4e00-\\u9fa5]*)*$|^[\\u4e00-\\u9fa5]+(·[\\u4e00-\\u9fa5]*)*$"; + + // 正则表达式: 验证昵称 + public static final String REGEX_NICKNAME = "^[\\u4E00-\\u9FA5A-Za-z0-9_]+$"; + + // 正则表达式: 验证用户名 ( 不包含中文和特殊字符 ) 如果用户名使用手机号码或邮箱 则结合手机号验证和邮箱验证 + public static final String REGEX_USERNAME = "^[a-zA-Z]\\w{5,17}$"; + + // 正则表达式: 验证密码 ( 不包含特殊字符 ) + public static final String REGEX_PASSWORD = "^[a-zA-Z0-9]{6,18}$"; + + // 正则表达式: 验证邮箱 + public static final String REGEX_EMAIL = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; + + // 正则表达式: 验证 URL + public static final String REGEX_URL = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?"; + + // 正则表达式: 验证 IP 地址 + public static final String REGEX_IP_ADDR = "(2[5][0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})"; + + /** + * 通用匹配函数 + * @param regex 正则表达式 + * @param input 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean match(final String regex, final String input) { + if (!isEmpty(input)) { + try { + return Pattern.matches(regex, input); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "match"); + } + } + return false; + } + + // = + + /** + * 检验数字 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNumber(final String str) { + return match(REGEX_NUMBER, str); + } + + /** + * 检验数字或包含小数点 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNumberDecimal(final String str) { + return match(REGEX_NUMBER_OR_DECIMAL, str); + } + + /** + * 判断字符串是不是全是字母 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isLetter(final String str) { + return match(REGEX_LETTER, str); + } + + /** + * 判断字符串是不是包含数字 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContainNumber(final String str) { + return match(REGEX_CONTAIN_NUMBER, str); + } + + /** + * 判断字符串是不是只含字母和数字 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNumberLetter(final String str) { + return match(REGEX_NUMBER_OR_LETTER, str); + } + + /** + * 检验特殊符号 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isSpec(final String str) { + return match(REGEX_SPECIAL, str); + } + + /** + * 检验微信号 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isWx(final String str) { + return match(REGEX_WX, str); + } + + /** + * 检验真实姓名 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isRealName(final String str) { + return match(REGEX_REALNAME, str); + } + + /** + * 校验昵称 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNickName(final String str) { + return match(REGEX_NICKNAME, str); + } + + /** + * 校验用户名 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isUserName(final String str) { + return match(REGEX_USERNAME, str); + } + + /** + * 校验密码 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isPassword(final String str) { + return match(REGEX_PASSWORD, str); + } + + /** + * 校验邮箱 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isEmail(final String str) { + return match(REGEX_EMAIL, str); + } + + /** + * 校验 URL + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isUrl(final String str) { + if (!isEmpty(str)) { + return match(REGEX_URL, str.toLowerCase()); + } + return false; + } + + /** + * 校验 IP 地址 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isIPAddress(final String str) { + return match(REGEX_IP_ADDR, str); + } + + // = + +// // http://blog.csdn.net/myfuturein/article/details/6885216 +// [\\u0391-\\uFFE5] 匹配双字节字符 ( 汉字 + 符号 ) +// [\\u4e00-\\u9fa5] 注意只匹配汉字, 不匹配双字节字符 + + // 正则表达式: 验证汉字 + public static final String REGEX_CHINESE = "^[\u4e00-\u9fa5]+$"; + // 正则表达式: 验证汉字 ( 含双角符号 ) + public static final String REGEX_CHINESE_ALL = "^[\u0391-\uFFE5]+$"; + // 正则表达式: 验证汉字 ( 含双角符号 ) + public static final String REGEX_CHINESE_ALL2 = "[\u0391-\uFFE5]"; + + /** + * 校验汉字 ( 无符号, 纯汉字 ) + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isChinese(final String str) { + return match(REGEX_CHINESE, str); + } + + /** + * 判断字符串是不是全是中文 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isChineseAll(final String str) { + return match(REGEX_CHINESE_ALL, str); + } + + /** + * 判断字符串中包含中文、包括中文字符标点等 + * @param str 待校验的字符串 + * @return {@code true} yes, {@code false} no + */ + public static boolean isContainChinese(final String str) { + if (!isEmpty(str)) { + try { + int length; + if (str != null && (length = str.length()) != 0) { + char[] dChar = str.toCharArray(); + for (int i = 0; i < length; i++) { + boolean flag = String.valueOf(dChar[i]).matches(REGEX_CHINESE_ALL2); + if (flag) { + return true; + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "isContainChinese"); + } + } + return false; + } + + // ====================== + // = 其他工具类实现代码 = + // ====================== + + // =============== + // = StringUtils = + // =============== + + /** + * 判断字符串是否为 null + * @param str 待校验的字符串 + * @return {@code true} is null, {@code false} not null + */ + private static boolean isEmpty(final String str) { + return (str == null || str.length() == 0); + } +} \ No newline at end of file diff --git a/lib/DevOther/.gitignore b/lib/DevOther/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/lib/DevOther/.gitignore @@ -0,0 +1 @@ +/build diff --git a/lib/DevOther/README.md b/lib/DevOther/README.md new file mode 100644 index 0000000000..4d329facc4 --- /dev/null +++ b/lib/DevOther/README.md @@ -0,0 +1,466 @@ + + +## 目录结构 + +``` +- dev | 根目录 + - other | 第三方库封装工具类 + - receiver | BroadcastReceiver 监听相关 + - service | Service 相关 +``` + + +## Use + +> 直接 copy 所需要的类到项目中使用 + + +## API + + +- dev | 根目录 + - [other](#devother) | 第三方库封装工具类 + - [receiver](#devreceiver) | BroadcastReceiver 监听相关 + - [service](#devservice) | Service 相关 + + + + +## **`dev`** + + +* **Dev 工具类链式调用 Helper 类 ->** [DevHelper.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/DevHelper.java) + +| 方法 | 注释 | +| :- | :- | +| get | 获取单例 DevHelper | +| viewHelper | 获取 ViewHelper | +| devHelper | 获取 DevHelper | +| postRunnable | 在主线程 Handler 中执行任务 | +| removeRunnable | 在主线程 Handler 中清除任务 | +| startTimer | 运行定时器 | +| closeTimer | 关闭定时器 | +| recycle | Bitmap 通知回收 | +| saveBitmapToSDCardJPEG | 保存图片到 SDCard - JPEG | +| saveBitmapToSDCardPNG | 保存图片到 SDCard - PNG | +| saveBitmapToSDCardWEBP | 保存图片到 SDCard - WEBP | +| saveBitmapToSDCard | 保存图片到 SDCard | +| addTextChangedListener | 添加输入监听事件 | +| removeTextChangedListener | 移除输入监听事件 | +| setKeyListener | 设置 KeyListener | +| record | 日志记录 | +| cleanInternalCache | 清除内部缓存 - path /data/data/package/cache | +| cleanInternalFiles | 清除内部文件 - path /data/data/package/files | +| cleanInternalDbs | 清除内部数据库 - path /data/data/package/databases | +| cleanInternalDbByName | 根据名称清除数据库 - path /data/data/package/databases/dbName | +| cleanInternalSp | 清除内部 SP - path /data/data/package/shared_prefs | +| cleanExternalCache | 清除外部缓存 - path /storage/emulated/0/android/data/package/cache | +| cleanCustomDir | 清除自定义路径下的文件, 使用需小心请不要误删, 而且只支持目录下的文件删除 | +| cleanApplicationData | 清除本应用所有的数据 | +| copyText | 复制文本到剪贴板 | +| copyUri | 复制 URI 到剪贴板 | +| copyIntent | 复制意图到剪贴板 | +| notifyMediaStore | 通知刷新本地资源 | +| insertImageIntoMediaStore | 添加图片到系统相册 ( 包含原图、相册图, 会存在两张 ) - 想要一张, 直接调用 notifyMediaStore() | +| insertVideoIntoMediaStore | 添加视频到系统相册 | +| insertIntoMediaStore | 保存到系统相册 | +| showDialog | 显示 Dialog | +| closeDialog | 关闭 Dialog | +| closeDialogs | 关闭多个 Dialog | +| closePopupWindow | 关闭 PopupWindow | +| closePopupWindows | 关闭多个 PopupWindow | +| autoCloseDialog | 自动关闭 dialog | +| autoClosePopupWindow | 自动关闭 PopupWindow | +| openKeyboard | 打开软键盘 | +| closeKeyboard | 关闭软键盘 | +| closeKeyBoardSpecial | 关闭软键盘 - 特殊处理 | +| judgeView | 设置某个 View 内所有非 EditText 的子 View OnTouchListener 事件 | +| registerSoftInputChangedListener | 注册软键盘改变监听 | +| registerSoftInputChangedListener2 | 注册软键盘改变监听 | +| applyLanguage | 修改系统语言 (APP 多语言, 单独改变 APP 语言 ) | +| setOnClicks | 设置点击事件 | +| setOnLongClicks | 设置长按事件 | +| addTouchArea | 增加控件的触摸范围, 最大范围只能是父布局所包含的的区域 | +| cancelAllNotification | 移除通知 - 移除所有通知 ( 只是针对当前 Context 下的 Notification) | +| cancelNotification | 移除通知 - 移除标记为 id 的通知 ( 只是针对当前 Context 下的所有 Notification) | +| notifyNotification | 进行通知 | +| saveAssetsFormFile | 获取 Assets 资源文件数据并保存到本地 | +| saveRawFormFile | 获取 Raw 资源文件数据并保存到本地 | +| setWindowSecure | 设置禁止截屏 | +| setFullScreen | 设置屏幕为全屏 | +| setLandscape | 设置屏幕为横屏 | +| setPortrait | 设置屏幕为竖屏 | +| toggleScreenOrientation | 切换屏幕方向 | +| forceGetViewSize | 在 onCreate 中获取视图的尺寸 - 需回调 onGetSizeListener 接口, 在 onGetSize 中获取 View 宽高 | +| vibrate | 震动 | +| cancel | 取消震动 | +| closeIO | 关闭 IO | +| closeIOQuietly | 安静关闭 IO | +| getNetTime | 获取网络时间 - 默认使用百度链接 | +| waitForEndAsyn | 设置等待一段时间后, 通知方法 ( 异步 ) | +| waitForEnd | 设置等待一段时间后, 通知方法 ( 同步 ) | + + +* **View 链式调用快捷设置 Helper 类 ->** [ViewHelper.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/ViewHelper.java) + +| 方法 | 注释 | +| :- | :- | +| get | 获取单例 ViewHelper | +| viewHelper | 获取 ViewHelper | +| devHelper | 获取 DevHelper | +| postRunnable | 在主线程 Handler 中执行任务 | +| removeRunnable | 在主线程 Handler 中清除任务 | +| setHint | 设置 Hint 文本 | +| setText | 设置文本 | +| setTexts | 设置多个 TextView 文本 | +| setHtmlText | 设置 Html 内容 | +| setHtmlTexts | 设置多个 TextView Html 内容 | +| setHintTextColor | 设置 Hint 字体颜色 | +| setHintTextColors | 设置多个 TextView Hint 字体颜色 | +| setTextColor | 设置字体颜色 | +| setTextColors | 设置多个 TextView 字体颜色 | +| setTypeface | 设置字体 | +| setTextSizeByPx | 设置字体大小 - px 像素 | +| setTextSizeBySp | 设置字体大小 - sp 缩放像素 | +| setTextSizeByDp | 设置字体大小 - dp 与设备无关的像素 | +| setTextSizeByIn | 设置字体大小 - inches 英寸 | +| setTextSize | 设置字体大小 | +| setTextSizes | 设置多个 TextView 字体大小 | +| clearFlags | 清空 flags | +| setBold | 设置 TextView 是否加粗 | +| setUnderlineText | 设置下划线 | +| setStrikeThruText | 设置中划线 | +| setLetterSpacing | 设置文字水平间距 | +| setLineSpacing | 设置文字行间距 ( 行高 ) | +| setLineSpacingAndMultiplier | 设置文字行间距 ( 行高 )、行间距倍数 | +| setTextScaleX | 设置字体水平方向的缩放 | +| setIncludeFontPadding | 设置是否保留字体留白间隙区域 | +| setInputType | 设置输入类型 | +| setImeOptions | 设置软键盘右下角按钮类型 | +| setLines | 设置行数 | +| setMaxLines | 设置最大行数 | +| setMinLines | 设置最小行数 | +| setMaxEms | 设置最大字符宽度限制 | +| setMinEms | 设置最小字符宽度限制 | +| setEms | 设置指定字符宽度 | +| setEllipsize | 设置 Ellipsize 效果 | +| setAutoLinkMask | 设置自动识别文本链接 | +| setAllCaps | 设置文本全为大写 | +| setTextGravity | 设置 Text Gravity | +| insert | 追加内容 ( 当前光标位置追加 ) | +| setMaxLength | 设置长度限制 | +| setMaxLengthAndText | 设置长度限制, 并且设置内容 | +| setCursorVisible | 设置是否显示光标 | +| setSelectionToTop | 设置光标在第一位 | +| setSelectionToBottom | 设置光标在最后一位 | +| setSelection | 设置光标位置 | +| addTextChangedListener | 添加输入监听事件 | +| removeTextChangedListener | 移除输入监听事件 | +| setKeyListener | 设置 KeyListener | +| setAdjustViewBounds | 设置 ImageView 是否保持宽高比 | +| setMaxHeight | 设置 ImageView 最大高度 | +| setMaxWidth | 设置 ImageView 最大宽度 | +| setBackground | 设置背景图片 | +| setBackgroundColor | 设置背景颜色 | +| setBackgroundResource | 设置背景资源 | +| setBackgroundTintList | 设置背景着色颜色 | +| setBackgroundTintMode | 设置背景着色模式 | +| setForeground | 设置前景图片 | +| setForegroundGravity | 设置前景重心 | +| setForegroundTintList | 设置前景着色颜色 | +| setForegroundTintMode | 设置前景着色模式 | +| setImageBitmap | 设置 ImageView Bitmap | +| setImageDrawable | 设置 ImageView Drawable | +| setImageResource | 设置 ImageView 资源 | +| setImageMatrix | 设置 ImageView Matrix | +| setImageTintList | 设置 ImageView 着色颜色 | +| setImageTintMode | 设置 ImageView 着色模式 | +| setScaleType | 设置 ImageView 缩放类型 | +| setColorFilter | ImageView 着色处理 | +| setBackgroundResources | 设置 View 图片资源 | +| setImageResources | 设置 View 图片资源 | +| setImageBitmaps | 设置 View Bitmap | +| setImageDrawables | 设置 View Drawable | +| setScaleTypes | 设置 View 缩放模式 | +| setMinimumHeight | 设置 View 最小高度 | +| setMinimumWidth | 设置 View 最小宽度 | +| setAlpha | 设置 View 透明度 | +| setTag | 设置 View Tag | +| setScrollContainer | 设置 View 滚动效应 | +| setNextFocusForwardId | 设置下一个获取焦点的 View id | +| setNextFocusDownId | 设置向下移动焦点时, 下一个获取焦点的 View id | +| setNextFocusLeftId | 设置向左移动焦点时, 下一个获取焦点的 View id | +| setNextFocusRightId | 设置向右移动焦点时, 下一个获取焦点的 View id | +| setNextFocusUpId | 设置向上移动焦点时, 下一个获取焦点的 View id | +| setRotation | 设置 View 旋转度数 | +| setRotationX | 设置 View 水平旋转度数 | +| setRotationY | 设置 View 竖直旋转度数 | +| setScaleX | 设置 View 水平方向缩放比例 | +| setScaleY | 设置 View 竖直方向缩放比例 | +| setTextAlignment | 设置文本的显示方式 | +| setTextDirection | 设置文本的显示方向 | +| setPivotX | 设置水平方向偏转量 | +| setPivotY | 设置竖直方向偏转量 | +| setTranslationX | 设置水平方向的移动距离 | +| setTranslationY | 设置竖直方向的移动距离 | +| setLayerType | 设置 View 硬件加速类型 | +| setLayoutParams | 设置 View LayoutParams | +| setFocusable | 设置 View 是否可以获取焦点 | +| setSelected | 设置 View 是否选中 | +| setEnabled | 设置 View 是否启用 | +| setClickable | 设置 View 是否可以点击 | +| setLongClickable | 设置 View 是否可以长按 | +| setVisibility | 设置 View 显示的状态 | +| setVisibilitys | 设置 View 显示的状态 | +| toggleVisibilitys | 切换 View 显示的状态 | +| reverseVisibilitys | 反转 View 显示的状态 | +| removeSelfFromParent | 把自身从父 View 中移除 | +| setLayoutGravity | 设置 View Layout Gravity | +| setMarginLeft | 设置 View Left Margin | +| setMarginTop | 设置 View Top Margin | +| setMarginRight | 设置 View Right Margin | +| setMarginBottom | 设置 View Bottom Margin | +| setMargin | 设置 Margin 边距 | +| setPaddingLeft | 设置 View Left Padding | +| setPaddingTop | 设置 View Top Padding | +| setPaddingRight | 设置 View Right Padding | +| setPaddingBottom | 设置 View Bottom Padding | +| setPadding | 设置 Padding 边距 | +| setCompoundDrawablesByLeft | 设置 Left CompoundDrawables | +| setCompoundDrawablesByTop | 设置 Top CompoundDrawables | +| setCompoundDrawablesByRight | 设置 Right CompoundDrawables | +| setCompoundDrawablesByBottom | 设置 Bottom CompoundDrawables | +| setCompoundDrawables | 设置 CompoundDrawables | +| setCompoundDrawablesWithIntrinsicBoundsByLeft | 设置 Left CompoundDrawables - 按照原有比例大小显示图片 | +| setCompoundDrawablesWithIntrinsicBoundsByTop | 设置 Top CompoundDrawables - 按照原有比例大小显示图片 | +| setCompoundDrawablesWithIntrinsicBoundsByRight | 设置 Right CompoundDrawables - 按照原有比例大小显示图片 | +| setCompoundDrawablesWithIntrinsicBoundsByBottom | 设置 Bottom CompoundDrawables - 按照原有比例大小显示图片 | +| setCompoundDrawablesWithIntrinsicBounds | 设置 CompoundDrawables - 按照原有比例大小显示图片 | +| setOnClicks | 设置点击事件 | +| setOnLongClicks | 设置长按事件 | +| addTouchArea | 增加控件的触摸范围, 最大范围只能是父布局所包含的的区域 | + + +## **`dev.other`** + + +* **EventBus 工具类 ->** [EventBusUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/other/EventBusUtils.java) + +| 方法 | 注释 | +| :- | :- | +| register | 注册 EventBus | +| unregister | 解绑 EventBus | +| post | 发送事件消息 | +| cancelEventDelivery | 取消事件传送 | +| postSticky | 发送粘性事件消息 | +| removeStickyEvent | 移除指定的粘性订阅事件 | +| removeAllStickyEvents | 移除所有的粘性订阅事件 | + + +* **Glide 图形处理工具类 ->** [GlideTransformUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/other/GlideTransformUtils.java) + +| 方法 | 注释 | +| :- | :- | +| transform | transform | +| updateDiskCacheKey | updateDiskCacheKey | +| blurBitmap | 模糊图片处理 | + + +* **Glide 工具类 ->** [GlideUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/other/GlideUtils.java) + +| 方法 | 注释 | +| :- | :- | +| with | with | +| init | 初始化方法 | +| cloneImageOptions | 克隆图片加载配置 | +| defaultOptions | 获取默认加载配置 | +| emptyOptions | 获取空白加载配置 | +| skipCacheOptions | 获取跳过缓存 ( 每次都从服务端获取最新 ) 加载配置 | +| getLoadResOptions | 获取自定义图片加载配置 | +| transformationOptions | 获取图片处理效果加载配置 | +| clearDiskCache | 清除磁盘缓存 | +| clearMemoryCache | 清除内存缓存 | +| onLowMemory | 低内存通知 | +| getDiskCache | 获取 SDCard 缓存空间 | +| preload | 预加载图片 | +| displayImage | 图片显示 | +| displayImageToGif | 图片显示 | +| loadImageBitmap | 图片加载 | +| loadImageDrawable | 图片加载 | +| loadImageFile | 图片加载 | +| loadImageGif | 图片加载 | +| cancelDisplayTask | 取消图片显示任务 | +| destroy | 销毁操作 | +| pause | 暂停图片加载 | +| resume | 恢复图片加载 | +| stop | 停止图片加载 | +| start | 开始图片加载 | + + +* **Gson 工具类 ->** [GsonUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/other/GsonUtils.java) + +| 方法 | 注释 | +| :- | :- | +| toJson | 将对象转换为 JSON String | +| fromJson | 将 JSON String 映射为指定类型对象 | +| isJSON | 判断字符串是否 JSON 格式 | +| toJsonIndent | JSON String 缩进处理 | +| createGson | 创建 GsonBuilder | +| createGsonExcludeFields | 创建过滤指定修饰符字段 GsonBuilder | +| getArrayType | 获取 Array Type | +| getListType | 获取 List Type | +| getSetType | 获取 Set Type | +| getMapType | 获取 Map Type | +| getType | 获取 Type | + + +* **ImageLoader 工具类 ->** [ImageLoaderUtils.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/other/ImageLoaderUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化 ImageLoader 加载配置 | +| defaultOptions | 获取 DisplayImageOptions 图片加载配置 | +| getDefaultImageOptions | 获取图片默认加载配置 | +| getNoCacheImageOptions | 获取不使用缓存的图片加载配置 | +| getCacheImageOptions | 获取 ImageLoader 图片缓存加载配置 | +| cloneImageOptions | 克隆图片加载配置 | +| getFadeInBitmapDisplayer | 获取图片渐变动画加载配置 | +| getRoundedBitmapDisplayer | 获取圆角图片加载配置 | +| getBitmapDisplayerOptions | 获取图片效果加载配置 | +| displayImage | 图片显示 | +| loadImage | 图片加载 | +| loadImageSync | 图片同步加载 | +| clearDiskCache | 清除磁盘缓存 | +| clearMemoryCache | 清除内存缓存 | +| getDiskCache | 获取 SDCard 缓存空间 | +| getMemoryCache | 获取 Memory 缓存空间 | +| handleSlowNetwork | 设置是否处理弱网情况 | +| denyNetworkDownloads | 设置是否禁止网络下载 | +| cancelDisplayTask | 取消图片显示任务 | +| getLoadingUriForView | 通过 ImageView 获取图片加载地址 | +| setDefaultLoadingListener | 设置全局加载监听事件 | +| destroy | 销毁操作 | +| pause | 暂停图片加载 | +| resume | 恢复图片加载 | +| stop | 停止图片加载 | + + +## **`dev.receiver`** + + +* **网络监听广播 ->** [NetWorkReceiver.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/receiver/NetWorkReceiver.java) + +| 方法 | 注释 | +| :- | :- | +| onReceive | onReceive | +| isConnectNetWork | 是否连接网络 | +| getConnectType | 获取连接的网络类型 | +| registerReceiver | 注册网络广播监听 | +| unregisterReceiver | 取消注册网络广播监听 | +| setNetListener | 设置监听通知事件 | +| onNetworkState | 网络连接状态改变时通知 | + + +* **手机监听广播 ->** [PhoneReceiver.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/receiver/PhoneReceiver.java) + +| 方法 | 注释 | +| :- | :- | +| onReceive | onReceive | +| registerReceiver | 注册电话监听广播 | +| unregisterReceiver | 取消注册电话监听广播 | +| setPhoneListener | 设置电话状态监听事件 | +| onPhoneStateChanged | 电话状态改变通知 | + + +* **屏幕监听广播 ( 锁屏 / 解锁 / 亮屏 ) ->** [ScreenReceiver.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/receiver/ScreenReceiver.java) + +| 方法 | 注释 | +| :- | :- | +| onReceive | onReceive | +| registerReceiver | 注册屏幕监听广播 | +| unregisterReceiver | 取消注册屏幕监听广播 | +| setScreenListener | 设置屏幕监听事件 | +| screenOn | 用户打开屏幕 ( 屏幕变亮 ) | +| screenOff | 锁屏触发 | +| userPresent | 用户解锁触发 | + + +* **短信监听广播 ->** [SmsReceiver.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/receiver/SmsReceiver.java) + +| 方法 | 注释 | +| :- | :- | +| onReceive | onReceive | +| getMessageData | 获取消息数据 | +| registerReceiver | 注册短信监听广播 | +| unregisterReceiver | 取消注册短信监听广播 | +| setSmsListener | 设置短信监听事件 | +| onMessage | 最终触发通知 ( 超过长度的短信消息, 最终合并成一条内容体 ) | + + +* **时间监听广播 ->** [TimeReceiver.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/receiver/TimeReceiver.java) + +| 方法 | 注释 | +| :- | :- | +| onReceive | onReceive | +| registerReceiver | 注册时间监听广播 | +| unregisterReceiver | 取消注册时间监听广播 | +| setTimeListener | 设置时间监听事件 | +| onTimeZoneChanged | 时区改变 | +| onTimeChanged | 设置时间 | +| onTimeTick | 每分钟调用 | + + +* **Wifi 监听广播 ->** [WifiReceiver.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/receiver/WifiReceiver.java) + +| 方法 | 注释 | +| :- | :- | +| onReceive | onReceive | +| registerReceiver | 注册 Wifi 监听广播 | +| unregisterReceiver | 取消注册 Wifi 监听广播 | +| setWifiListener | 设置 Wifi 监听事件 | +| onIntoTrigger | 触发回调通知 ( 每次进入都通知 ) | +| onTrigger | 触发回调通知 | +| onWifiSwitch | Wifi 开关状态 | + + +## **`dev.service`** + + +* **无障碍功能监听服务 ->** [AccessibilityListenerService.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/service/AccessibilityListenerService.java) + +| 方法 | 注释 | +| :- | :- | +| onAccessibilityEvent | 通过这个函数可以接收系统发送来的 AccessibilityEvent | +| onInterrupt | 系统想要中断 AccessibilityService 返给的响应时会调用 | +| onServiceConnected | 系统成功绑定该服务时被触发, 也就是当你在设置中开启相应的服务, 系统成功的绑定了该服务时会触发, 通常我们可以在这里做一些初始化操作 | +| onCreate | onCreate | +| onDestroy | onDestroy | +| getSelf | 获取当前服务所持对象 | +| startService | 启动服务 | +| stopService | 停止服务 | +| checkAccessibility | 检查是否开启无障碍功能 | +| isAccessibilitySettingsOn | 判断是否开启无障碍功能 | +| setAccessibilityListener | 设置监听事件 | +| onServiceCreated | 服务创建通知 | +| onServiceDestroy | 服务销毁通知 | + + +* **通知栏监听服务 ->** [NotificationService.java](https://github.com/afkT/DevUtils/blob/master/lib/DevOther/src/main/java/dev/service/NotificationService.java) + +| 方法 | 注释 | +| :- | :- | +| onNotificationPosted | 当系统收到新的通知后触发回调 | +| onNotificationRemoved | 当系统通知被删掉后触发回调 | +| onCreate | onCreate | +| onDestroy | onDestroy | +| onStartCommand | onStartCommand | +| getSelf | 获取当前服务所持对象 | +| startService | 启动服务 | +| stopService | 停止服务 | +| checkAndIntentSetting | 检查是否有获取通知栏信息权限并跳转设置页面 | +| isNotificationListenerEnabled | 判断是否有获取通知栏信息权限 | +| startNotificationListenSettings | 跳转到设置页面, 开启获取通知栏信息权限 | +| cancelNotification | 取消通知方法 | +| setNotificationListener | 设置通知栏监听事件 | +| onServiceCreated | 服务创建通知 | +| onServiceDestroy | 服务销毁通知 | \ No newline at end of file diff --git a/lib/DevOther/build.gradle b/lib/DevOther/build.gradle new file mode 100644 index 0000000000..55a6b78d70 --- /dev/null +++ b/lib/DevOther/build.gradle @@ -0,0 +1,25 @@ +apply { + from rootProject.file(files.app_lib_gradle) +} + +android.defaultConfig { + versionCode versions.dev_other_versionCode + versionName versions.dev_other_versionName +} + +dependencies { + compileOnly project(path: ':DevApp') + compileOnly project(path: ':DevAssist') + + // Gson + implementation deps.lib.gson + // Imageloader + implementation deps.lib.imageloader + // Glide + implementation deps.lib.glide + annotationProcessor deps.lib.glide_compiler + // EventBus + implementation deps.lib.eventbus + // OkHttp3 + implementation deps.lib.okhttp3 +} \ No newline at end of file diff --git a/lib/DevOther/proguard-rules.pro b/lib/DevOther/proguard-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/DevOther/src/main/AndroidManifest.xml b/lib/DevOther/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..7991be99e7 --- /dev/null +++ b/lib/DevOther/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/DevOther/src/main/java/dev/DevHelper.java b/lib/DevOther/src/main/java/dev/DevHelper.java new file mode 100644 index 0000000000..e8aa646809 --- /dev/null +++ b/lib/DevOther/src/main/java/dev/DevHelper.java @@ -0,0 +1,1363 @@ +package dev; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Dialog; +import android.app.Notification; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Handler; +import android.support.annotation.IntRange; +import android.support.annotation.RawRes; +import android.support.v4.app.DialogFragment; +import android.text.TextWatcher; +import android.text.method.KeyListener; +import android.view.View; +import android.widget.EditText; +import android.widget.PopupWindow; + +import java.io.Closeable; +import java.io.File; +import java.util.Locale; + +import dev.utils.app.AnalysisRecordUtils; +import dev.utils.app.CleanUtils; +import dev.utils.app.ClickUtils; +import dev.utils.app.ClipboardUtils; +import dev.utils.app.ContentResolverUtils; +import dev.utils.app.DialogUtils; +import dev.utils.app.EditTextUtils; +import dev.utils.app.HandlerUtils; +import dev.utils.app.KeyBoardUtils; +import dev.utils.app.LanguageUtils; +import dev.utils.app.ListenerUtils; +import dev.utils.app.NotificationUtils; +import dev.utils.app.ResourceUtils; +import dev.utils.app.ScreenUtils; +import dev.utils.app.SizeUtils; +import dev.utils.app.VibrationUtils; +import dev.utils.app.assist.manager.TimerManager; +import dev.utils.app.image.BitmapUtils; +import dev.utils.app.image.ImageUtils; +import dev.utils.common.CloseUtils; +import dev.utils.common.HttpURLConnectionUtils; +import dev.utils.common.assist.TimeKeeper; + +/** + * detail: Dev 工具类链式调用 Helper 类 + * @author Ttt + *
+ *     通过 DevApp 工具类快捷实现
+ *     

+ * DevApp Api + * @see
+ *
+ */ +public final class DevHelper { + + // TimeKeeper + private TimeKeeper mTimeKeeper = new TimeKeeper(); + // DevHelper + private static final DevHelper HELPER = new DevHelper(); + + /** + * 获取单例 DevHelper + * @return {@link DevHelper} + */ + public static DevHelper get() { + return HELPER; + } + + // ========== + // = Helper = + // ========== + + /** + * 获取 ViewHelper + * @return {@link ViewHelper} + */ + public ViewHelper viewHelper() { + return ViewHelper.get(); + } + + /** + * 获取 DevHelper + * @return {@link DevHelper} + */ + public DevHelper devHelper() { + return this; + } + + // =========== + // = Handler = + // =========== + + /** + * 在主线程 Handler 中执行任务 + * @param runnable 可执行的任务 + * @return {@link DevHelper} + */ + public DevHelper postRunnable(final Runnable runnable) { + HandlerUtils.postRunnable(runnable); + return this; + } + + /** + * 在主线程 Handler 中执行延迟任务 + * @param runnable 可执行的任务 + * @param delayMillis 延迟时间 + * @return {@link DevHelper} + */ + public DevHelper postRunnable(final Runnable runnable, final long delayMillis) { + HandlerUtils.postRunnable(runnable, delayMillis); + return this; + } + + /** + * 在主线程 Handler 中执行延迟任务 + * @param runnable 可执行的任务 + * @param delayMillis 延迟时间 + * @param number 轮询次数 + * @param interval 轮询时间 + * @return {@link DevHelper} + */ + public DevHelper postRunnable(final Runnable runnable, final long delayMillis, final int number, final int interval) { + HandlerUtils.postRunnable(runnable, delayMillis, number, interval); + return this; + } + + /** + * 在主线程 Handler 中执行延迟任务 + * @param runnable 可执行的任务 + * @param delayMillis 延迟时间 + * @param number 轮询次数 + * @param interval 轮询时间 + * @param onEndListener 结束通知 + * @return {@link DevHelper} + */ + public DevHelper postRunnable(final Runnable runnable, final long delayMillis, final int number, final int interval, final HandlerUtils.OnEndListener onEndListener) { + HandlerUtils.postRunnable(runnable, delayMillis, number, interval, onEndListener); + return this; + } + + /** + * 在主线程 Handler 中清除任务 + * @param runnable 需要清除的任务 + * @return {@link DevHelper} + */ + public DevHelper removeRunnable(final Runnable runnable) { + HandlerUtils.removeRunnable(runnable); + return this; + } + + // ================ + // = TimerManager = + // ================ + + /** + * 运行定时器 + * @param timer {@link TimerManager.AbsTimer} + * @return {@link DevHelper} + */ + public DevHelper startTimer(final TimerManager.AbsTimer timer) { + if (timer != null) timer.startTimer(); + return this; + } + + /** + * 关闭定时器 + * @param timer {@link TimerManager.AbsTimer} + * @return {@link DevHelper} + */ + public DevHelper closeTimer(final TimerManager.AbsTimer timer) { + if (timer != null) timer.closeTimer(); + return this; + } + + // =============== + // = BitmapUtils = + // =============== + + /** + * Bitmap 通知回收 + * @param bitmap 待回收图片 + * @return {@link DevHelper} + */ + public DevHelper recycle(final Bitmap bitmap) { + BitmapUtils.recycle(bitmap); + return this; + } + + // ============== + // = ImageUtils = + // ============== + + /** + * 保存图片到 SDCard - JPEG + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardJPEG(final Bitmap bitmap, final String filePath) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.JPEG, 100); + } + + /** + * 保存图片到 SDCard - JPEG + * @param bitmap 待保存图片 + * @param file 保存路径 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardJPEG(final Bitmap bitmap, final File file) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.JPEG, 100); + } + + // = + + /** + * 保存图片到 SDCard - JPEG + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @param quality 质量 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardJPEG(final Bitmap bitmap, final String filePath, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.JPEG, quality); + } + + /** + * 保存图片到 SDCard - JPEG + * @param bitmap 待保存图片 + * @param file 保存路径 + * @param quality 质量 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardJPEG(final Bitmap bitmap, final File file, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.JPEG, quality); + } + + // = + + /** + * 保存图片到 SDCard - PNG + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardPNG(final Bitmap bitmap, final String filePath) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.PNG, 100); + } + + /** + * 保存图片到 SDCard - PNG + * @param bitmap 待保存图片 + * @param file 保存路径 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardPNG(final Bitmap bitmap, final File file) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.PNG, 100); + } + + // = + + /** + * 保存图片到 SDCard - PNG + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @param quality 质量 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardPNG(final Bitmap bitmap, final String filePath, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.PNG, quality); + } + + /** + * 保存图片到 SDCard - PNG + * @param bitmap 待保存图片 + * @param file 保存路径 + * @param quality 质量 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardPNG(final Bitmap bitmap, final File file, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.PNG, quality); + } + + // = + + /** + * 保存图片到 SDCard - WEBP + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardWEBP(final Bitmap bitmap, final String filePath) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.WEBP, 100); + } + + /** + * 保存图片到 SDCard - WEBP + * @param bitmap 待保存图片 + * @param file 保存路径 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardWEBP(final Bitmap bitmap, final File file) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.WEBP, 100); + } + + // = + + /** + * 保存图片到 SDCard - WEBP + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @param quality 质量 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardWEBP(final Bitmap bitmap, final String filePath, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, filePath, Bitmap.CompressFormat.WEBP, quality); + } + + /** + * 保存图片到 SDCard - WEBP + * @param bitmap 待保存图片 + * @param file 保存路径 + * @param quality 质量 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCardWEBP(final Bitmap bitmap, final File file, + @IntRange(from = 0, to = 100) final int quality) { + return saveBitmapToSDCard(bitmap, file, Bitmap.CompressFormat.WEBP, quality); + } + + // = + + /** + * 保存图片到 SDCard + * @param bitmap 待保存图片 + * @param filePath 保存路径 + * @param format 如 Bitmap.CompressFormat.PNG + * @param quality 质量 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCard(final Bitmap bitmap, final String filePath, final Bitmap.CompressFormat format, + @IntRange(from = 0, to = 100) final int quality) { + ImageUtils.saveBitmapToSDCard(bitmap, filePath, format, quality); + return this; + } + + /** + * 保存图片到 SDCard + * @param bitmap 待保存图片 + * @param file 保存路径 + * @param format 如 Bitmap.CompressFormat.PNG + * @param quality 质量 + * @return {@link DevHelper} + */ + public DevHelper saveBitmapToSDCard(final Bitmap bitmap, final File file, final Bitmap.CompressFormat format, + @IntRange(from = 0, to = 100) final int quality) { + ImageUtils.saveBitmapToSDCard(bitmap, file, format, quality); + return this; + } + + // ================= + // = EditTextUtils = + // ================= + + /** + * 添加输入监听事件 + * @param editText {@link EditText} + * @param watcher 输入监听 + * @return {@link DevHelper} + */ + public DevHelper addTextChangedListener(final EditText editText, final TextWatcher watcher) { + EditTextUtils.addTextChangedListener(editText, watcher); + return this; + } + + /** + * 移除输入监听事件 + * @param editText {@link EditText} + * @param watcher 输入监听 + * @return {@link DevHelper} + */ + public DevHelper removeTextChangedListener(final EditText editText, final TextWatcher watcher) { + EditTextUtils.removeTextChangedListener(editText, watcher); + return this; + } + + /** + * 设置 KeyListener + * @param editText {@link EditText} + * @param keyListener {@link KeyListener} + * @return {@link DevHelper} + */ + public DevHelper setKeyListener(final EditText editText, final KeyListener keyListener) { + EditTextUtils.setKeyListener(editText, keyListener); + return this; + } + + /** + * 设置 KeyListener + * @param editText {@link EditText} + * @param accepted 允许输入的内容, 如: 0123456789 + * @return {@link DevHelper} + */ + public DevHelper setKeyListener(final EditText editText, final String accepted) { + EditTextUtils.setKeyListener(editText, accepted); + return this; + } + + /** + * 设置 KeyListener + * @param editText {@link EditText} + * @param accepted 允许输入的内容 + * @return {@link DevHelper} + */ + public DevHelper setKeyListener(final EditText editText, final char[] accepted) { + EditTextUtils.setKeyListener(editText, accepted); + return this; + } + + // ======================= + // = AnalysisRecordUtils = + // ======================= + + /** + * 日志记录 + * @param fileInfo {@link AnalysisRecordUtils.FileInfo} + * @param logs 日志内容数组 + * @return {@link DevHelper} + */ + public DevHelper record(final AnalysisRecordUtils.FileInfo fileInfo, final String... logs) { + AnalysisRecordUtils.record(fileInfo, logs); + return this; + } + + // ============== + // = CleanUtils = + // ============== + + /** + * 清除内部缓存 - path /data/data/package/cache + * @return {@link DevHelper} + */ + public DevHelper cleanInternalCache() { + CleanUtils.cleanInternalCache(); + return this; + } + + /** + * 清除内部文件 - path /data/data/package/files + * @return {@link DevHelper} + */ + public DevHelper cleanInternalFiles() { + CleanUtils.cleanInternalFiles(); + return this; + } + + /** + * 清除内部数据库 - path /data/data/package/databases + * @return {@link DevHelper} + */ + public DevHelper cleanInternalDbs() { + CleanUtils.cleanInternalDbs(); + return this; + } + + /** + * 根据名称清除数据库 - path /data/data/package/databases/dbName + * @param dbName 数据库名 + * @return {@link DevHelper} + */ + public DevHelper cleanInternalDbByName(final String dbName) { + CleanUtils.cleanInternalDbByName(dbName); + return this; + } + + /** + * 清除内部 SP - path /data/data/package/shared_prefs + * @return {@link DevHelper} + */ + public DevHelper cleanInternalSp() { + CleanUtils.cleanInternalSp(); + return this; + } + + /** + * 清除外部缓存 - path /storage/emulated/0/android/data/package/cache + * @return {@link DevHelper} + */ + public DevHelper cleanExternalCache() { + CleanUtils.cleanExternalCache(); + return this; + } + + // = + + /** + * 清除自定义路径下的文件, 使用需小心请不要误删, 而且只支持目录下的文件删除 + * @param filePath 文件路径 + * @return {@link DevHelper} + */ + public DevHelper cleanCustomDir(final String filePath) { + CleanUtils.cleanCustomDir(filePath); + return this; + } + + /** + * 清除自定义路径下的文件, 使用需小心请不要误删, 而且只支持目录下的文件删除 + * @param file 文件路径 + * @return {@link DevHelper} + */ + public DevHelper cleanCustomDir(final File file) { + CleanUtils.cleanCustomDir(file); + return this; + } + + /** + * 清除本应用所有的数据 + * @param filePaths 文件路径数组 + * @return {@link DevHelper} + */ + public DevHelper cleanApplicationData(final String... filePaths) { + CleanUtils.cleanApplicationData(filePaths); + return this; + } + + // ================== + // = ClipboardUtils = + // ================== + + /** + * 复制文本到剪贴板 + * @param text 文本 + * @return {@link DevHelper} + */ + public DevHelper copyText(final CharSequence text) { + ClipboardUtils.copyText(text); + return this; + } + + /** + * 复制 URI 到剪贴板 + * @param uri {@link Uri} + * @return {@link DevHelper} + */ + public DevHelper copyUri(final Uri uri) { + ClipboardUtils.copyUri(uri); + return this; + } + + /** + * 复制意图到剪贴板 + * @param intent 意图 + * @return {@link DevHelper} + */ + public DevHelper copyIntent(final Intent intent) { + ClipboardUtils.copyIntent(intent); + return this; + } + + // ======================== + // = ContentResolverUtils = + // ======================== + + /** + * 通知刷新本地资源 + * @param file 文件 + * @return {@link DevHelper} + */ + public DevHelper notifyMediaStore(final File file) { + ContentResolverUtils.notifyMediaStore(file); + return this; + } + + /** + * 添加图片到系统相册 ( 包含原图、相册图, 会存在两张 ) - 想要一张, 直接调用 notifyMediaStore() + * @param file 文件 + * @param fileName 文件名 + * @param isNotify 是否广播通知图库扫描 + * @return {@link DevHelper} + */ + public DevHelper insertImageIntoMediaStore(final File file, final String fileName, final boolean isNotify) { + ContentResolverUtils.insertImageIntoMediaStore(file, fileName, isNotify); + return this; + } + + // = + + /** + * 添加视频到系统相册 + * @param file 文件 + * @return {@link DevHelper} + */ + public DevHelper insertVideoIntoMediaStore(final File file) { + ContentResolverUtils.insertVideoIntoMediaStore(file); + return this; + } + + /** + * 保存到系统相册 + * @param file 文件 + * @param createTime 创建时间 + * @param isVideo 是否视频 + * @param mimeType 资源类型 + * @return {@link DevHelper} + */ + public DevHelper insertIntoMediaStore(final File file, final long createTime, final boolean isVideo, final String mimeType) { + ContentResolverUtils.insertIntoMediaStore(file, createTime, isVideo, mimeType); + return this; + } + + // =============== + // = DialogUtils = + // =============== + + /** + * 显示 Dialog + * @param dialog {@link Dialog} + * @param 泛型 + * @return {@link DevHelper} + */ + public DevHelper showDialog(final T dialog) { + DialogUtils.showDialog(dialog); + return this; + } + + /** + * 关闭 Dialog + * @param dialog {@link Dialog} + * @return {@link DevHelper} + */ + public DevHelper closeDialog(final Dialog dialog) { + DialogUtils.closeDialog(dialog); + return this; + } + + /** + * 关闭多个 Dialog + * @param dialogs {@link Dialog} 数组 + * @return {@link DevHelper} + */ + public DevHelper closeDialogs(final Dialog... dialogs) { + DialogUtils.closeDialogs(dialogs); + return this; + } + + // = + + /** + * 关闭 DialogFragment + * @param dialog {@link DialogFragment} + * @return {@link DevHelper} + */ + public DevHelper closeDialog(final DialogFragment dialog) { + DialogUtils.closeDialog(dialog); + return this; + } + + /** + * 关闭多个 DialogFragment + * @param dialogs {@link DialogFragment} 数组 + * @return {@link DevHelper} + */ + public DevHelper closeDialogs(final DialogFragment... dialogs) { + DialogUtils.closeDialogs(dialogs); + return this; + } + + // = + + /** + * 关闭 PopupWindow + * @param popupWindow {@link PopupWindow} + * @return {@link DevHelper} + */ + public DevHelper closePopupWindow(final PopupWindow popupWindow) { + DialogUtils.closePopupWindow(popupWindow); + return this; + } + + /** + * 关闭多个 PopupWindow + * @param popupWindows {@link PopupWindow} 数组 + * @return {@link DevHelper} + */ + public DevHelper closePopupWindows(final PopupWindow... popupWindows) { + DialogUtils.closePopupWindows(popupWindows); + return this; + } + + // = + + /** + * 自动关闭 dialog + * @param dialog {@link Dialog} + * @param delayMillis 延迟关闭时间 + * @param handler {@link Handler} + * @param 泛型 + * @return {@link DevHelper} + */ + public DevHelper autoCloseDialog(final T dialog, final long delayMillis, final Handler handler) { + DialogUtils.autoCloseDialog(dialog, delayMillis, handler); + return this; + } + + /** + * 自动关闭 DialogFragment + * @param dialog {@link DialogFragment} + * @param delayMillis 延迟关闭时间 + * @param handler {@link Handler} + * @param 泛型 + * @return {@link DevHelper} + */ + public DevHelper autoCloseDialog(final T dialog, final long delayMillis, final Handler handler) { + DialogUtils.autoCloseDialog(dialog, delayMillis, handler); + return this; + } + + /** + * 自动关闭 PopupWindow + * @param popupWindow {@link PopupWindow} + * @param delayMillis 延迟关闭时间 + * @param handler {@link Handler} + * @param 泛型 + * @return {@link DevHelper} + */ + public DevHelper autoClosePopupWindow(final T popupWindow, final long delayMillis, final Handler handler) { + DialogUtils.autoClosePopupWindow(popupWindow, delayMillis, handler); + return this; + } + + // ================= + // = KeyBoardUtils = + // ================= + + // ============== + // = 打开软键盘 = + // ============== + + /** + * 打开软键盘 + * @param editText {@link EditText} + * @return {@link DevHelper} + */ + public DevHelper openKeyboard(final EditText editText) { + KeyBoardUtils.openKeyboard(editText); + return this; + } + + /** + * 打开软键盘 + * @param editText {@link EditText} + * @param handler {@link Handler} + * @return {@link DevHelper} + */ + public DevHelper openKeyboard(final EditText editText, final Handler handler) { + KeyBoardUtils.openKeyboard(editText, handler); + return this; + } + + /** + * 打开软键盘 + * @param editText {@link EditText} + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + * @return {@link DevHelper} + */ + public DevHelper openKeyboard(final EditText editText, final Handler handler, final int delayMillis) { + KeyBoardUtils.openKeyboard(editText, handler, delayMillis); + return this; + } + + // = + + /** + * 打开软键盘 + * @return {@link DevHelper} + */ + public DevHelper openKeyboard() { + KeyBoardUtils.openKeyboard(); + return this; + } + + /** + * 打开软键盘 + * @param handler {@link Handler} + * @return {@link DevHelper} + */ + public DevHelper openKeyboard(final Handler handler) { + KeyBoardUtils.openKeyboard(handler); + return this; + } + + /** + * 打开软键盘 + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + * @return {@link DevHelper} + */ + public DevHelper openKeyboard(final Handler handler, final int delayMillis) { + KeyBoardUtils.openKeyboard(handler, delayMillis); + return this; + } + + // ============== + // = 关闭软键盘 = + // ============== + + /** + * 关闭软键盘 + * @param editText {@link EditText} + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final EditText editText) { + KeyBoardUtils.closeKeyboard(editText); + return this; + } + + /** + * 关闭软键盘 + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard() { + KeyBoardUtils.closeKeyboard(); + return this; + } + + /** + * 关闭软键盘 + * @param activity {@link Activity} + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final Activity activity) { + KeyBoardUtils.closeKeyboard(activity); + return this; + } + + /** + * 关闭 dialog 中打开的键盘 + * @param dialog {@link Dialog} + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final Dialog dialog) { + KeyBoardUtils.closeKeyboard(dialog); + return this; + } + + // = + + /** + * 关闭软键盘 - 特殊处理 + * @param editText {@link EditText} + * @param dialog {@link Dialog} + * @return {@link DevHelper} + */ + public DevHelper closeKeyBoardSpecial(final EditText editText, final Dialog dialog) { + KeyBoardUtils.closeKeyBoardSpecial(editText, dialog); + return this; + } + + /** + * 关闭软键盘 - 特殊处理 + * @param editText {@link EditText} + * @param dialog {@link Dialog} + * @param handler {@link Handler} + * @return {@link DevHelper} + */ + public DevHelper closeKeyBoardSpecial(final EditText editText, final Dialog dialog, final Handler handler) { + KeyBoardUtils.closeKeyBoardSpecial(editText, dialog, handler); + return this; + } + + /** + * 关闭软键盘 - 特殊处理 ( 两个都关闭 ) + * @param editText {@link EditText} + * @param dialog {@link Dialog} + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + * @return {@link DevHelper} + */ + public DevHelper closeKeyBoardSpecial(final EditText editText, final Dialog dialog, final Handler handler, final int delayMillis) { + KeyBoardUtils.closeKeyBoardSpecial(editText, dialog, handler, delayMillis); + return this; + } + + // = + + /** + * 关闭软键盘 + * @param editText {@link EditText} + * @param handler {@link Handler} + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final EditText editText, final Handler handler) { + KeyBoardUtils.closeKeyboard(editText, handler); + return this; + } + + /** + * 关闭软键盘 + * @param editText {@link EditText} + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final EditText editText, final Handler handler, final int delayMillis) { + KeyBoardUtils.closeKeyboard(editText, handler, delayMillis); + return this; + } + + /** + * 关闭软键盘 + * @param handler {@link Handler} + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final Handler handler) { + KeyBoardUtils.closeKeyboard(handler); + return this; + } + + /** + * 关闭软键盘 + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final Handler handler, final int delayMillis) { + KeyBoardUtils.closeKeyboard(handler, delayMillis); + return this; + } + + /** + * 关闭软键盘 + * @param activity {@link Activity} + * @param handler {@link Handler} + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final Activity activity, final Handler handler) { + KeyBoardUtils.closeKeyboard(activity, handler); + return this; + } + + /** + * 关闭软键盘 + * @param activity {@link Activity} + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final Activity activity, final Handler handler, final int delayMillis) { + KeyBoardUtils.closeKeyboard(activity, handler, delayMillis); + return this; + } + + /** + * 关闭软键盘 + * @param dialog {@link Dialog} + * @param handler {@link Handler} + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final Dialog dialog, final Handler handler) { + KeyBoardUtils.closeKeyboard(dialog, handler); + return this; + } + + /** + * 关闭软键盘 + * @param dialog {@link Dialog} + * @param handler {@link Handler} + * @param delayMillis 延迟时间 ( 毫秒 ) + * @return {@link DevHelper} + */ + public DevHelper closeKeyboard(final Dialog dialog, final Handler handler, final int delayMillis) { + KeyBoardUtils.closeKeyboard(dialog, handler, delayMillis); + return this; + } + + // ================================ + // = 点击非 EditText 则隐藏输入法 = + // ================================ + + /** + * 设置某个 View 内所有非 EditText 的子 View OnTouchListener 事件 + * @param view {@link View} + * @param activity {@link Activity} + * @return {@link DevHelper} + */ + public DevHelper judgeView(final View view, final Activity activity) { + KeyBoardUtils.judgeView(view, activity); + return this; + } + + /** + * 注册软键盘改变监听 + * @param activity {@link Activity} + * @param listener {@link KeyBoardUtils.OnSoftInputChangedListener} + * @return {@link DevHelper} + */ + public DevHelper registerSoftInputChangedListener(final Activity activity, final KeyBoardUtils.OnSoftInputChangedListener listener) { + KeyBoardUtils.registerSoftInputChangedListener(activity, listener); + return this; + } + + /** + * 注册软键盘改变监听 + * @param activity {@link Activity} + * @param listener {@link KeyBoardUtils.OnSoftInputChangedListener} + * @return {@link DevHelper} + */ + public DevHelper registerSoftInputChangedListener2(final Activity activity, final KeyBoardUtils.OnSoftInputChangedListener listener) { + KeyBoardUtils.registerSoftInputChangedListener2(activity, listener); + return this; + } + + // ================= + // = LanguageUtils = + // ================= + + /** + * 修改系统语言 (APP 多语言, 单独改变 APP 语言 ) + * @param context {@link Context} + * @param locale {@link Locale} + * @return {@link DevHelper} + */ + public DevHelper applyLanguage(final Context context, final Locale locale) { + LanguageUtils.applyLanguage(context, locale); + return this; + } + + /** + * 修改系统语言 (APP 多语言, 单独改变 APP 语言 ) + * @param context {@link Context} + * @param language 语言 + * @return {@link DevHelper} + */ + public DevHelper applyLanguage(final Context context, final String language) { + LanguageUtils.applyLanguage(context, language); + return this; + } + + // ================= + // = ListenerUtils = + // ================= + + /** + * 设置点击事件 + * @param onClickListener {@link View.OnClickListener} + * @param views View 数组 + * @return {@link DevHelper} + */ + public DevHelper setOnClicks(final View.OnClickListener onClickListener, final View... views) { + ListenerUtils.setOnClicks(onClickListener, views); + return this; + } + + /** + * 设置长按事件 + * @param onLongClickListener {@link View.OnLongClickListener} + * @param views View 数组 + * @return {@link DevHelper} + */ + public DevHelper setOnLongClicks(final View.OnLongClickListener onLongClickListener, final View... views) { + ListenerUtils.setOnLongClicks(onLongClickListener, views); + return this; + } + + /** + * 增加控件的触摸范围, 最大范围只能是父布局所包含的的区域 + * @param view 待添加点击范围 View + * @param range 点击范围 + * @return {@link DevHelper} + */ + public DevHelper addTouchArea(final View view, final int range) { + ClickUtils.addTouchArea(view, range); + return this; + } + + /** + * 增加控件的触摸范围, 最大范围只能是父布局所包含的的区域 + * @param view 待添加点击范围 View + * @param top top range + * @param bottom bottom range + * @param left left range + * @param right right range + * @return {@link DevHelper} + */ + public DevHelper addTouchArea(final View view, final int top, final int bottom, final int left, final int right) { + ClickUtils.addTouchArea(view, top, bottom, left, right); + return this; + } + + // ===================== + // = NotificationUtils = + // ===================== + + /** + * 移除通知 - 移除所有通知 ( 只是针对当前 Context 下的 Notification) + * @return {@link DevHelper} + */ + public DevHelper cancelAllNotification() { + NotificationUtils.cancelAll(); + return this; + } + + /** + * 移除通知 - 移除标记为 id 的通知 ( 只是针对当前 Context 下的所有 Notification) + * @param args 消息 id 集合 + * @return {@link DevHelper} + */ + public DevHelper cancelNotification(final int... args) { + NotificationUtils.cancel(args); + return this; + } + + /** + * 移除通知 - 移除标记为 id 的通知 ( 只是针对当前 Context 下的所有 Notification) + * @param tag 标记 TAG + * @param id 消息 id + * @return {@link DevHelper} + */ + public DevHelper cancelNotification(final String tag, final int id) { + NotificationUtils.cancel(tag, id); + return this; + } + + /** + * 进行通知 + * @param id 消息 id + * @param notification {@link Notification} + * @return {@link DevHelper} + */ + public DevHelper notifyNotification(final int id, final Notification notification) { + NotificationUtils.notify(id, notification); + return this; + } + + /** + * 进行通知 + * @param tag 标记 TAG + * @param id 消息 id + * @param notification {@link Notification} + * @return {@link DevHelper} + */ + public DevHelper notifyNotification(final String tag, final int id, final Notification notification) { + NotificationUtils.notify(tag, id, notification); + return this; + } + + // ================= + // = ResourceUtils = + // ================= + + /** + * 获取 Assets 资源文件数据并保存到本地 + * @param fileName 文件名 + * @param file 文件保存地址 + * @return {@link DevHelper} + */ + public DevHelper saveAssetsFormFile(final String fileName, final File file) { + ResourceUtils.saveAssetsFormFile(fileName, file); + return this; + } + + /** + * 获取 Raw 资源文件数据并保存到本地 + * @param resId 资源 id + * @param file 文件保存地址 + * @return {@link DevHelper} + */ + public DevHelper saveRawFormFile(@RawRes final int resId, final File file) { + ResourceUtils.saveRawFormFile(resId, file); + return this; + } + + // =============== + // = ScreenUtils = + // =============== + + /** + * 设置禁止截屏 + * @param activity {@link Activity} + * @return {@link DevHelper} + */ + public DevHelper setWindowSecure(final Activity activity) { + ScreenUtils.setWindowSecure(activity); + return this; + } + + /** + * 设置屏幕为全屏 + * @param activity {@link Activity} + * @return {@link DevHelper} + */ + public DevHelper setFullScreen(final Activity activity) { + ScreenUtils.setFullScreen(activity); + return this; + } + + /** + * 设置屏幕为横屏 + * @param activity {@link Activity} + * @return {@link DevHelper} + */ + public DevHelper setLandscape(final Activity activity) { + ScreenUtils.setLandscape(activity); + return this; + } + + /** + * 设置屏幕为竖屏 + * @param activity {@link Activity} + * @return {@link DevHelper} + */ + public DevHelper setPortrait(final Activity activity) { + ScreenUtils.setPortrait(activity); + return this; + } + + /** + * 切换屏幕方向 + * @param activity {@link Activity} + * @return {@link DevHelper} + */ + public DevHelper toggleScreenOrientation(final Activity activity) { + ScreenUtils.toggleScreenOrientation(activity); + return this; + } + + // ============= + // = SizeUtils = + // ============= + + /** + * 在 onCreate 中获取视图的尺寸 - 需回调 onGetSizeListener 接口, 在 onGetSize 中获取 View 宽高 + * @param view {@link View} + * @param listener {@link SizeUtils.onGetSizeListener} + * @return {@link DevHelper} + */ + public DevHelper forceGetViewSize(final View view, final SizeUtils.onGetSizeListener listener) { + SizeUtils.forceGetViewSize(view, listener); + return this; + } + + // ================== + // = VibrationUtils = + // ================== + + /** + * 震动 + * @param milliseconds 震动时长 ( 毫秒 ) + * @return {@link DevHelper} + */ + @SuppressLint("MissingPermission") + public DevHelper vibrate(final long milliseconds) { + VibrationUtils.vibrate(milliseconds); + return this; + } + + /** + * pattern 模式震动 + * @param pattern new long[]{400, 800, 1200, 1600}, 就是指定在 400ms、800ms、1200ms、1600ms 这些时间点交替启动、关闭手机震动器 + * @param repeat 指定 pattern 数组的索引, 指定 pattern 数组中从 repeat 索引开始的震动进行循环, + * -1 表示只震动一次, 非 -1 表示从 pattern 数组指定下标开始重复震动 + * @return {@link DevHelper} + */ + @SuppressLint("MissingPermission") + public DevHelper vibrate(final long[] pattern, final int repeat) { + VibrationUtils.vibrate(pattern, repeat); + return this; + } + + /** + * 取消震动 + * @return {@link DevHelper} + */ + @SuppressLint("MissingPermission") + public DevHelper cancel() { + VibrationUtils.cancel(); + return this; + } + + // ============== + // = CloseUtils = + // ============== + + /** + * 关闭 IO + * @param closeables Closeable[] + * @return {@link DevHelper} + */ + public DevHelper closeIO(final Closeable... closeables) { + CloseUtils.closeIO(closeables); + return this; + } + + /** + * 安静关闭 IO + * @param closeables Closeable[] + * @return {@link DevHelper} + */ + public DevHelper closeIOQuietly(final Closeable... closeables) { + CloseUtils.closeIOQuietly(closeables); + return this; + } + + // ========================== + // = HttpURLConnectionUtils = + // ========================== + + /** + * 获取网络时间 - 默认使用百度链接 + * @param timeCallBack 请求时间回调接口 + * @return {@link DevHelper} + */ + public DevHelper getNetTime(final HttpURLConnectionUtils.TimeCallBack timeCallBack) { + HttpURLConnectionUtils.getNetTime(timeCallBack); + return this; + } + + /** + * 获取网络时间 + * @param urlStr 请求地址 + * @param timeCallBack 请求时间回调接口 + * @return {@link DevHelper} + */ + public DevHelper getNetTime(final String urlStr, final HttpURLConnectionUtils.TimeCallBack timeCallBack) { + HttpURLConnectionUtils.getNetTime(urlStr, timeCallBack); + return this; + } + + // ============== + // = TimeKeeper = + // ============== + + /** + * 设置等待一段时间后, 通知方法 ( 异步 ) + * @param keepTimeMillis 堵塞时间 ( 毫秒 ) + * @param endCallback 结束回调通知 + * @return {@link DevHelper} + */ + public DevHelper waitForEndAsyn(final long keepTimeMillis, final TimeKeeper.OnEndCallback endCallback) { + mTimeKeeper.waitForEndAsyn(keepTimeMillis, endCallback); + return this; + } + + /** + * 设置等待一段时间后, 通知方法 ( 同步 ) + * @param keepTimeMillis 堵塞时间 ( 毫秒 ) + * @param endCallback 结束回调通知 + * @return {@link DevHelper} + */ + public DevHelper waitForEnd(final long keepTimeMillis, final TimeKeeper.OnEndCallback endCallback) { + mTimeKeeper.waitForEnd(keepTimeMillis, endCallback); + return this; + } +} diff --git a/lib/DevOther/src/main/java/dev/ViewHelper.java b/lib/DevOther/src/main/java/dev/ViewHelper.java new file mode 100644 index 0000000000..483e1e7b31 --- /dev/null +++ b/lib/DevOther/src/main/java/dev/ViewHelper.java @@ -0,0 +1,2168 @@ +package dev; + +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.FloatRange; +import android.support.annotation.IdRes; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.KeyListener; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; + +import dev.utils.app.ClickUtils; +import dev.utils.app.EditTextUtils; +import dev.utils.app.HandlerUtils; +import dev.utils.app.ImageViewUtils; +import dev.utils.app.ListenerUtils; +import dev.utils.app.TextViewUtils; +import dev.utils.app.ViewUtils; + +/** + * detail: View 链式调用快捷设置 Helper 类 + * @author Ttt + *
+ *     通过 DevApp 工具类快捷实现
+ *     

+ * DevApp Api + * @see
+ *
+ */ +public final class ViewHelper { + + // ViewHelper + private static final ViewHelper HELPER = new ViewHelper(); + + /** + * 获取单例 ViewHelper + * @return {@link ViewHelper} + */ + public static ViewHelper get() { + return HELPER; + } + + // ========== + // = Helper = + // ========== + + /** + * 获取 ViewHelper + * @return {@link ViewHelper} + */ + public ViewHelper viewHelper() { + return this; + } + + /** + * 获取 DevHelper + * @return {@link DevHelper} + */ + public DevHelper devHelper() { + return DevHelper.get(); + } + + // =========== + // = Handler = + // =========== + + /** + * 在主线程 Handler 中执行任务 + * @param runnable 可执行的任务 + * @return {@link ViewHelper} + */ + public ViewHelper postRunnable(final Runnable runnable) { + HandlerUtils.postRunnable(runnable); + return this; + } + + /** + * 在主线程 Handler 中执行延迟任务 + * @param runnable 可执行的任务 + * @param delayMillis 延迟时间 + * @return {@link ViewHelper} + */ + public ViewHelper postRunnable(final Runnable runnable, final long delayMillis) { + HandlerUtils.postRunnable(runnable, delayMillis); + return this; + } + + /** + * 在主线程 Handler 中执行延迟任务 + * @param runnable 可执行的任务 + * @param delayMillis 延迟时间 + * @param number 轮询次数 + * @param interval 轮询时间 + * @return {@link ViewHelper} + */ + public ViewHelper postRunnable(final Runnable runnable, final long delayMillis, final int number, final int interval) { + HandlerUtils.postRunnable(runnable, delayMillis, number, interval); + return this; + } + + /** + * 在主线程 Handler 中执行延迟任务 + * @param runnable 可执行的任务 + * @param delayMillis 延迟时间 + * @param number 轮询次数 + * @param interval 轮询时间 + * @param onEndListener 结束通知 + * @return {@link ViewHelper} + */ + public ViewHelper postRunnable(final Runnable runnable, final long delayMillis, final int number, final int interval, final HandlerUtils.OnEndListener onEndListener) { + HandlerUtils.postRunnable(runnable, delayMillis, number, interval, onEndListener); + return this; + } + + /** + * 在主线程 Handler 中清除任务 + * @param runnable 需要清除的任务 + * @return {@link ViewHelper} + */ + public ViewHelper removeRunnable(final Runnable runnable) { + HandlerUtils.removeRunnable(runnable); + return this; + } + + // ======== + // = Text = + // ======== + + /** + * 设置 Hint 文本 + * @param view {@link TextView} + * @param text Hint text + * @return {@link ViewHelper} + */ + public ViewHelper setHint(final View view, final String text) { + TextViewUtils.setHint(view, text); + return this; + } + + /** + * 设置文本 + * @param view {@link TextView} + * @param text TextView text + * @return {@link ViewHelper} + */ + public ViewHelper setText(final View view, final String text) { + if (view instanceof EditText) { + EditTextUtils.setText(EditTextUtils.getEditText(view), text); + } else { + TextViewUtils.setText(view, text); + } + return this; + } + + /** + * 设置多个 TextView 文本 + * @param text TextView text + * @param views View(TextView)[] + * @return {@link ViewHelper} + */ + public ViewHelper setTexts(final String text, final View... views) { + TextViewUtils.setTexts(text, views); + return this; + } + + /** + * 设置 Html 内容 + * @param view {@link TextView} + * @param content Html content + * @return {@link ViewHelper} + */ + public ViewHelper setHtmlText(final View view, final String content) { + TextViewUtils.setHtmlText(view, content); + return this; + } + + /** + * 设置多个 TextView Html 内容 + * @param content Html content + * @param views View(TextView)[] + * @return {@link ViewHelper} + */ + public ViewHelper setHtmlTexts(final String content, final View... views) { + TextViewUtils.setHtmlTexts(content, views); + return this; + } + + /** + * 设置 Hint 字体颜色 + * @param view {@link TextView} + * @param color R.color.id + * @return {@link ViewHelper} + */ + public ViewHelper setHintTextColor(final View view, @ColorInt final int color) { + TextViewUtils.setHintTextColor(view, color); + return this; + } + + /** + * 设置 Hint 字体颜色 + * @param view {@link TextView} + * @param colors {@link ColorStateList} + * @return {@link ViewHelper} + */ + public ViewHelper setHintTextColor(final View view, final ColorStateList colors) { + TextViewUtils.setHintTextColor(view, colors); + return this; + } + + /** + * 设置多个 TextView Hint 字体颜色 + * @param color R.color.id + * @param views View(TextView)[] + * @return {@link ViewHelper} + */ + public ViewHelper setHintTextColors(@ColorInt final int color, final View... views) { + TextViewUtils.setHintTextColors(color, views); + return this; + } + + /** + * 设置多个 TextView Hint 字体颜色 + * @param colors {@link ColorStateList} + * @param views View(TextView)[] + * @return {@link ViewHelper} + */ + public ViewHelper setHintTextColors(final ColorStateList colors, final View... views) { + TextViewUtils.setHintTextColors(colors, views); + return this; + } + + /** + * 设置字体颜色 + * @param view {@link TextView} + * @param color R.color.id + * @return {@link ViewHelper} + */ + public ViewHelper setTextColor(final View view, @ColorInt final int color) { + TextViewUtils.setTextColor(view, color); + return this; + } + + /** + * 设置字体颜色 + * @param view {@link TextView} + * @param colors {@link ColorStateList} + * @return {@link ViewHelper} + */ + public ViewHelper setTextColor(final View view, final ColorStateList colors) { + TextViewUtils.setTextColor(view, colors); + return this; + } + + /** + * 设置多个 TextView 字体颜色 + * @param color R.color.id + * @param views View(TextView)[] + * @return {@link ViewHelper} + */ + public ViewHelper setTextColors(@ColorInt final int color, final View... views) { + TextViewUtils.setTextColors(color, views); + return this; + } + + /** + * 设置多个 TextView 字体颜色 + * @param colors {@link ColorStateList} + * @param views View(TextView)[] + * @return {@link ViewHelper} + */ + public ViewHelper setTextColors(final ColorStateList colors, final View... views) { + TextViewUtils.setTextColors(colors, views); + return this; + } + + /** + * 设置字体 + * @param view {@link TextView} + * @param typeface {@link Typeface} 字体样式 + * @return {@link ViewHelper} + */ + public ViewHelper setTypeface(final View view, final Typeface typeface) { + TextViewUtils.setTypeface(view, typeface); + return this; + } + + /** + * 设置字体大小 - px 像素 + * @param view {@link TextView} + * @param size 字体大小 + * @return {@link ViewHelper} + */ + public ViewHelper setTextSizeByPx(final View view, final float size) { + TextViewUtils.setTextSizeByPx(view, size); + return this; + } + + /** + * 设置字体大小 - sp 缩放像素 + * @param view {@link TextView} + * @param size 字体大小 + * @return {@link ViewHelper} + */ + public ViewHelper setTextSizeBySp(final View view, final float size) { + TextViewUtils.setTextSizeBySp(view, size); + return this; + } + + /** + * 设置字体大小 - dp 与设备无关的像素 + * @param view {@link TextView} + * @param size 字体大小 + * @return {@link ViewHelper} + */ + public ViewHelper setTextSizeByDp(final View view, final float size) { + TextViewUtils.setTextSizeByDp(view, size); + return this; + } + + /** + * 设置字体大小 - inches 英寸 + * @param view {@link TextView} + * @param size 字体大小 + * @return {@link ViewHelper} + */ + public ViewHelper setTextSizeByIn(final View view, final float size) { + TextViewUtils.setTextSizeByIn(view, size); + return this; + } + + /** + * 设置字体大小 + * @param view {@link TextView} + * @param unit 字体参数类型 + * @param size 字体大小 + * @return {@link ViewHelper} + */ + public ViewHelper setTextSize(final View view, final int unit, final float size) { + TextViewUtils.setTextSize(view, unit, size); + return this; + } + + /** + * 设置多个 TextView 字体大小 + * @param views View(TextView)[] + * @param unit 参数类型 + * @param size 字体大小 + * @return {@link ViewHelper} + */ + public ViewHelper setTextSizes(final View[] views, final int unit, final float size) { + TextViewUtils.setTextSizes(views, unit, size); + return this; + } + + /** + * 清空 flags + * @param view {@link TextView} + * @return {@link ViewHelper} + */ + public ViewHelper clearFlags(final View view) { + TextViewUtils.clearFlags(view); + return this; + } + + /** + * 设置 TextView 是否加粗 + * @param view {@link TextView} + * @param isBold {@code true} yes, {@code false} no + * @return {@link ViewHelper} + */ + public ViewHelper setBold(final View view, final boolean isBold) { + TextViewUtils.setBold(view, isBold); + return this; + } + + /** + * 设置 TextView 是否加粗 + * @param view {@link TextView} + * @param typeface {@link Typeface} 字体样式 + * @param isBold {@code true} yes, {@code false} no + * @return {@link ViewHelper} + */ + public ViewHelper setBold(final View view, final Typeface typeface, final boolean isBold) { + TextViewUtils.setBold(view, typeface, isBold); + return this; + } + + /** + * 设置下划线 + * @param view {@link TextView} + * @return {@link ViewHelper} + */ + public ViewHelper setUnderlineText(final View view) { + TextViewUtils.setUnderlineText(view); + return this; + } + + /** + * 设置下划线并加清晰 + * @param view {@link TextView} + * @param isAntiAlias 是否消除锯齿 + * @return {@link ViewHelper} + */ + public ViewHelper setUnderlineText(final View view, final boolean isAntiAlias) { + TextViewUtils.setUnderlineText(view, isAntiAlias); + return this; + } + + /** + * 设置中划线 + * @param view {@link TextView} + * @return {@link ViewHelper} + */ + public ViewHelper setStrikeThruText(final View view) { + TextViewUtils.setStrikeThruText(view); + return this; + } + + /** + * 设置中划线并加清晰 + * @param view {@link TextView} + * @param isAntiAlias 是否消除锯齿 + * @return {@link ViewHelper} + */ + public ViewHelper setStrikeThruText(final View view, final boolean isAntiAlias) { + TextViewUtils.setStrikeThruText(view, isAntiAlias); + return this; + } + + /** + * 设置文字水平间距 + * @param view {@link TextView} + * @param letterSpacing 文字水平间距值 + * @return {@link ViewHelper} + */ + public ViewHelper setLetterSpacing(final View view, final float letterSpacing) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + TextViewUtils.setLetterSpacing(view, letterSpacing); + } + return this; + } + + /** + * 设置文字行间距 ( 行高 ) + * @param view {@link TextView} + * @param lineSpacing 文字行间距 ( 行高 ), android:lineSpacingExtra + * @return {@link ViewHelper} + */ + public ViewHelper setLineSpacing(final View view, final float lineSpacing) { + TextViewUtils.setLineSpacing(view, lineSpacing); + return this; + } + + /** + * 设置文字行间距 ( 行高 )、行间距倍数 + * @param view {@link TextView} + * @param lineSpacing 文字行间距 ( 行高 ), android:lineSpacingExtra + * @param multiplier 行间距倍数, android:lineSpacingMultiplier + * @return {@link ViewHelper} + */ + public ViewHelper setLineSpacingAndMultiplier(final View view, final float lineSpacing, final float multiplier) { + TextViewUtils.setLineSpacingAndMultiplier(view, lineSpacing, multiplier); + return this; + } + + /** + * 设置字体水平方向的缩放 + * @param view {@link TextView} + * @param size 缩放比例 + * @return {@link ViewHelper} + */ + public ViewHelper setTextScaleX(final View view, final float size) { + TextViewUtils.setTextScaleX(view, size); + return this; + } + + /** + * 设置是否保留字体留白间隙区域 + * @param view {@link TextView} + * @param includepad 是否保留字体留白间隙区域 + * @return {@link ViewHelper} + */ + public ViewHelper setIncludeFontPadding(final View view, final boolean includepad) { + TextViewUtils.setIncludeFontPadding(view, includepad); + return this; + } + + /** + * 设置输入类型 + * @param view {@link TextView} + * @param type 类型 + * @return {@link ViewHelper} + */ + public ViewHelper setInputType(final View view, final int type) { + TextViewUtils.setInputType(view, type); + return this; + } + + /** + * 设置软键盘右下角按钮类型 + * @param view {@link TextView} + * @param imeOptions 软键盘按钮类型 + * @return {@link ViewHelper} + */ + public ViewHelper setImeOptions(final View view, final int imeOptions) { + TextViewUtils.setImeOptions(view, imeOptions); + return this; + } + + /** + * 设置行数 + * @param view {@link TextView} + * @param lines 行数 + * @return {@link ViewHelper} + */ + public ViewHelper setLines(final View view, final int lines) { + TextViewUtils.setLines(view, lines); + return this; + } + + /** + * 设置最大行数 + * @param view {@link TextView} + * @param maxLines 最大行数 + * @return {@link ViewHelper} + */ + public ViewHelper setMaxLines(final View view, final int maxLines) { + TextViewUtils.setMaxLines(view, maxLines); + return this; + } + + /** + * 设置最小行数 + * @param view {@link TextView} + * @param minLines 最小行数 + * @return {@link ViewHelper} + */ + public ViewHelper setMinLines(final View view, final int minLines) { + TextViewUtils.setMinLines(view, minLines); + return this; + } + + /** + * 设置最大字符宽度限制 + * @param view {@link TextView} + * @param maxEms 最大字符 + * @return {@link ViewHelper} + */ + public ViewHelper setMaxEms(final View view, final int maxEms) { + TextViewUtils.setMaxEms(view, maxEms); + return this; + } + + /** + * 设置最小字符宽度限制 + * @param view {@link TextView} + * @param minEms 最小字符 + * @return {@link ViewHelper} + */ + public ViewHelper setMinEms(final View view, final int minEms) { + TextViewUtils.setMinEms(view, minEms); + return this; + } + + /** + * 设置指定字符宽度 + * @param view {@link TextView} + * @param ems 字符 + * @return {@link ViewHelper} + */ + public ViewHelper setEms(final View view, final int ems) { + TextViewUtils.setEms(view, ems); + return this; + } + + /** + * 设置 Ellipsize 效果 + * @param view {@link TextView} + * @param where {@link TextUtils.TruncateAt} + * @return {@link ViewHelper} + */ + public ViewHelper setEllipsize(final View view, final TextUtils.TruncateAt where) { + TextViewUtils.setEllipsize(view, where); + return this; + } + + /** + * 设置自动识别文本链接 + * @param view {@link TextView} + * @param mask {@link android.text.util.Linkify} + * @return {@link ViewHelper} + */ + public ViewHelper setAutoLinkMask(final View view, final int mask) { + TextViewUtils.setAutoLinkMask(view, mask); + return this; + } + + /** + * 设置文本全为大写 + * @param view {@link TextView} + * @param allCaps 是否全部大写 + * @return {@link ViewHelper} + */ + public ViewHelper setAllCaps(final View view, final boolean allCaps) { + TextViewUtils.setAllCaps(view, allCaps); + return this; + } + + /** + * 设置 Text Gravity + * @param view {@link TextView} + * @param gravity {@link android.view.Gravity} + * @return {@link ViewHelper} + */ + public ViewHelper setTextGravity(final View view, final int gravity) { + TextViewUtils.setGravity(view, gravity); + return this; + } + + // ============ + // = EditText = + // ============ + + /** + * 设置内容 + * @param editText {@link EditText} + * @param content 文本内容 + * @param isSelect 是否设置光标 + * @return {@link ViewHelper} + */ + public ViewHelper setText(final EditText editText, final String content, final boolean isSelect) { + EditTextUtils.setText(editText, content, isSelect); + return this; + } + + /** + * 追加内容 ( 当前光标位置追加 ) + * @param editText {@link EditText} + * @param content 文本内容 + * @param isSelect 是否设置光标 + * @return {@link ViewHelper} + */ + public ViewHelper insert(final EditText editText, final String content, final boolean isSelect) { + EditTextUtils.insert(editText, content, isSelect); + return this; + } + + /** + * 追加内容 + * @param editText {@link EditText} + * @param content 文本内容 + * @param start 开始添加的位置 + * @param isSelect 是否设置光标 + * @return {@link ViewHelper} + */ + public ViewHelper insert(final EditText editText, final String content, final int start, final boolean isSelect) { + EditTextUtils.insert(editText, content, start, isSelect); + return this; + } + + /** + * 设置长度限制 + * @param view {@link View} + * @param maxLength 长度限制 + * @return {@link ViewHelper} + */ + public ViewHelper setMaxLength(final View view, final int maxLength) { + if (view instanceof EditText) { + EditTextUtils.setMaxLength(EditTextUtils.getEditText(view), maxLength); + } else { + TextViewUtils.setMaxLength(view, maxLength); + } + return this; + } + + /** + * 设置长度限制, 并且设置内容 + * @param view {@link View} + * @param content 文本内容 + * @param maxLength 长度限制 + * @return {@link ViewHelper} + */ + public ViewHelper setMaxLengthAndText(final View view, final String content, final int maxLength) { + if (view instanceof EditText) { + EditTextUtils.setMaxLengthAndText(EditTextUtils.getEditText(view), content, maxLength); + } else { + TextViewUtils.setMaxLengthAndText(view, content, maxLength); + } + return this; + } + + /** + * 设置是否显示光标 + * @param editText {@link EditText} + * @param visible 是否显示光标 + * @return {@link ViewHelper} + */ + public ViewHelper setCursorVisible(final EditText editText, final boolean visible) { + EditTextUtils.setCursorVisible(editText, visible); + return this; + } + + /** + * 设置光标在第一位 + * @param editText {@link EditText} + * @return {@link ViewHelper} + */ + public ViewHelper setSelectionToTop(final EditText editText) { + EditTextUtils.setSelectionToTop(editText); + return this; + } + + /** + * 设置光标在最后一位 + * @param editText {@link EditText} + * @return {@link ViewHelper} + */ + public ViewHelper setSelectionToBottom(final EditText editText) { + EditTextUtils.setSelectionToBottom(editText); + return this; + } + + /** + * 设置光标位置 + * @param editText {@link EditText} + * @param index 光标位置 + * @return {@link ViewHelper} + */ + public ViewHelper setSelection(final EditText editText, final int index) { + EditTextUtils.setSelection(editText, index); + return this; + } + + /** + * 添加输入监听事件 + * @param editText {@link EditText} + * @param watcher 输入监听 + * @return {@link ViewHelper} + */ + public ViewHelper addTextChangedListener(final EditText editText, final TextWatcher watcher) { + EditTextUtils.addTextChangedListener(editText, watcher); + return this; + } + + /** + * 移除输入监听事件 + * @param editText {@link EditText} + * @param watcher 输入监听 + * @return {@link ViewHelper} + */ + public ViewHelper removeTextChangedListener(final EditText editText, final TextWatcher watcher) { + EditTextUtils.removeTextChangedListener(editText, watcher); + return this; + } + + /** + * 设置 KeyListener + * @param editText {@link EditText} + * @param keyListener {@link KeyListener} + * @return {@link ViewHelper} + */ + public ViewHelper setKeyListener(final EditText editText, final KeyListener keyListener) { + EditTextUtils.setKeyListener(editText, keyListener); + return this; + } + + /** + * 设置 KeyListener + * @param editText {@link EditText} + * @param accepted 允许输入的内容, 如: 0123456789 + * @return {@link ViewHelper} + */ + public ViewHelper setKeyListener(final EditText editText, final String accepted) { + EditTextUtils.setKeyListener(editText, accepted); + return this; + } + + /** + * 设置 KeyListener + * @param editText {@link EditText} + * @param accepted 允许输入的内容 + * @return {@link ViewHelper} + */ + public ViewHelper setKeyListener(final EditText editText, final char[] accepted) { + EditTextUtils.setKeyListener(editText, accepted); + return this; + } + + // ========= + // = Image = + // ========= + + /** + * 设置 ImageView 是否保持宽高比 + * @param imageView ImageView + * @param adjustViewBounds 是否调整此视图的边界以保持可绘制的原始纵横比 + * @return {@link ViewHelper} + */ + public ViewHelper setAdjustViewBounds(final ImageView imageView, final boolean adjustViewBounds) { + ImageViewUtils.setAdjustViewBounds(imageView, adjustViewBounds); + return this; + } + + /** + * 设置 ImageView 最大高度 + * @param imageView ImageView + * @param maxHeight 最大高度 + * @return {@link ViewHelper} + */ + public ViewHelper setMaxHeight(final ImageView imageView, final int maxHeight) { + ImageViewUtils.setMaxHeight(imageView, maxHeight); + return this; + } + + /** + * 设置 ImageView 最大宽度 + * @param imageView ImageView + * @param maxWidth 最大宽度 + * @return {@link ViewHelper} + */ + public ViewHelper setMaxWidth(final ImageView imageView, final int maxWidth) { + ImageViewUtils.setMaxWidth(imageView, maxWidth); + return this; + } + + // = + + /** + * 设置背景图片 + * @param view {@link View} + * @param background 背景图片 + * @return {@link ViewHelper} + */ + public ViewHelper setBackground(final View view, final Drawable background) { + ImageViewUtils.setBackground(view, background); + return this; + } + + /** + * 设置背景颜色 + * @param view {@link View} + * @param color 背景颜色 + * @return {@link ViewHelper} + */ + public ViewHelper setBackgroundColor(final View view, @ColorInt final int color) { + ImageViewUtils.setBackgroundColor(view, color); + return this; + } + + /** + * 设置背景资源 + * @param view {@link View} + * @param resId resource identifier + * @return {@link ViewHelper} + */ + public ViewHelper setBackgroundResource(final View view, @DrawableRes final int resId) { + ImageViewUtils.setBackgroundResource(view, resId); + return this; + } + + /** + * 设置背景着色颜色 + * @param view {@link View} + * @param tint 着色颜色 + * @return {@link ViewHelper} + */ + public ViewHelper setBackgroundTintList(final View view, final ColorStateList tint) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ImageViewUtils.setBackgroundTintList(view, tint); + } + return this; + } + + /** + * 设置背景着色模式 + * @param view {@link View} + * @param tintMode 着色模式 {@link PorterDuff.Mode} + * @return {@link ViewHelper} + */ + public ViewHelper setBackgroundTintMode(final View view, final PorterDuff.Mode tintMode) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ImageViewUtils.setBackgroundTintMode(view, tintMode); + } + return this; + } + + /** + * 设置前景图片 + * @param view {@link View} + * @param foreground 前景图片 + * @return {@link ViewHelper} + */ + public ViewHelper setForeground(final View view, final Drawable foreground) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ImageViewUtils.setForeground(view, foreground); + } + return this; + } + + /** + * 设置前景重心 + * @param view {@link View} + * @param gravity 重心 + * @return {@link ViewHelper} + */ + public ViewHelper setForegroundGravity(final View view, final int gravity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ImageViewUtils.setForegroundGravity(view, gravity); + } + return this; + } + + /** + * 设置前景着色颜色 + * @param view {@link View} + * @param tint 着色颜色 + * @return {@link ViewHelper} + */ + public ViewHelper setForegroundTintList(final View view, final ColorStateList tint) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ImageViewUtils.setForegroundTintList(view, tint); + } + return this; + } + + /** + * 设置前景着色模式 + * @param view {@link View} + * @param tintMode 着色模式 {@link PorterDuff.Mode} + * @return {@link ViewHelper} + */ + public ViewHelper setForegroundTintMode(final View view, final PorterDuff.Mode tintMode) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ImageViewUtils.setForegroundTintMode(view, tintMode); + } + return this; + } + + /** + * 设置 ImageView Bitmap + * @param view {@link View} + * @param bitmap {@link Bitmap} + * @return {@link ViewHelper} + */ + public ViewHelper setImageBitmap(final View view, final Bitmap bitmap) { + ImageViewUtils.setImageBitmap(view, bitmap); + return this; + } + + /** + * 设置 ImageView Drawable + * @param view {@link View} + * @param drawable {@link Bitmap} + * @return {@link ViewHelper} + */ + public ViewHelper setImageDrawable(final View view, final Drawable drawable) { + ImageViewUtils.setImageDrawable(view, drawable); + return this; + } + + /** + * 设置 ImageView 资源 + * @param view {@link View} + * @param resId resource identifier + * @return {@link ViewHelper} + */ + public ViewHelper setImageResource(final View view, @DrawableRes final int resId) { + ImageViewUtils.setImageResource(view, resId); + return this; + } + + /** + * 设置 ImageView Matrix + * @param view {@link View} + * @param matrix {@link Matrix} + * @return {@link ViewHelper} + */ + public ViewHelper setImageMatrix(final View view, final Matrix matrix) { + ImageViewUtils.setImageMatrix(view, matrix); + return this; + } + + /** + * 设置 ImageView 着色颜色 + * @param view {@link View} + * @param tint 着色颜色 + * @return {@link ViewHelper} + */ + public ViewHelper setImageTintList(final View view, final ColorStateList tint) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ImageViewUtils.setImageTintList(view, tint); + } + return this; + } + + /** + * 设置 ImageView 着色模式 + * @param view {@link View} + * @param tintMode 着色模式 {@link PorterDuff.Mode} + * @return {@link ViewHelper} + */ + public ViewHelper setImageTintMode(final View view, final PorterDuff.Mode tintMode) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ImageViewUtils.setImageTintMode(view, tintMode); + } + return this; + } + + /** + * 设置 ImageView 缩放类型 + * @param view {@link View} + * @param scaleType 缩放类型 {@link ImageView.ScaleType} + * @return {@link ViewHelper} + */ + public ViewHelper setScaleType(final View view, final ImageView.ScaleType scaleType) { + ImageViewUtils.setScaleType(view, scaleType); + return this; + } + + /** + * ImageView 着色处理 + * @param imageView {@link ImageView} + * @param color 颜色值 + * @return {@link ViewHelper} + */ + public ViewHelper setColorFilter(final ImageView imageView, @ColorInt final int color) { + ImageViewUtils.setColorFilter(imageView, color); + return this; + } + + /** + * ImageView 着色处理, 并且设置 Drawable + * @param imageView {@link ImageView} + * @param drawable {@link Drawable} + * @param color 颜色值 + * @return {@link ViewHelper} + */ + public ViewHelper setColorFilter(final ImageView imageView, final Drawable drawable, @ColorInt final int color) { + ImageViewUtils.setColorFilter(imageView, drawable, color); + return this; + } + + /** + * ImageView 着色处理 + * @param imageView {@link ImageView} + * @param colorFilter 颜色过滤 ( 效果 ) + * @return {@link ViewHelper} + */ + public ViewHelper setColorFilter(final ImageView imageView, final ColorFilter colorFilter) { + ImageViewUtils.setColorFilter(imageView, colorFilter); + return this; + } + + /** + * ImageView 着色处理, 并且设置 Drawable + * @param imageView {@link ImageView} + * @param drawable {@link Drawable} + * @param colorFilter 颜色过滤 ( 效果 ) + * @return {@link ViewHelper} + */ + public ViewHelper setColorFilter(final ImageView imageView, final Drawable drawable, final ColorFilter colorFilter) { + ImageViewUtils.setColorFilter(imageView, drawable, colorFilter); + return this; + } + + /** + * 设置 View 图片资源 + * @param resId resource identifier + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setBackgroundResources(@DrawableRes final int resId, final View... views) { + ImageViewUtils.setBackgroundResources(resId, views); + return this; + } + + /** + * 设置 View 图片资源 + * @param resId resource identifier + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setBackgroundResources(@DrawableRes final int resId, final int isVisibility, final View... views) { + ImageViewUtils.setBackgroundResources(resId, isVisibility, views); + return this; + } + + /** + * 设置 View 图片资源 + * @param resId resource identifier + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setImageResources(@DrawableRes final int resId, final View... views) { + ImageViewUtils.setImageResources(resId, views); + return this; + } + + /** + * 设置 View 图片资源 + * @param resId resource identifier + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setImageResources(@DrawableRes final int resId, final int isVisibility, final View... views) { + ImageViewUtils.setImageResources(resId, isVisibility, views); + return this; + } + + /** + * 设置 View Bitmap + * @param bitmap {@link Bitmap} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setImageBitmaps(final Bitmap bitmap, final View... views) { + ImageViewUtils.setImageBitmaps(bitmap, views); + return this; + } + + /** + * 设置 View Bitmap + * @param bitmap {@link Bitmap} + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setImageBitmaps(final Bitmap bitmap, final int isVisibility, final View... views) { + ImageViewUtils.setImageBitmaps(bitmap, isVisibility, views); + return this; + } + + /** + * 设置 View Drawable + * @param drawable {@link drawable} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setImageDrawables(final Drawable drawable, final View... views) { + ImageViewUtils.setImageDrawables(drawable, views); + return this; + } + + /** + * 设置 View Drawable + * @param drawable {@link drawable} + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setImageDrawables(final Drawable drawable, final int isVisibility, final View... views) { + ImageViewUtils.setImageDrawables(drawable, isVisibility, views); + return this; + } + + /** + * 设置 View 缩放模式 + * @param scaleType {@link ImageView.ScaleType} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setScaleTypes(final ImageView.ScaleType scaleType, final View... views) { + ImageViewUtils.setScaleTypes(scaleType, views); + return this; + } + + /** + * 设置 View 缩放模式 + * @param scaleType {@link ImageView.ScaleType} + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setScaleTypes(final ImageView.ScaleType scaleType, final int isVisibility, final View... views) { + ImageViewUtils.setScaleTypes(scaleType, isVisibility, views); + return this; + } + + // ======== + // = View = + // ======== + + /** + * 设置 View 最小高度 + * @param view View + * @param minHeight 最小高度 + * @return {@link ViewHelper} + */ + public ViewHelper setMinimumHeight(final View view, final int minHeight) { + ViewUtils.setMinimumHeight(view, minHeight); + return this; + } + + /** + * 设置 View 最小宽度 + * @param view View + * @param minWidth 最小宽度 + * @return {@link ViewHelper} + */ + public ViewHelper setMinimumWidth(final View view, final int minWidth) { + ViewUtils.setMinimumWidth(view, minWidth); + return this; + } + + /** + * 设置 View 透明度 + * @param view View + * @param alpha 透明度 + * @return {@link ViewHelper} + */ + public ViewHelper setAlpha(final View view, @FloatRange(from = 0.0, to = 1.0) final float alpha) { + ViewUtils.setAlpha(view, alpha); + return this; + } + + /** + * 设置 View Tag + * @param view View + * @param object Tag + * @return {@link ViewHelper} + */ + public ViewHelper setTag(final View view, final Object object) { + ViewUtils.setTag(view, object); + return this; + } + + // = + + /** + * 设置 View 滚动效应 + * @param view View + * @param isScrollContainer 是否需要滚动效应 + * @return {@link ViewHelper} + */ + public ViewHelper setScrollContainer(final View view, final boolean isScrollContainer) { + ViewUtils.setScrollContainer(view, isScrollContainer); + return this; + } + + /** + * 设置下一个获取焦点的 View id + * @param view View + * @param nextFocusForwardId 下一个获取焦点的 View id + * @return {@link ViewHelper} + */ + public ViewHelper setNextFocusForwardId(final View view, @IdRes final int nextFocusForwardId) { + ViewUtils.setNextFocusForwardId(view, nextFocusForwardId); + return this; + } + + /** + * 设置向下移动焦点时, 下一个获取焦点的 View id + * @param view View + * @param nextFocusDownId 下一个获取焦点的 View id + * @return {@link ViewHelper} + */ + public ViewHelper setNextFocusDownId(final View view, @IdRes final int nextFocusDownId) { + ViewUtils.setNextFocusDownId(view, nextFocusDownId); + return this; + } + + /** + * 设置向左移动焦点时, 下一个获取焦点的 View id + * @param view View + * @param nextFocusLeftId 下一个获取焦点的 View id + * @return {@link ViewHelper} + */ + public ViewHelper setNextFocusLeftId(final View view, @IdRes final int nextFocusLeftId) { + ViewUtils.setNextFocusLeftId(view, nextFocusLeftId); + return this; + } + + /** + * 设置向右移动焦点时, 下一个获取焦点的 View id + * @param view View + * @param nextFocusRightId 下一个获取焦点的 View id + * @return {@link ViewHelper} + */ + public ViewHelper setNextFocusRightId(final View view, @IdRes final int nextFocusRightId) { + ViewUtils.setNextFocusRightId(view, nextFocusRightId); + return this; + } + + /** + * 设置向上移动焦点时, 下一个获取焦点的 View id + * @param view View + * @param nextFocusUpId 下一个获取焦点的 View id + * @return {@link ViewHelper} + */ + public ViewHelper setNextFocusUpId(final View view, @IdRes final int nextFocusUpId) { + ViewUtils.setNextFocusUpId(view, nextFocusUpId); + return this; + } + + /** + * 设置 View 旋转度数 + * @param view View + * @param rotation 旋转度数 + * @return {@link ViewHelper} + */ + public ViewHelper setRotation(final View view, final float rotation) { + ViewUtils.setRotation(view, rotation); + return this; + } + + /** + * 设置 View 水平旋转度数 + * @param view View + * @param rotationX 水平旋转度数 + * @return {@link ViewHelper} + */ + public ViewHelper setRotationX(final View view, final float rotationX) { + ViewUtils.setRotationX(view, rotationX); + return this; + } + + /** + * 设置 View 竖直旋转度数 + * @param view View + * @param rotationY 竖直旋转度数 + * @return {@link ViewHelper} + */ + public ViewHelper setRotationY(final View view, final float rotationY) { + ViewUtils.setRotationY(view, rotationY); + return this; + } + + /** + * 设置 View 水平方向缩放比例 + * @param view View + * @param scaleX 水平方向缩放比例 + * @return {@link ViewHelper} + */ + public ViewHelper setScaleX(final View view, final float scaleX) { + ViewUtils.setScaleX(view, scaleX); + return this; + } + + /** + * 设置 View 竖直方向缩放比例 + * @param view View + * @param scaleY 竖直方向缩放比例 + * @return {@link ViewHelper} + */ + public ViewHelper setScaleY(final View view, final float scaleY) { + ViewUtils.setScaleY(view, scaleY); + return this; + } + + /** + * 设置文本的显示方式 + * @param view View + * @param textAlignment 文本的显示方式 + * @return {@link ViewHelper} + */ + public ViewHelper setTextAlignment(final View view, final int textAlignment) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + ViewUtils.setTextAlignment(view, textAlignment); + } + return this; + } + + /** + * 设置文本的显示方向 + * @param view View + * @param textDirection 文本的显示方向 + * @return {@link ViewHelper} + */ + public ViewHelper setTextDirection(final View view, final int textDirection) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + ViewUtils.setTextDirection(view, textDirection); + } + return this; + } + + /** + * 设置水平方向偏转量 + * @param view View + * @param pivotX 水平方向偏转量 + * @return {@link ViewHelper} + */ + public ViewHelper setPivotX(final View view, final float pivotX) { + ViewUtils.setPivotX(view, pivotX); + return this; + } + + /** + * 设置竖直方向偏转量 + * @param view View + * @param pivotY 竖直方向偏转量 + * @return {@link ViewHelper} + */ + public ViewHelper setPivotY(final View view, final float pivotY) { + ViewUtils.setPivotY(view, pivotY); + return this; + } + + /** + * 设置水平方向的移动距离 + * @param view View + * @param translationX 水平方向的移动距离 + * @return {@link ViewHelper} + */ + public ViewHelper setTranslationX(final View view, final float translationX) { + ViewUtils.setTranslationX(view, translationX); + return this; + } + + /** + * 设置竖直方向的移动距离 + * @param view View + * @param translationY 竖直方向的移动距离 + * @return {@link ViewHelper} + */ + public ViewHelper setTranslationY(final View view, final float translationY) { + ViewUtils.setTranslationY(view, translationY); + return this; + } + + /** + * 设置 View 硬件加速类型 + * @param view View + * @param layerType 硬件加速类型 + * @param paint {@link Paint} + * @return {@link ViewHelper} + */ + public ViewHelper setLayerType(final View view, final int layerType, final Paint paint) { + ViewUtils.setLayerType(view, layerType, paint); + return this; + } + + // = + + /** + * 设置 View LayoutParams + * @param view {@link View} + * @param params LayoutParams + * @return {@link ViewHelper} + */ + public ViewHelper setLayoutParams(final View view, final ViewGroup.LayoutParams params) { + ViewUtils.setLayoutParams(view, params); + return this; + } + + /** + * 设置 View 是否可以获取焦点 + * @param focusable {@code true} 可获取, {@code false} 不可获取 + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setFocusable(final boolean focusable, final View... views) { + ViewUtils.setFocusable(focusable, views); + return this; + } + + /** + * 设置 View 是否选中 + * @param selected {@code true} 选中, {@code false} 非选中 + * @param views View[] + * @return {@code true} 选中, {@code false} 非选中 + */ + public ViewHelper setSelected(final boolean selected, final View... views) { + ViewUtils.setSelected(selected, views); + return this; + } + + /** + * 设置 View 是否启用 + * @param enabled {@code true} 启用, {@code false} 禁用 + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setEnabled(final boolean enabled, final View... views) { + ViewUtils.setEnabled(enabled, views); + return this; + } + + /** + * 设置 View 是否可以点击 + * @param clickable {@code true} 可点击, {@code false} 不可点击 + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setClickable(final boolean clickable, final View... views) { + ViewUtils.setClickable(clickable, views); + return this; + } + + /** + * 设置 View 是否可以长按 + * @param longClickable {@code true} 可长按, {@code false} 不可长按 + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setLongClickable(final boolean longClickable, final View... views) { + ViewUtils.setLongClickable(longClickable, views); + return this; + } + + /** + * 设置 View 显示的状态 + * @param isVisibility {@code true} View.VISIBLE, {@code false} View.GONE + * @param view {@link View} + * @return {@link ViewHelper} + */ + public ViewHelper setVisibility(final boolean isVisibility, final View view) { + ViewUtils.setVisibility(isVisibility, view); + return this; + } + + /** + * 设置 View 显示的状态 + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param view {@link View} + * @return {@link ViewHelper} + */ + public ViewHelper setVisibility(final int isVisibility, final View view) { + ViewUtils.setVisibility(isVisibility, view); + return this; + } + + /** + * 设置 View 显示的状态 + * @param isVisibility {@code true} View.VISIBLE, {@code false} View.GONE + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setVisibilitys(final boolean isVisibility, final View... views) { + ViewUtils.setVisibilitys(isVisibility, views); + return this; + } + + /** + * 设置 View 显示的状态 + * @param isVisibility {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper setVisibilitys(final int isVisibility, final View... views) { + ViewUtils.setVisibilitys(isVisibility, views); + return this; + } + + /** + * 切换 View 显示的状态 + * @param view {@link View} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper toggleVisibilitys(final View view, final View... views) { + ViewUtils.toggleVisibilitys(view, views); + return this; + } + + /** + * 切换 View 显示的状态 + * @param viewArys View[] + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper toggleVisibilitys(final View[] viewArys, final View... views) { + ViewUtils.toggleVisibilitys(viewArys, views); + return this; + } + + /** + * 切换 View 显示的状态 + * @param status {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param viewArys View[] + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper toggleVisibilitys(final int status, final View[] viewArys, final View... views) { + ViewUtils.toggleVisibilitys(status, viewArys, views); + return this; + } + + /** + * 反转 View 显示的状态 + * @param status {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param viewArys View[] + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper reverseVisibilitys(final int status, final View[] viewArys, final View... views) { + ViewUtils.reverseVisibilitys(status, viewArys, views); + return this; + } + + /** + * 反转 View 显示的状态 + * @param isVisibility {@code true} View.VISIBLE, {@code false} View.GONE + * @param viewArys View[] + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper reverseVisibilitys(final boolean isVisibility, final View[] viewArys, final View... views) { + ViewUtils.reverseVisibilitys(isVisibility, viewArys, views); + return this; + } + + /** + * 反转 View 显示的状态 + * @param status {@link View#VISIBLE}、{@link View#INVISIBLE}、{@link View#GONE} + * @param view {@link View} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper reverseVisibilitys(final int status, final View view, final View... views) { + ViewUtils.reverseVisibilitys(status, view, views); + return this; + } + + /** + * 反转 View 显示的状态 + * @param isVisibility {@code true} View.VISIBLE, {@code false} View.GONE + * @param view {@link View} + * @param views View[] + * @return {@link ViewHelper} + */ + public ViewHelper reverseVisibilitys(final boolean isVisibility, final View view, final View... views) { + ViewUtils.reverseVisibilitys(isVisibility, view, views); + return this; + } + + /** + * 把自身从父 View 中移除 + * @param view {@link View} + * @return {@link ViewHelper} + */ + public ViewHelper removeSelfFromParent(final View view) { + ViewUtils.removeSelfFromParent(view); + return this; + } + + /** + * 设置 View Layout Gravity + * @param view {@link View} + * @param gravity Gravity + * @return {@link ViewHelper} + */ + public ViewHelper setLayoutGravity(final View view, final int gravity) { + ViewUtils.setLayoutGravity(view, gravity); + return this; + } + + /** + * 设置 View Left Margin + * @param view {@link View} + * @param leftMargin Left Margin + * @return {@link ViewHelper} + */ + public ViewHelper setMarginLeft(final View view, final int leftMargin) { + ViewUtils.setMarginLeft(view, leftMargin); + return this; + } + + /** + * 设置 View Left Margin + * @param view {@link View} + * @param leftMargin Left Margin + * @param reset 是否重置清空其他 margin + * @return {@link ViewHelper} + */ + public ViewHelper setMarginLeft(final View view, final int leftMargin, final boolean reset) { + ViewUtils.setMarginLeft(view, leftMargin, reset); + return this; + } + + /** + * 设置 View Top Margin + * @param view {@link View} + * @param topMargin Top Margin + * @return {@link ViewHelper} + */ + public ViewHelper setMarginTop(final View view, final int topMargin) { + ViewUtils.setMarginTop(view, topMargin); + return this; + } + + /** + * 设置 View Top Margin + * @param view {@link View} + * @param topMargin Top Margin + * @param reset 是否重置清空其他 margin + * @return {@link ViewHelper} + */ + public ViewHelper setMarginTop(final View view, final int topMargin, final boolean reset) { + ViewUtils.setMarginTop(view, topMargin, reset); + return this; + } + + /** + * 设置 View Right Margin + * @param view {@link View} + * @param rightMargin Right Margin + * @return {@link ViewHelper} + */ + public ViewHelper setMarginRight(final View view, final int rightMargin) { + ViewUtils.setMarginRight(view, rightMargin); + return this; + } + + /** + * 设置 View Right Margin + * @param view {@link View} + * @param rightMargin Right Margin + * @param reset 是否重置清空其他 margin + * @return {@link ViewHelper} + */ + public ViewHelper setMarginRight(final View view, final int rightMargin, final boolean reset) { + ViewUtils.setMarginRight(view, rightMargin, reset); + return this; + } + + /** + * 设置 View Bottom Margin + * @param view {@link View} + * @param bottomMargin Bottom Margin + * @return {@link ViewHelper} + */ + public ViewHelper setMarginBottom(final View view, final int bottomMargin) { + ViewUtils.setMarginBottom(view, bottomMargin); + return this; + } + + /** + * 设置 View Bottom Margin + * @param view {@link View} + * @param bottomMargin Bottom Margin + * @param reset 是否重置清空其他 margin + * @return {@link ViewHelper} + */ + public ViewHelper setMarginBottom(final View view, final int bottomMargin, final boolean reset) { + ViewUtils.setMarginBottom(view, bottomMargin, reset); + return this; + } + + /** + * 设置 Margin 边距 + * @param view {@link View} + * @param leftRight Left and Right Margin + * @param topBottom Top and bottom Margin + * @return {@link ViewHelper} + */ + public ViewHelper setMargin(final View view, final int leftRight, final int topBottom) { + ViewUtils.setMargin(view, leftRight, topBottom); + return this; + } + + /** + * 设置 Margin 边距 + * @param view {@link View} + * @param margin Margin + * @return {@link ViewHelper} + */ + public ViewHelper setMargin(final View view, final int margin) { + ViewUtils.setMargin(view, margin); + return this; + } + + /** + * 设置 Margin 边距 + * @param view {@link View} + * @param left Left Margin + * @param top Top Margin + * @param right Right Margin + * @param bottom Bottom Margin + * @return {@link ViewHelper} + */ + public ViewHelper setMargin(final View view, final int left, final int top, final int right, final int bottom) { + ViewUtils.setMargin(view, left, top, right, bottom); + return this; + } + + /** + * 设置 Margin 边距 + * @param views View[] + * @param leftRight Left and Right Margin + * @param topBottom Top and bottom Margin + * @return {@link ViewHelper} + */ + public ViewHelper setMargin(final View[] views, final int leftRight, final int topBottom) { + ViewUtils.setMargin(views, leftRight, topBottom); + return this; + } + + /** + * 设置 Margin 边距 + * @param views View[] + * @param margin Margin + * @return {@link ViewHelper} + */ + public ViewHelper setMargin(final View[] views, final int margin) { + ViewUtils.setMargin(views, margin); + return this; + } + + /** + * 设置 Margin 边距 + * @param views View[] + * @param left Left Margin + * @param top Top Margin + * @param right Right Margin + * @param bottom Bottom Margin + * @return {@link ViewHelper} + */ + public ViewHelper setMargin(final View[] views, final int left, final int top, final int right, final int bottom) { + ViewUtils.setMargin(views, left, top, right, bottom); + return this; + } + + /** + * 设置 View Left Padding + * @param view {@link View} + * @param leftPadding Left Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPaddingLeft(final View view, final int leftPadding) { + ViewUtils.setPaddingLeft(view, leftPadding); + return this; + } + + /** + * 设置 View Left Padding + * @param view {@link View} + * @param leftPadding Left Padding + * @param reset 是否重置清空其他 Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPaddingLeft(final View view, final int leftPadding, final boolean reset) { + ViewUtils.setPaddingLeft(view, leftPadding, reset); + return this; + } + + /** + * 设置 View Top Padding + * @param view {@link View} + * @param topPadding Top Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPaddingTop(final View view, final int topPadding) { + ViewUtils.setPaddingTop(view, topPadding); + return this; + } + + /** + * 设置 View Top Padding + * @param view {@link View} + * @param topPadding Top Padding + * @param reset 是否重置清空其他 Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPaddingTop(final View view, final int topPadding, final boolean reset) { + ViewUtils.setPaddingTop(view, topPadding, reset); + return this; + } + + /** + * 设置 View Right Padding + * @param view {@link View} + * @param rightPadding Right Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPaddingRight(final View view, final int rightPadding) { + ViewUtils.setPaddingRight(view, rightPadding); + return this; + } + + /** + * 设置 View Right Padding + * @param view {@link View} + * @param rightPadding Right Padding + * @param reset 是否重置清空其他 Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPaddingRight(final View view, final int rightPadding, final boolean reset) { + ViewUtils.setPaddingRight(view, rightPadding, reset); + return this; + } + + /** + * 设置 View Bottom Padding + * @param view {@link View} + * @param bottomPadding Bottom Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPaddingBottom(final View view, final int bottomPadding) { + ViewUtils.setPaddingBottom(view, bottomPadding); + return this; + } + + /** + * 设置 View Bottom Padding + * @param view {@link View} + * @param bottomPadding Bottom Padding + * @param reset 是否重置清空其他 Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPaddingBottom(final View view, final int bottomPadding, final boolean reset) { + ViewUtils.setPaddingBottom(view, bottomPadding, reset); + return this; + } + + /** + * 设置 Padding 边距 + * @param view {@link View} + * @param leftRight Left and Right Padding + * @param topBottom Top and bottom Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPadding(final View view, final int leftRight, final int topBottom) { + ViewUtils.setPadding(view, leftRight, topBottom); + return this; + } + + /** + * 设置 Padding 边距 + * @param view {@link View} + * @param padding Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPadding(final View view, final int padding) { + ViewUtils.setPadding(view, padding); + return this; + } + + /** + * 设置 Padding 边距 + * @param view {@link View} + * @param left Left Padding + * @param top Top Padding + * @param right Right Padding + * @param bottom Bottom Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPadding(final View view, final int left, final int top, final int right, final int bottom) { + ViewUtils.setPadding(view, left, top, right, bottom); + return this; + } + + /** + * 设置 Padding 边距 + * @param views View[] + * @param leftRight Left and Right Padding + * @param topBottom Top and bottom Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPadding(final View[] views, final int leftRight, final int topBottom) { + ViewUtils.setPadding(views, leftRight, topBottom); + return this; + } + + /** + * 设置 Padding 边距 + * @param views View[] + * @param padding Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPadding(final View[] views, final int padding) { + ViewUtils.setPadding(views, padding); + return this; + } + + /** + * 设置 Padding 边距 + * @param views View[] + * @param left Left Padding + * @param top Top Padding + * @param right Right Padding + * @param bottom Bottom Padding + * @return {@link ViewHelper} + */ + public ViewHelper setPadding(final View[] views, final int left, final int top, final int right, final int bottom) { + ViewUtils.setPadding(views, left, top, right, bottom); + return this; + } + + /** + * 设置 Left CompoundDrawables + * @param textView {@link TextView} + * @param left left Drawable + * @return {@link ViewHelper} + */ + public ViewHelper setCompoundDrawablesByLeft(final TextView textView, final Drawable left) { + ViewUtils.setCompoundDrawablesByLeft(textView, left); + return this; + } + + /** + * 设置 Top CompoundDrawables + * @param textView {@link TextView} + * @param top top Drawable + * @return {@link ViewHelper} + */ + public ViewHelper setCompoundDrawablesByTop(final TextView textView, final Drawable top) { + ViewUtils.setCompoundDrawablesByTop(textView, top); + return this; + } + + /** + * 设置 Right CompoundDrawables + * @param textView {@link TextView} + * @param right right Drawable + * @return {@link ViewHelper} + */ + public ViewHelper setCompoundDrawablesByRight(final TextView textView, final Drawable right) { + ViewUtils.setCompoundDrawablesByRight(textView, right); + return this; + } + + /** + * 设置 Bottom CompoundDrawables + * @param textView {@link TextView} + * @param bottom bottom Drawable + * @return {@link ViewHelper} + */ + public ViewHelper setCompoundDrawablesByBottom(final TextView textView, final Drawable bottom) { + ViewUtils.setCompoundDrawablesByBottom(textView, bottom); + return this; + } + + /** + * 设置 CompoundDrawables + *
+     *     CompoundDrawable 的大小控制是通过 drawable.setBounds() 控制
+     *     需要先设置 Drawable 的 setBounds
+     * 
+ * @param textView {@link TextView} + * @param left left Drawable + * @param top top Drawable + * @param right right Drawable + * @param bottom bottom Drawable + * @return {@link ViewHelper} + */ + public ViewHelper setCompoundDrawables(final TextView textView, + final Drawable left, final Drawable top, + final Drawable right, final Drawable bottom) { + ViewUtils.setCompoundDrawables(textView, left, top, right, bottom); + return this; + } + + /** + * 设置 Left CompoundDrawables - 按照原有比例大小显示图片 + * @param textView {@link TextView} + * @param left left Drawable + * @return {@link ViewHelper} + */ + public ViewHelper setCompoundDrawablesWithIntrinsicBoundsByLeft(final TextView textView, final Drawable left) { + ViewUtils.setCompoundDrawablesWithIntrinsicBoundsByLeft(textView, left); + return this; + } + + /** + * 设置 Top CompoundDrawables - 按照原有比例大小显示图片 + * @param textView {@link TextView} + * @param top top Drawable + * @return {@link ViewHelper} + */ + public ViewHelper setCompoundDrawablesWithIntrinsicBoundsByTop(final TextView textView, final Drawable top) { + ViewUtils.setCompoundDrawablesWithIntrinsicBoundsByTop(textView, top); + return this; + } + + /** + * 设置 Right CompoundDrawables - 按照原有比例大小显示图片 + * @param textView {@link TextView} + * @param right right Drawable + * @return {@link ViewHelper} + */ + public ViewHelper setCompoundDrawablesWithIntrinsicBoundsByRight(final TextView textView, final Drawable right) { + ViewUtils.setCompoundDrawablesWithIntrinsicBoundsByRight(textView, right); + return this; + } + + /** + * 设置 Bottom CompoundDrawables - 按照原有比例大小显示图片 + * @param textView {@link TextView} + * @param bottom bottom Drawable + * @return {@link ViewHelper} + */ + public ViewHelper setCompoundDrawablesWithIntrinsicBoundsByBottom(final TextView textView, final Drawable bottom) { + ViewUtils.setCompoundDrawablesWithIntrinsicBoundsByBottom(textView, bottom); + return this; + } + + /** + * 设置 CompoundDrawables - 按照原有比例大小显示图片 + * @param textView {@link TextView} + * @param left left Drawable + * @param top top Drawable + * @param right right Drawable + * @param bottom bottom Drawable + * @return {@link ViewHelper} + */ + public ViewHelper setCompoundDrawablesWithIntrinsicBounds(final TextView textView, + final Drawable left, final Drawable top, + final Drawable right, final Drawable bottom) { + ViewUtils.setCompoundDrawablesWithIntrinsicBounds(textView, left, top, right, bottom); + return this; + } + + // ============ + // = Listener = + // ============ + + /** + * 设置点击事件 + * @param onClickListener {@link View.OnClickListener} + * @param views View 数组 + * @return {@link ViewHelper} + */ + public ViewHelper setOnClicks(final View.OnClickListener onClickListener, final View... views) { + ListenerUtils.setOnClicks(onClickListener, views); + return this; + } + + /** + * 设置长按事件 + * @param onLongClickListener {@link View.OnLongClickListener} + * @param views View 数组 + * @return {@link ViewHelper} + */ + public ViewHelper setOnLongClicks(final View.OnLongClickListener onLongClickListener, final View... views) { + ListenerUtils.setOnLongClicks(onLongClickListener, views); + return this; + } + + /** + * 增加控件的触摸范围, 最大范围只能是父布局所包含的的区域 + * @param view 待添加点击范围 View + * @param range 点击范围 + * @return {@link ViewHelper} + */ + public ViewHelper addTouchArea(final View view, final int range) { + ClickUtils.addTouchArea(view, range); + return this; + } + + /** + * 增加控件的触摸范围, 最大范围只能是父布局所包含的的区域 + * @param view 待添加点击范围 View + * @param top top range + * @param bottom bottom range + * @param left left range + * @param right right range + * @return {@link ViewHelper} + */ + public ViewHelper addTouchArea(final View view, final int top, final int bottom, final int left, final int right) { + ClickUtils.addTouchArea(view, top, bottom, left, right); + return this; + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/other/EventBusUtils.java b/lib/DevOther/src/main/java/dev/other/EventBusUtils.java new file mode 100644 index 0000000000..15454059f9 --- /dev/null +++ b/lib/DevOther/src/main/java/dev/other/EventBusUtils.java @@ -0,0 +1,88 @@ +package dev.other; + +import org.greenrobot.eventbus.EventBus; + +/** + * detail: EventBus 工具类 + * @author Ttt + *
+ *     eventbus: 注册后才能接收到该事件
+ *     poststicky: 事件消费者在事件发布之后才注册也能接收到该事件
+ * 
+ */ +public final class EventBusUtils { + + private EventBusUtils() { + } + + /** + * 注册 EventBus + * @param subscriber 订阅者 + */ + public static void register(final Object subscriber) { + EventBus eventBus = EventBus.getDefault(); + if (!eventBus.isRegistered(subscriber)) { + eventBus.register(subscriber); + } + } + + /** + * 解绑 EventBus + * @param subscriber 订阅者 + */ + public static void unregister(final Object subscriber) { + EventBus eventBus = EventBus.getDefault(); + if (eventBus.isRegistered(subscriber)) { + eventBus.unregister(subscriber); + } + } + + // ========= + // = Event = + // ========= + + /** + * 发送事件消息 + * @param event Event + */ + public static void post(final Object event) { + EventBus.getDefault().post(event); + } + + /** + * 取消事件传送 + * @param event Event + */ + public static void cancelEventDelivery(final Object event) { + EventBus.getDefault().cancelEventDelivery(event); + } + + // = + + /** + * 发送粘性事件消息 + * @param event Event + */ + public static void postSticky(final Object event) { + EventBus.getDefault().postSticky(event); + } + + /** + * 移除指定的粘性订阅事件 + * @param eventType Event Type + * @param 泛型 + */ + public static void removeStickyEvent(final Class eventType) { + T stickyEvent = EventBus.getDefault().getStickyEvent(eventType); + if (stickyEvent != null) { + EventBus.getDefault().removeStickyEvent(stickyEvent); + } + } + + /** + * 移除所有的粘性订阅事件 + */ + public static void removeAllStickyEvents() { + EventBus.getDefault().removeAllStickyEvents(); + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/other/GlideTransformUtils.java b/lib/DevOther/src/main/java/dev/other/GlideTransformUtils.java new file mode 100644 index 0000000000..05a3b992b1 --- /dev/null +++ b/lib/DevOther/src/main/java/dev/other/GlideTransformUtils.java @@ -0,0 +1,200 @@ +package dev.other; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.RectF; +import android.os.Build; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.renderscript.ScriptIntrinsicBlur; +import android.support.annotation.NonNull; + +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; +import com.bumptech.glide.load.resource.bitmap.TransformationUtils; + +import java.security.MessageDigest; + +/** + * detail: Glide 图形处理工具类 + * @author Ttt + *
+ *     结合 Glide 实现很炫的图片效果框架
+ *     @see 
+ *     @see 
+ * 
+ */ +public final class GlideTransformUtils { + + private GlideTransformUtils() { + } + + /** + * detail: 旋转处理 + * @author Ttt + */ + public static class RotateTransformation extends BitmapTransformation { + // 旋转角度 + private float rotateRotationAngle = 0f; + + public RotateTransformation(float rotateRotationAngle) { + this.rotateRotationAngle = rotateRotationAngle; + } + + @Override + protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { + Matrix matrix = new Matrix(); + matrix.postRotate(rotateRotationAngle); + return Bitmap.createBitmap(toTransform, 0, 0, + toTransform.getWidth(), toTransform.getHeight(), matrix, true); + } + + @Override + public void updateDiskCacheKey(MessageDigest messageDigest) { + } + } + + /** + * detail: 转换圆形处理 + * @author Ttt + */ + public static class GlideCircleTransform extends BitmapTransformation { + + @Override + protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { + return circleCrop(pool, toTransform); + } + + private Bitmap circleCrop(BitmapPool pool, Bitmap source) { + if (source == null) return null; + + int size = Math.min(source.getWidth(), source.getHeight()); + int x = (source.getWidth() - size) / 2; + int y = (source.getHeight() - size) / 2; + + Bitmap squared = Bitmap.createBitmap(source, x, y, size, size); + + Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_4444); + if (result == null) { + result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_4444); + } + + Canvas canvas = new Canvas(result); + Paint paint = new Paint(); + paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); + paint.setAntiAlias(true); + float r = size / 2f; + canvas.drawCircle(r, r, r, paint); + return result; + } + + @Override + public void updateDiskCacheKey(MessageDigest messageDigest) { + } + } + + /** + * detail: 圆角处理 + * @author Ttt + */ + public static class GlideRoundTransform extends BitmapTransformation { + + // 圆角大小 + private float radius = 0f; + + public GlideRoundTransform(float radius) { + this.radius = radius; + } + + @Override + protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { + return roundCrop(pool, toTransform); + } + + private Bitmap roundCrop(BitmapPool pool, Bitmap source) { + if (source == null) return null; + + Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_4444); + if (result == null) { + result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_4444); + } + + Canvas canvas = new Canvas(result); + Paint paint = new Paint(); + paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); + paint.setAntiAlias(true); + RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight()); + canvas.drawRoundRect(rectF, radius, radius, paint); + return result; + } + + @Override + public void updateDiskCacheKey(MessageDigest messageDigest) { + } + } + + /** + * detail: 图片模糊处理 + * @author Ttt + */ + public static class GlideBlurformation extends BitmapTransformation { + + // Context + private Context mContext; + + public GlideBlurformation(Context context) { + this.mContext = context; + } + + @Override + protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { + Bitmap bitmap = TransformationUtils.centerCrop(pool, toTransform, outWidth, outHeight); + return blurBitmap(mContext, bitmap, 20, outWidth, outHeight); + } + + @Override + public void updateDiskCacheKey(MessageDigest messageDigest) { + } + + /** + * 模糊图片处理 + * @param context {@link Context} + * @param image 待模糊的图片 + * @param outWidth 输出的宽度 + * @param outHeight 输出的高度 + * @return 模糊处理后的 Bitmap + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public Bitmap blurBitmap(Context context, Bitmap image, float blurRadius, int outWidth, int outHeight) { + // 将缩小后的图片做为预渲染的图片 + Bitmap inputBitmap = Bitmap.createScaledBitmap(image, outWidth, outHeight, false); + // 创建一张渲染后的输出图片 + Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap); + // 创建 RenderScript 内核对象 + RenderScript rs = RenderScript.create(context); + // 创建一个模糊效果的 RenderScript 的工具对象 + ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + // 由于 RenderScript 并没有使用 VM 来分配内存, 所以需要使用 Allocation 类来创建和分配内存空间 + // 创建 Allocation 对象的时候其实内存是空的, 需要使用 copyTo() 将数据填充进去 + Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); + Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); + // 设置渲染的模糊程度, 25f 是最大模糊度 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + blurScript.setRadius(blurRadius); + } + // 设置 blurScript 对象的输入内存 + blurScript.setInput(tmpIn); + // 将输出数据保存到输出内存中 + blurScript.forEach(tmpOut); + // 将数据填充到 Allocation 中 + tmpOut.copyTo(outputBitmap); + return outputBitmap; + } + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/other/GlideUtils.java b/lib/DevOther/src/main/java/dev/other/GlideUtils.java new file mode 100644 index 0000000000..8127607da4 --- /dev/null +++ b/lib/DevOther/src/main/java/dev/other/GlideUtils.java @@ -0,0 +1,592 @@ +package dev.other; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.view.View; +import android.widget.ImageView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.Priority; +import com.bumptech.glide.RequestManager; +import com.bumptech.glide.load.Transformation; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.gif.GifDrawable; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.Target; + +import java.io.File; + +import dev.utils.LogPrintUtils; + +/** + * detail: Glide 工具类 + * @author Ttt + *
+ *     init: GlideUtils.init(getApplicationContext());
+ *     use modify: 修改 defaultOptions() 配置、以及加载默认图片资源
+ *     

+ * Glide 详细使用 + * @see
+ * Glide 文档 + * @see + *
+ */ +public final class GlideUtils { + + private GlideUtils() { + } + + // 日志 TAG + private static final String TAG = GlideUtils.class.getSimpleName(); + // GlideLoader + private static GlideLoader sGlideLoader; + // 图片默认加载配置 + private static RequestOptions DF_OPTIONS = defaultOptions(); + // 全局 Context + private static Context sContext; + // 图片加载中 + private static int sImageLoadingRes = 0; + // 图片地址异常 + private static int sImageUriErrorRes = 0; + // 图片 ( 加载 / 解码 ) 失败 + private static int sImageFailRes = 0; + + // ================================ + // = GlideLoader(RequestManager) = + // ================================ + + @NonNull + public static GlideLoader with(@NonNull Context context) { + return new GlideLoader(Glide.with(context)); + } + + @NonNull + public static GlideLoader with(@NonNull Activity activity) { + return new GlideLoader(Glide.with(activity)); + } + + @NonNull + public static GlideLoader with(@NonNull FragmentActivity activity) { + return new GlideLoader(Glide.with(activity)); + } + + @NonNull + public static GlideLoader with(@NonNull android.app.Fragment fragment) { + return new GlideLoader(Glide.with(fragment)); + } + + @NonNull + public static GlideLoader with(@NonNull Fragment fragment) { + return new GlideLoader(Glide.with(fragment)); + } + + @NonNull + public static GlideLoader with(@NonNull View view) { + return new GlideLoader(Glide.with(view)); + } + + /** + * 获取全局 Context Glide + * @return {@link GlideLoader} + */ + public static GlideLoader with() { + if (sGlideLoader == null) { + try { + sGlideLoader = new GlideLoader(Glide.with(sContext)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "with"); + } + } + return sGlideLoader; + } + + // ========== + // = 初始化 = + // ========== + + /** + * 初始化方法 + * @param context {@link Context} + */ + public static void init(final Context context) { + if (sContext == null && context != null) { + // 设置全局 Context + sContext = context.getApplicationContext(); + // 默认进行初始化 + with(); + } + } + + // ================== + // = RequestOptions = + // ================== + + /** + * 克隆图片加载配置 + * @param options 待克隆加载配置 + * @return {@link RequestOptions} + */ + public static RequestOptions cloneImageOptions(final RequestOptions options) { + return (options != null) ? options.clone() : null; + } + + /** + * 获取默认加载配置 + *
+     *     优先级:
+     *     Priority.LOW 低
+     *     Priority.NORMAL 默认正常
+     *     Priority.HIGH 高 / 优先
+     *     Priority.IMMEDIATE 立即加载
+     *     缓存:
+     *     DiskCacheStrategy.NONE 不做磁盘缓存
+     *     DiskCacheStrategy.SOURCE 只缓存图像原图
+     *     DiskCacheStrategy.RESULT 只缓存加载后的图像, 即处理后最终显示时的图像
+     *     DiskCacheStrategy.ALL 缓存所有版本的图像 ( 默认行为 )
+     * 
+ * @return {@link RequestOptions} + */ + public static RequestOptions defaultOptions() { + RequestOptions requestOptions = new RequestOptions() + .diskCacheStrategy(DiskCacheStrategy.ALL) // 设置全缓存 + .placeholder(sImageLoadingRes) // 设置图片在下载期间显示的图片 + .fallback(sImageUriErrorRes) // 设置图片 Uri 为空或是错误的时候显示的图片 + .error(sImageFailRes) // 设置图片 ( 加载 / 解码 ) 过程中错误时候显示的图片 +// .format(DecodeFormat.PREFER_ARGB_8888) // 设置图片解码格式, 默认 8888 + .priority(Priority.HIGH); + return requestOptions; + } + + /** + * 获取空白加载配置 + * @return {@link RequestOptions} + */ + public static RequestOptions emptyOptions() { + return new RequestOptions(); + } + + /** + * 获取跳过缓存 ( 每次都从服务端获取最新 ) 加载配置 + * @return {@link RequestOptions} + */ + public static RequestOptions skipCacheOptions() { + return skipCacheOptions(cloneImageOptions(DF_OPTIONS)); + } + + /** + * 获取跳过缓存 ( 每次都从服务端获取最新 ) 加载配置 + * @param options {@link RequestOptions} + * @return {@link RequestOptions} + */ + public static RequestOptions skipCacheOptions(final RequestOptions options) { + if (options != null) { + return options.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true); + } + return options; + } + + /** + * 获取自定义图片加载配置 + * @param loadingRes 设置加载中显示的图片 + * @return {@link RequestOptions} + */ + public static RequestOptions getLoadResOptions(@DrawableRes final int loadingRes) { + return getLoadResOptions(cloneImageOptions(DF_OPTIONS), loadingRes); + } + + /** + * 获取自定义图片加载配置 + * @param options {@link RequestOptions} + * @param loadingRes 设置加载中显示的图片 + * @return {@link RequestOptions} + */ + public static RequestOptions getLoadResOptions(final RequestOptions options, @DrawableRes final int loadingRes) { + if (options != null && loadingRes != 0) { + options.placeholder(loadingRes) // 设置图片在下载期间显示的图片 + .fallback(loadingRes) // 设置图片 Uri 为空或是错误的时候显示的图片 + .error(loadingRes); // 设置图片 ( 加载 / 解码 ) 过程中错误时候显示的图片 + } + return options; + } + + /** + * 获取图片处理效果加载配置 + * @param transformation {@link Transformation} 图形效果 + * @return {@link RequestOptions} + */ + public static RequestOptions transformationOptions(final Transformation transformation) { + return transformationOptions(cloneImageOptions(DF_OPTIONS), transformation); + } + + /** + * 获取图片处理效果加载配置 + * @param options {@link RequestOptions} + * @param transformation {@link Transformation} 图形效果 + * @return {@link RequestOptions} + */ + public static RequestOptions transformationOptions(final RequestOptions options, final Transformation transformation) { + if (options != null) { + try { + options.transform(transformation); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "transformationOptions"); + } + } + return options; + } + + // ============== + // = 内部方法类 = + // ============== + + /** + * detail: Glide Loader 封装内部类 + * @author Ttt + */ + public final static class GlideLoader { + + // RequestManager + private RequestManager mRequestManager; + + /** + * 构造函数 + * @param requestManager {@link RequestManager} + */ + public GlideLoader(RequestManager requestManager) { + this.mRequestManager = requestManager; + // 设置加载配置 + if (requestManager != null) requestManager.setDefaultRequestOptions(DF_OPTIONS); + } + + // ============== + // = 预加载处理 = + // ============== + + /** + * 预加载图片 + * @param uri Image Uri + */ + public void preload(final String uri) { + preload(uri, null); + } + + /** + * 预加载图片 + *
+         *     先加载图片, 不显示, 等到需要显示的时候, 直接拿缓存用
+         * 
+ * @param uri Image Uri + * @param options {@link RequestOptions} + */ + public void preload(final String uri, final RequestOptions options) { + if (mRequestManager != null) { + if (options != null) { + mRequestManager.asBitmap().load(uri).apply(options).preload(); + } else { + mRequestManager.asBitmap().load(uri).preload(); + } + } + } + + // ============ + // = 图片显示 = + // ============ + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + */ + public void displayImage(final String uri, final ImageView imageView) { + displayImage(uri, imageView, null); + } + + /** + * 图片显示 + *
+         *     支持显示 Gif 图片第一帧
+         * 
+ * @param uri Image Uri + * @param imageView ImageView + * @param options {@link RequestOptions} + */ + public void displayImage(final String uri, final ImageView imageView, final RequestOptions options) { + if (mRequestManager != null && imageView != null) { + if (options != null) { + mRequestManager.asBitmap().load(uri).apply(options).into(imageView); + } else { + mRequestManager.asBitmap().load(uri).into(imageView); + } + } + } + + // = + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + */ + public void displayImageToGif(final String uri, final ImageView imageView) { + displayImageToGif(uri, imageView, null); + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param options {@link RequestOptions} + */ + public void displayImageToGif(final String uri, final ImageView imageView, final RequestOptions options) { + if (mRequestManager != null && imageView != null) { + if (options != null) { + mRequestManager.asGif().load(uri).apply(options).into(imageView); + } else { + mRequestManager.asGif().load(uri).into(imageView); + } + } + } + + // ============ + // = 图片加载 = + // ============ + + /** + * 图片加载 + * @param uri Image Uri + * @param target {@link Target} + */ + public void loadImageBitmap(final String uri, final Target target) { + loadImageBitmap(uri, target, null); + } + + /** + * 图片加载 + * @param uri Image Uri + * @param target {@link Target} + * @param options {@link RequestOptions} + */ + public void loadImageBitmap(final String uri, final Target target, final RequestOptions options) { + if (mRequestManager != null) { + if (options != null) { + mRequestManager.asBitmap().load(uri).apply(options).into(target); + } else { + mRequestManager.asBitmap().load(uri).into(target); + } + } + } + + // = + + /** + * 图片加载 + * @param uri Image Uri + * @param target {@link Target} + */ + public void loadImageDrawable(final String uri, final Target target) { + loadImageDrawable(uri, target, null); + } + + /** + * 图片加载 + * @param uri Image Uri + * @param target {@link Target} + * @param options {@link RequestOptions} + */ + public void loadImageDrawable(final String uri, final Target target, final RequestOptions options) { + if (mRequestManager != null) { + if (options != null) { + mRequestManager.asDrawable().load(uri).apply(options).into(target); + } else { + mRequestManager.asDrawable().load(uri).into(target); + } + } + } + + // = + + /** + * 图片加载 + * @param uri Image Uri + * @param target {@link Target} + */ + public void loadImageFile(final String uri, final Target target) { + loadImageFile(uri, target, null); + } + + /** + * 图片加载 + * @param uri Image Uri + * @param target {@link Target} + * @param options {@link RequestOptions} + */ + public void loadImageFile(final String uri, final Target target, final RequestOptions options) { + if (mRequestManager != null) { + if (options != null) { + mRequestManager.asFile().load(uri).apply(options).into(target); + } else { + mRequestManager.asFile().load(uri).into(target); + } + } + } + + /** + * 图片加载 + * @param uri Image Uri + * @param target {@link Target} + */ + public void loadImageGif(final String uri, final Target target) { + loadImageGif(uri, target, null); + } + + /** + * 图片加载 + * @param uri Image Uri + * @param target {@link Target} + * @param options {@link RequestOptions} + */ + public void loadImageGif(final String uri, final Target target, final RequestOptions options) { + if (mRequestManager != null) { + if (options != null) { + mRequestManager.asGif().load(uri).apply(options).into(target); + } else { + mRequestManager.asGif().load(uri).into(target); + } + } + } + + // ============ + // = 其他操作 = + // ============ + + /** + * 取消图片显示任务 + * @param view {@link View} + */ + public void cancelDisplayTask(final View view) { + if (mRequestManager != null && view != null) { + mRequestManager.clear(view); + } + } + + /** + * 取消图片显示任务 + * @param target {@link Target} + */ + public void cancelDisplayTask(final Target target) { + if (mRequestManager != null && target != null) { + mRequestManager.clear(target); + } + } + + // = + + /** + * 销毁操作 + */ + public void destroy() { + if (mRequestManager != null) { + mRequestManager.onDestroy(); + } + } + + /** + * 暂停图片加载 + */ + public void pause() { + if (mRequestManager != null) { + mRequestManager.pauseAllRequests(); + } + } + + /** + * 恢复图片加载 + */ + public void resume() { + if (mRequestManager != null) { + mRequestManager.resumeRequests(); + } + } + + /** + * 停止图片加载 + */ + public void stop() { + if (mRequestManager != null) { + mRequestManager.onStop(); + } + } + + /** + * 开始图片加载 + */ + public void start() { + if (mRequestManager != null) { + mRequestManager.onStart(); + } + } + } + + // ============ + // = 其他操作 = + // ============ + + /** + * 清除磁盘缓存 + */ + public static void clearDiskCache() { + new Thread(new Runnable() { + @Override + public void run() { + try { + // This method must be called on a background thread. + Glide.get(sContext).clearDiskCache(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "clearDiskCache"); + } + } + }).start(); + } + + /** + * 清除内存缓存 + */ + public static void clearMemoryCache() { + try { + // This method must be called on the main thread. + Glide.get(sContext).clearMemory(); // 必须在主线程上调用该方法 + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "clearMemoryCache"); + } + } + + /** + * 低内存通知 + */ + public static void onLowMemory() { + try { + Glide.get(sContext).onLowMemory(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "onLowMemory"); + } + } + + /** + * 获取 SDCard 缓存空间 + * @return SDCard 缓存空间 File + */ + public static File getDiskCache() { + try { + return Glide.getPhotoCacheDir(sContext); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDiskCache"); + } + return null; + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/other/GsonUtils.java b/lib/DevOther/src/main/java/dev/other/GsonUtils.java new file mode 100644 index 0000000000..7fbe5fce6a --- /dev/null +++ b/lib/DevOther/src/main/java/dev/other/GsonUtils.java @@ -0,0 +1,296 @@ +package dev.other; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; + +import java.io.StringReader; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import dev.utils.JCLogUtils; + +/** + * detail: Gson 工具类 + * @author Ttt + *
+ *     Gson 详细使用
+ *     @see 
+ * 
+ */ +public final class GsonUtils { + + private GsonUtils() { + } + + // 日志 TAG + private static final String TAG = GsonUtils.class.getSimpleName(); + // Object 转 JSON 字符串 + private static final Gson TO_GSON = createGson(true).create(); + // JSON 字符串转 T Object + private static final Gson FROM_GSON = createGson(true).create(); + // JSON 缩进 + private static final Gson INDENT_GSON = createGson(true).setPrettyPrinting().create(); + + // ============ + // = 转换方法 = + // ============ + + /** + * 将对象转换为 JSON String + * @param object {@link Object} + * @return JSON String + */ + public static String toJson(final Object object) { + return toJson(object, TO_GSON); + } + + /** + * 将对象转换为 JSON String + * @param object {@link Object} + * @param gson {@link Gson} + * @return JSON String + */ + public static String toJson(final Object object, final Gson gson) { + if (gson != null) { + try { + return gson.toJson(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toJson"); + } + } + return null; + } + + // = + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param classOfT {@link Class} T + * @param 泛型 + * @return instance of type + */ + public static T fromJson(final String json, final Class classOfT) { + return fromJson(json, classOfT, FROM_GSON); + } + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param classOfT {@link Class} T + * @param gson {@link Gson} + * @param 泛型 + * @return instance of type + */ + public static T fromJson(final String json, final Class classOfT, final Gson gson) { + if (gson != null) { + try { + return gson.fromJson(json, classOfT); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "fromJson"); + } + } + return null; + } + + // = + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param typeOfT {@link Type} T + * @param 泛型 + * @return instance of type + */ + public static T fromJson(final String json, final Type typeOfT) { + return fromJson(json, typeOfT, FROM_GSON); + } + + /** + * 将 JSON String 映射为指定类型对象 + * @param json JSON String + * @param typeOfT {@link Type} T + * @param gson {@link Gson} + * @param 泛型 + * @return instance of type + */ + public static T fromJson(final String json, final Type typeOfT, final Gson gson) { + if (gson != null) { + try { + return gson.fromJson(json, typeOfT); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "fromJson"); + } + } + return null; + } + + // ============ + // = 其他方法 = + // ============ + + /** + * 判断字符串是否 JSON 格式 + * @param json 待校验 JSON String + * @return {@code true} yes, {@code false} no + */ + public static boolean isJSON(final String json) { + JsonElement jsonElement; + try { + jsonElement = new JsonParser().parse(json); + } catch (Exception e) { + return false; + } + if (jsonElement == null) { + return false; + } + if (!jsonElement.isJsonObject()) { + return false; + } + return true; + } + + /** + * JSON String 缩进处理 + * @param json JSON String + * @return JSON String + */ + public static String toJsonIndent(final String json) { + return toJsonIndent(json, INDENT_GSON); + } + + /** + * JSON String 缩进处理 + * @param json JSON String + * @param gson {@link Gson} + * @return JSON String + */ + public static String toJsonIndent(final String json, final Gson gson) { + if (gson != null) { + try { + JsonReader reader = new JsonReader(new StringReader(json)); + reader.setLenient(true); + JsonParser jsonParser = new JsonParser(); + JsonElement jsonElement = jsonParser.parse(reader); + return gson.toJson(jsonElement); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toJsonIndent"); + } + } + return null; + } + + // = + + /** + * Object 转 JSON String 并进行缩进处理 + * @param object {@link Object} + * @return JSON String + */ + public static String toJsonIndent(final Object object) { + return toJsonIndent(object, INDENT_GSON); + } + + /** + * Object 转 JSON String 并进行缩进处理 + * @param object {@link Object} + * @param gson {@link Gson} + * @return JSON String + */ + public static String toJsonIndent(final Object object, final Gson gson) { + if (gson != null) { + try { + return gson.toJson(object); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "toJsonIndent"); + } + } + return null; + } + + // ======== + // = Gson = + // ======== + + /** + * 创建 GsonBuilder + * @param serializeNulls 是否序列化 null 值 + * @return {@link GsonBuilder} + */ + public static GsonBuilder createGson(final boolean serializeNulls) { + GsonBuilder builder = new GsonBuilder(); + if (serializeNulls) builder.serializeNulls(); + return builder; + } + + /** + * 创建过滤指定修饰符字段 GsonBuilder + * @param builder {@link GsonBuilder} + * @param modifiers 需过滤不处理的字段修饰符 {@link Modifier} + * @return {@link GsonBuilder} + */ + public static GsonBuilder createGsonExcludeFields(final GsonBuilder builder, final int... modifiers) { + if (builder != null) { + return builder.excludeFieldsWithModifiers(modifiers); + } + return null; + } + + // ======== + // = Type = + // ======== + + /** + * 获取 Array Type + * @param type Bean.class + * @return Bean[] Type + */ + public static Type getArrayType(final Type type) { + return TypeToken.getArray(type).getType(); + } + + /** + * 获取 List Type + * @param type Bean.class + * @return List Type + */ + public static Type getListType(final Type type) { + return TypeToken.getParameterized(List.class, type).getType(); + } + + /** + * 获取 Set Type + * @param type Bean.class + * @return Set Type + */ + public static Type getSetType(final Type type) { + return TypeToken.getParameterized(Set.class, type).getType(); + } + + /** + * 获取 Map Type + * @param keyType Key.class + * @param valueType Value.class + * @return Map Type + */ + public static Type getMapType(final Type keyType, final Type valueType) { + return TypeToken.getParameterized(Map.class, keyType, valueType).getType(); + } + + /** + * 获取 Type + * @param rawType raw type + * @param typeArguments type arguments + * @return Type + */ + public static Type getType(final Type rawType, final Type... typeArguments) { + return TypeToken.getParameterized(rawType, typeArguments).getType(); + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/other/ImageLoaderUtils.java b/lib/DevOther/src/main/java/dev/other/ImageLoaderUtils.java new file mode 100644 index 0000000000..d228bb83db --- /dev/null +++ b/lib/DevOther/src/main/java/dev/other/ImageLoaderUtils.java @@ -0,0 +1,575 @@ +package dev.other; + +import android.content.Context; +import android.graphics.Bitmap; +import android.support.annotation.DrawableRes; +import android.widget.ImageView; + +import com.nostra13.universalimageloader.cache.disc.DiskCache; +import com.nostra13.universalimageloader.cache.memory.MemoryCache; +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; +import com.nostra13.universalimageloader.core.assist.ImageScaleType; +import com.nostra13.universalimageloader.core.assist.ImageSize; +import com.nostra13.universalimageloader.core.assist.QueueProcessingType; +import com.nostra13.universalimageloader.core.display.BitmapDisplayer; +import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer; +import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer; +import com.nostra13.universalimageloader.core.download.BaseImageDownloader; +import com.nostra13.universalimageloader.core.imageaware.ImageAware; +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; +import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener; + +/** + * detail: ImageLoader 工具类 + * @author Ttt + *
+ *     init: ImageLoaderUtils.init(getApplicationContext());
+ *     use modify: 修改 defaultOptions() 配置、以及加载默认图片资源
+ * 
+ */ +public final class ImageLoaderUtils { + + private ImageLoaderUtils() { + } + + // 图片加载中 + private static int sImageLoadingRes = 0; + // 图片地址异常 + private static int sImageUriErrorRes = 0; + // 图片 ( 加载 / 解码 ) 失败 + private static int sImageFailRes = 0; + // 图片默认加载配置 + private static final DisplayImageOptions DF_OPTIONS = defaultOptions(); + + // ======== + // = init = + // ======== + + /** + * 初始化 ImageLoader 加载配置 + * @param context {@link Context} + */ + public static void init(final Context context) { + DisplayImageOptions options = DF_OPTIONS; + // 针对图片缓存的全局加载配置 ( 主要有线程类、缓存大小、磁盘大小、图片下载与解析、日志方面的配置 ) + ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context) + .defaultDisplayImageOptions(options) // 加载 DisplayImageOptions 参数 + .threadPriority(Thread.NORM_PRIORITY - 2) // 线程池内加载的数量 + .denyCacheImageMultipleSizesInMemory() // 加载同一 URL 图片时 imageView 从小变大时从内存缓存中加载 + //.memoryCache(new UsingFreqLimitedMemoryCache(1024 * 1024)) // 通过自己的内存缓存实现 + .memoryCacheSize(2 * 1024 * 1024) // 内存缓存最大值 + .memoryCacheSizePercentage(13) + //.diskCacheSize(50 * 1024 * 1024) // SDCard 缓存最大值 50mb + //.discCacheFileNameGenerator(new Md5FileNameGenerator()) // 将保存的时候的 URI 名称用 MD5 加密 + //.diskCacheFileCount(100) // 缓存的文件数量 + //.memoryCache(new WeakMemoryCache()).diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) + .imageDownloader(new BaseImageDownloader(context)) // default + .tasksProcessingOrder(QueueProcessingType.LIFO).build(); + ImageLoader.getInstance().init(config); + } + + // ======================= + // = DisplayImageOptions = + // ======================= + + /** + * 获取 DisplayImageOptions 图片加载配置 + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions defaultOptions() { + DisplayImageOptions options = new DisplayImageOptions.Builder() + .showImageOnLoading(sImageLoadingRes) // 设置图片在下载期间显示的图片 + .showImageForEmptyUri(sImageUriErrorRes) // 设置图片 Uri 为空或是错误的时候显示的图片 + .showImageOnFail(sImageFailRes) // 设置图片 ( 加载 / 解码 ) 过程中错误时候显示的图片 + .imageScaleType(ImageScaleType.EXACTLY) // 设置图片缩放 + .bitmapConfig(Bitmap.Config.RGB_565) // 图片解码类型 + .cacheInMemory(true) // 是否保存到内存 + .considerExifParams(false) // 是否考虑 JPEG 图像 EXIF 参数 ( 旋转、翻转 ) + .resetViewBeforeLoading(false) // default 设置图片在加载前是否重置、复位 + //.displayer(new FadeInBitmapDisplayer(100)) // 图片加载好后渐入的动画时间 + .cacheOnDisk(true).build(); // 是否保存到 SDCard 上 + return options; + } + + /** + * 获取图片默认加载配置 + * @param loadingRes 设置加载中显示的图片 + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions getDefaultImageOptions(@DrawableRes final int loadingRes) { + DisplayImageOptions.Builder optionsBuilder = cloneImageOptions(DF_OPTIONS); + optionsBuilder.showImageOnLoading(loadingRes) // 设置图片在下载期间显示的图片 + .showImageForEmptyUri(loadingRes) // 设置图片 Uri 为空或是错误的时候显示的图片 + .showImageOnFail(loadingRes); // 设置图片 ( 加载 / 解码 ) 过程中错误时候显示的图片 + return optionsBuilder.build(); + } + + /** + * 获取不使用缓存的图片加载配置 + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions getNoCacheImageOptions() { + return getCacheImageOptions(false, false, -1); + } + + /** + * 获取不使用缓存的图片加载配置 + * @param loadingRes 设置加载中显示的图片 + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions getNoCacheImageOptions(@DrawableRes final int loadingRes) { + return getCacheImageOptions(false, false, loadingRes); + } + + /** + * 获取 ImageLoader 图片缓存加载配置 + * @param isCache 是否缓存在内存中 + * @param isCacheDisk 是否保存在 SDCard + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions getCacheImageOptions(final boolean isCache, final boolean isCacheDisk) { + return getCacheImageOptions(isCache, isCacheDisk, -1); + } + + /** + * 获取 ImageLoader 图片加载配置 + * @param isCache 是否缓存在内存中 + * @param isCacheDisk 是否保存在 SDCard + * @param loadingRes 设置加载中显示的图片 + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions getCacheImageOptions(final boolean isCache, + final boolean isCacheDisk, + @DrawableRes final int loadingRes) { + DisplayImageOptions.Builder builder = new DisplayImageOptions.Builder() + .imageScaleType(ImageScaleType.EXACTLY) // 设置图片缩放 + .bitmapConfig(Bitmap.Config.RGB_565) // 图片解码类型 + .cacheInMemory(isCache) // 是否保存到内存 + .displayer(new RoundedBitmapDisplayer(20)) + .cacheOnDisk(isCacheDisk); // 是否保存到 SDCard 上 + // 判断是否使用加载图片 + if (loadingRes != 0) { + builder.showImageOnLoading(loadingRes) // 设置图片在下载期间显示的图片 + .showImageForEmptyUri(loadingRes) // 设置图片 Uri 为空或是错误的时候显示的图片 + .showImageOnFail(loadingRes); // 设置图片 ( 加载 / 解码 ) 过程中错误时候显示的图片 + } + return builder.build(); + } + + /** + * 克隆图片加载配置 + * @param options 待克隆加载配置 + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions.Builder cloneImageOptions(final DisplayImageOptions options) { + return (options != null) ? new DisplayImageOptions.Builder().cloneFrom(options) : null; + } + + // ============ + // = 图片效果 = + // ============ + + /** + * 获取图片渐变动画加载配置 + * @param durationMillis 动画持续时间 + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions getFadeInBitmapDisplayer(final int durationMillis) { + return getBitmapDisplayerOptions(cloneImageOptions(DF_OPTIONS).build(), new FadeInBitmapDisplayer(durationMillis)); + } + + /** + * 获取图片渐变动画加载配置 + * @param options {@link DisplayImageOptions} + * @param durationMillis 动画持续时间 + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions getFadeInBitmapDisplayer(final DisplayImageOptions options, final int durationMillis) { + if (options != null) { + return getBitmapDisplayerOptions(options, new FadeInBitmapDisplayer(durationMillis)); + } + return options; + } + + // = + + /** + * 获取圆角图片加载配置 + * @param cornerRadiusPixels 圆角大小 + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions getRoundedBitmapDisplayer(final int cornerRadiusPixels) { + return getBitmapDisplayerOptions(cloneImageOptions(DF_OPTIONS).build(), new RoundedBitmapDisplayer(cornerRadiusPixels)); + } + + /** + * 获取圆角图片加载配置 + * @param options {@link DisplayImageOptions} + * @param cornerRadiusPixels 圆角大小 + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions getRoundedBitmapDisplayer(final DisplayImageOptions options, final int cornerRadiusPixels) { + if (options != null) { + return getBitmapDisplayerOptions(options, new RoundedBitmapDisplayer(cornerRadiusPixels)); + } + return options; + } + + /** + * 获取图片效果加载配置 + * @param options {@link DisplayImageOptions} + * @param displayer {@link BitmapDisplayer} + * @return {@link DisplayImageOptions} + */ + public static DisplayImageOptions getBitmapDisplayerOptions(final DisplayImageOptions options, final BitmapDisplayer displayer) { + if (options != null && displayer != null) { + return cloneImageOptions(options).displayer(displayer).build(); + } + return null; + } + + // ============ + // = 图片显示 = + // ============ + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + */ + public static void displayImage(final String uri, final ImageView imageView) { + if (imageView != null) ImageLoader.getInstance().displayImage(uri, imageView); + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param options {@link DisplayImageOptions} + */ + public static void displayImage(final String uri, final ImageView imageView, final DisplayImageOptions options) { + if (imageView != null) { + // 判断是否使用自定义图片加载配置 + if (options != null) { + ImageLoader.getInstance().displayImage(uri, imageView, options); + } else { + ImageLoader.getInstance().displayImage(uri, imageView); + } + } + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param imageSize {@link ImageSize} + */ + public static void displayImage(final String uri, final ImageView imageView, final ImageSize imageSize) { + if (imageView != null && imageSize != null) { + ImageLoader.getInstance().displayImage(uri, imageView, imageSize); + } + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageAware new ImageViewAware(imageView); + * @param options {@link DisplayImageOptions} + */ + public static void displayImage(final String uri, final ImageAware imageAware, final DisplayImageOptions options) { + if (imageAware != null) { + if (options != null) { + ImageLoader.getInstance().displayImage(uri, imageAware, options); + } else { + ImageLoader.getInstance().displayImage(uri, imageAware); + } + } + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param listener 加载监听事件 + */ + public static void displayImage(final String uri, final ImageView imageView, final ImageLoadingListener listener) { + displayImage(uri, imageView, null, listener); + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param options {@link DisplayImageOptions} + * @param listener 加载监听事件 + */ + public static void displayImage(final String uri, final ImageView imageView, + final DisplayImageOptions options, final ImageLoadingListener listener) { + if (imageView != null) { + if (options != null) { + ImageLoader.getInstance().displayImage(uri, imageView, options, listener); + } else { + ImageLoader.getInstance().displayImage(uri, imageView, listener); + } + } + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param listener 加载监听事件 + * @param progressListener 图片下载监听事件 + */ + public static void displayImage(final String uri, final ImageView imageView, + final ImageLoadingListener listener, + final ImageLoadingProgressListener progressListener) { + displayImage(uri, imageView, null, listener, progressListener); + } + + /** + * 图片显示 + * @param uri Image Uri + * @param imageView ImageView + * @param options {@link DisplayImageOptions} + * @param listener 加载监听事件 + * @param progressListener 图片下载监听事件 + */ + public static void displayImage(final String uri, final ImageView imageView, + final DisplayImageOptions options, final ImageLoadingListener listener, + final ImageLoadingProgressListener progressListener) { + if (imageView != null) { + ImageLoader.getInstance().displayImage(uri, imageView, options, listener, progressListener); + } + } + + // ============ + // = 图片加载 = + // ============ + + /** + * 图片加载 + * @param uri Image Uri + * @param listener 加载监听事件 + */ + public static void loadImage(final String uri, final ImageLoadingListener listener) { + loadImage(uri, null, null, listener, null); + } + + /** + * 图片加载 + * @param uri Image Uri + * @param imageSize {@link ImageSize} + * @param listener 加载监听事件 + */ + public static void loadImage(final String uri, final ImageSize imageSize, final ImageLoadingListener listener) { + loadImage(uri, imageSize, null, listener, null); + } + + /** + * 图片加载 + * @param uri Image Uri + * @param imageSize {@link ImageSize} + * @param listener 加载监听事件 + * @param progressListener 图片下载监听事件 + */ + public static void loadImage(final String uri, final ImageSize imageSize, + final ImageLoadingListener listener, + final ImageLoadingProgressListener progressListener) { + loadImage(uri, imageSize, null, listener, progressListener); + } + + /** + * 图片加载 + * @param uri Image Uri + * @param listener 加载监听事件 + * @param progressListener 图片下载监听事件 + */ + public static void loadImage(final String uri, final ImageLoadingListener listener, + final ImageLoadingProgressListener progressListener) { + loadImage(uri, null, null, listener, progressListener); + } + + /** + * 图片加载 + * @param uri Image Uri + * @param options {@link DisplayImageOptions} + * @param listener 加载监听事件 + * @param progressListener 图片下载监听事件 + */ + public static void loadImage(final String uri, final DisplayImageOptions options, + final ImageLoadingListener listener, final ImageLoadingProgressListener progressListener) { + loadImage(uri, null, options, listener, progressListener); + } + + /** + * 图片加载 + * @param uri Image Uri + * @param imageSize {@link ImageSize} + * @param options {@link DisplayImageOptions} + * @param listener 加载监听事件 + * @param progressListener 图片下载监听事件 + */ + public static void loadImage(final String uri, final ImageSize imageSize, final DisplayImageOptions options, + final ImageLoadingListener listener, final ImageLoadingProgressListener progressListener) { + ImageLoader.getInstance().loadImage(uri, imageSize, options, listener, progressListener); + } + + // ================ + // = 图片同步加载 = + // ================ + + /** + * 图片同步加载 + * @param uri Image Uri + * @return {@link Bitmap} + */ + public static Bitmap loadImageSync(final String uri) { + return loadImageSync(uri, null, null); + } + + /** + * 图片同步加载 + * @param uri Image Uri + * @param imageSize {@link ImageSize} + * @return {@link Bitmap} + */ + public static Bitmap loadImageSync(final String uri, final ImageSize imageSize) { + return loadImageSync(uri, imageSize, null); + } + + /** + * 图片同步加载 + * @param uri Image Uri + * @param options {@link DisplayImageOptions} + * @return {@link Bitmap} + */ + public static Bitmap loadImageSync(final String uri, final DisplayImageOptions options) { + return loadImageSync(uri, null, options); + } + + /** + * 图片同步加载 + * @param uri Image Uri + * @param imageSize {@link ImageSize} + * @param options {@link DisplayImageOptions} + * @return {@link Bitmap} + */ + public static Bitmap loadImageSync(final String uri, final ImageSize imageSize, final DisplayImageOptions options) { + return ImageLoader.getInstance().loadImageSync(uri, imageSize, options); + } + + // ============ + // = 其他操作 = + // ============ + + /** + * 清除磁盘缓存 + */ + public static void clearDiskCache() { + ImageLoader.getInstance().clearDiskCache(); + } + + /** + * 清除内存缓存 + */ + public static void clearMemoryCache() { + ImageLoader.getInstance().clearMemoryCache(); + } + + /** + * 获取 SDCard 缓存空间 + * @return {@link DiskCache} + */ + public static DiskCache getDiskCache() { + return ImageLoader.getInstance().getDiskCache(); + } + + /** + * 获取 Memory 缓存空间 + * @return {@link MemoryCache} + */ + public static MemoryCache getMemoryCache() { + return ImageLoader.getInstance().getMemoryCache(); + } + + /** + * 设置是否处理弱网情况 + * @param handleSlowNetwork 是否处理弱网情况 + */ + public static void handleSlowNetwork(final boolean handleSlowNetwork) { + ImageLoader.getInstance().handleSlowNetwork(handleSlowNetwork); + } + + /** + * 设置是否禁止网络下载 + * @param denyNetworkDownloads 是否禁止网络下载 + */ + public static void denyNetworkDownloads(final boolean denyNetworkDownloads) { + ImageLoader.getInstance().denyNetworkDownloads(denyNetworkDownloads); + } + + // = + + /** + * 取消图片显示任务 + * @param imageView ImageView + */ + public static void cancelDisplayTask(final ImageView imageView) { + if (imageView != null) { + ImageLoader.getInstance().cancelDisplayTask(imageView); + } + } + + /** + * 通过 ImageView 获取图片加载地址 + * @param imageView ImageView + * @return Image Uri + */ + public static String getLoadingUriForView(final ImageView imageView) { + if (imageView != null) { + return ImageLoader.getInstance().getLoadingUriForView(imageView); + } + return null; + } + + /** + * 设置全局加载监听事件 + * @param listener 加载监听事件 + */ + public static void setDefaultLoadingListener(final ImageLoadingListener listener) { + ImageLoader.getInstance().setDefaultLoadingListener(listener); + } + + // = + + /** + * 销毁操作 + */ + public static void destroy() { + ImageLoader.getInstance().destroy(); + } + + /** + * 暂停图片加载 + */ + public static void pause() { + ImageLoader.getInstance().pause(); + } + + /** + * 恢复图片加载 + */ + public static void resume() { + ImageLoader.getInstance().resume(); + } + + /** + * 停止图片加载 + */ + public static void stop() { + ImageLoader.getInstance().stop(); + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/receiver/NetWorkReceiver.java b/lib/DevOther/src/main/java/dev/receiver/NetWorkReceiver.java new file mode 100644 index 0000000000..ff94d0f409 --- /dev/null +++ b/lib/DevOther/src/main/java/dev/receiver/NetWorkReceiver.java @@ -0,0 +1,177 @@ +package dev.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.os.Build; +import android.support.annotation.RequiresPermission; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 网络监听广播 + * @author Ttt + */ +public final class NetWorkReceiver extends BroadcastReceiver { + + private NetWorkReceiver() { + super(); + } + + // 日志 TAG + private static final String TAG = NetWorkReceiver.class.getSimpleName(); + // 当前连接的状态 + private static int mConnectState = NetWorkReceiver.NO_NETWORK; + + // ======== + // = 常量 = + // ======== + + private static final int BASE = 202030; + // Wifi + public static final int NET_WIFI = BASE + 1; + // 移动网络 + public static final int NET_MOBILE = BASE + 2; + // ( 无网络 / 未知 ) 状态 + public static final int NO_NETWORK = BASE + 3; + + @Override + public void onReceive(Context context, Intent intent) { + try { + String action = intent.getAction(); + // 打印当前触发的广播 + LogPrintUtils.dTag(TAG, "NetWorkReceiver onReceive Action: " + action); + // 网络连接状态改变时通知 + if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) { + // 设置连接类型 + mConnectState = getConnectType(); + // 触发事件 + if (NetWorkReceiver.networdStateListener != null) { + NetWorkReceiver.networdStateListener.onNetworkState(mConnectState); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "onReceive"); + } + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 是否连接网络 + * @return {@code true} yes, {@code false} no + */ + public static boolean isConnectNetWork() { + return (mConnectState == NET_WIFI || mConnectState == NET_MOBILE); + } + + /** + * 获取连接的网络类型 + * @return 连接的网络类型 + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public static int getConnectType() { + // 获取手机所有连接管理对象 ( 包括对 wi-fi,net 等连接的管理 ) + try { + // 获取网络连接状态 + ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); + // 版本兼容处理 + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + // 判断连接的是否 wifi + NetworkInfo.State wifiState = cManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState(); + // 判断是否连接上 + if (wifiState == NetworkInfo.State.CONNECTED || wifiState == NetworkInfo.State.CONNECTING) { + return NET_WIFI; + } else { + // 判断连接的是否移动网络 + NetworkInfo.State mobileState = cManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState(); + // 判断移动网络是否连接上 + if (mobileState == NetworkInfo.State.CONNECTED || mobileState == NetworkInfo.State.CONNECTING) { + return NET_MOBILE; + } + } + } else { + // 获取当前活跃的网络 ( 连接的网络信息 ) + Network network = cManager.getActiveNetwork(); + if (network != null) { + NetworkCapabilities networkCapabilities = cManager.getNetworkCapabilities(network); + // 判断连接的是否 wifi + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + return NET_WIFI; + } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + return NET_MOBILE; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getConnectType"); + } + return NO_NETWORK; + } + + // = + + // 网络广播监听 + private static final NetWorkReceiver netReceiver = new NetWorkReceiver(); + + /** + * 注册网络广播监听 + */ + public static void registerReceiver() { + try { + IntentFilter filter = new IntentFilter(); + // 网络连接状态改变时通知 + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.setPriority(Integer.MAX_VALUE); + // 注册广播 + DevUtils.getContext().registerReceiver(netReceiver, filter); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "registerReceiver"); + } + } + + /** + * 取消注册网络广播监听 + */ + public static void unregisterReceiver() { + try { + DevUtils.getContext().unregisterReceiver(netReceiver); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "unregisterReceiver"); + } + } + + // = + + // 监听通知事件 + private static NetwordStateListener networdStateListener; + + /** + * 设置监听通知事件 + * @param listener {@link NetwordStateListener} + */ + public static void setNetListener(final NetwordStateListener listener) { + NetWorkReceiver.networdStateListener = listener; + } + + /** + * detail: 监听通知事件 + * @author Ttt + */ + public interface NetwordStateListener { + + /** + * 网络连接状态改变时通知 + * @param type 通知类型 + */ + void onNetworkState(int type); + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/receiver/PhoneReceiver.java b/lib/DevOther/src/main/java/dev/receiver/PhoneReceiver.java new file mode 100644 index 0000000000..730f0d94be --- /dev/null +++ b/lib/DevOther/src/main/java/dev/receiver/PhoneReceiver.java @@ -0,0 +1,174 @@ +package dev.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 手机监听广播 + * @author Ttt + *
+ *     所需权限
+ *     
+ *     
+ * 
+ */ +public final class PhoneReceiver extends BroadcastReceiver { + + private PhoneReceiver() { + super(); + } + + // 日志 TAG + private static final String TAG = PhoneReceiver.class.getSimpleName(); + // 电话状态监听意图 + private static final String PHONE_STATE = "android.intent.action.PHONE_STATE"; + // 拨出电话意图 + private static final String NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL"; + // 通话号码 + private String mNumber; + // 是否拨号打出 + private boolean mIsDialOut; + + // ======== + // = 状态 = + // ======== + + // 未接 + private static final String RINGING = "RINGING"; + // 已接 + private static final String OFFHOOK = "OFFHOOK"; + // 挂断 + private static final String IDLE = "IDLE"; + + @Override + public void onReceive(Context context, Intent intent) { + try { + String action = intent.getAction(); + // 打印当前触发的广播 + LogPrintUtils.dTag(TAG, "PhoneReceiver onReceive Action: " + action); + // 判断类型 + if (NEW_OUTGOING_CALL.equals(action)) { + // 表示属于拨号 + mIsDialOut = true; + // 拨出号码 + mNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); + // 触发事件 + if (phoneListener != null) { // 播出电话 + phoneListener.onPhoneStateChanged(CallState.Outgoing, mNumber); + } + } else if (PHONE_STATE.equals(action)) { + // 通话号码 + mNumber = intent.getStringExtra("incoming_number"); + // 状态 + String state = intent.getStringExtra("state"); + // 判断状态 + switch (state) { + case RINGING: // 未接 + mIsDialOut = false; + if (phoneListener != null) { // 接入电话铃响 + phoneListener.onPhoneStateChanged(CallState.IncomingRing, mNumber); + } + break; + case OFFHOOK: // 已接 + if (!mIsDialOut && phoneListener != null) { // 接入通话中 + phoneListener.onPhoneStateChanged(CallState.Incoming, mNumber); + } + break; + case IDLE: // 挂断 + if (mIsDialOut) { + if (phoneListener != null) { // 播出电话结束 + phoneListener.onPhoneStateChanged(CallState.OutgoingEnd, mNumber); + } + } else { + if (phoneListener != null) { // 接入通话完毕 + phoneListener.onPhoneStateChanged(CallState.IncomingEnd, mNumber); + } + } + break; + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "onReceive"); + } + } + + // ================ + // = 对外公开方法 = + // ================ + + // 电话监听广播 + private static final PhoneReceiver phoneReceiver = new PhoneReceiver(); + + /** + * 注册电话监听广播 + */ + public static void registerReceiver() { + try { + IntentFilter filter = new IntentFilter(); + // 电话状态监听 + filter.addAction(PHONE_STATE); + filter.addAction(NEW_OUTGOING_CALL); + filter.setPriority(Integer.MAX_VALUE); + // 注册广播 + DevUtils.getContext().registerReceiver(phoneReceiver, filter); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "registerReceiver"); + } + } + + /** + * 取消注册电话监听广播 + */ + public static void unregisterReceiver() { + try { + DevUtils.getContext().unregisterReceiver(phoneReceiver); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "unregisterReceiver"); + } + } + + // = + + // 电话状态监听事件 + private static PhoneListener phoneListener; + + /** + * 设置电话状态监听事件 + * @param listener {@link PhoneListener} + */ + public static void setPhoneListener(final PhoneListener listener) { + PhoneReceiver.phoneListener = listener; + } + + /** + * detail: 电话状态监听事件 + * @author Ttt + */ + public interface PhoneListener { + + /** + * 电话状态改变通知 + * @param state 通话状态 + * @param number 通话号码 + */ + void onPhoneStateChanged(CallState state, String number); + } + + /** + * detail: 通话状态 + * @author Ttt + */ + public enum CallState { + + Outgoing, // 播出电话 + OutgoingEnd, // 播出电话结束 + IncomingRing, // 接入电话铃响 + Incoming, // 接入通话中 + IncomingEnd // 接入通话完毕 + } +} diff --git a/lib/DevOther/src/main/java/dev/receiver/ScreenReceiver.java b/lib/DevOther/src/main/java/dev/receiver/ScreenReceiver.java new file mode 100644 index 0000000000..8de451f085 --- /dev/null +++ b/lib/DevOther/src/main/java/dev/receiver/ScreenReceiver.java @@ -0,0 +1,123 @@ +package dev.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 屏幕监听广播 ( 锁屏 / 解锁 / 亮屏 ) + * @author Ttt + */ +public final class ScreenReceiver extends BroadcastReceiver { + + private ScreenReceiver() { + super(); + } + + // 日志 TAG + private static final String TAG = ScreenReceiver.class.getSimpleName(); + + @Override + public void onReceive(Context context, Intent intent) { + try { + String action = intent.getAction(); + // 打印当前触发的广播 + LogPrintUtils.dTag(TAG, "ScreenReceiver onReceive Action: " + action); + // 判断类型 + switch (action) { + case Intent.ACTION_SCREEN_ON: // 开屏 + if (screenListener != null) { + screenListener.screenOn(); + } + break; + case Intent.ACTION_SCREEN_OFF: // 锁屏 + if (screenListener != null) { + screenListener.screenOff(); + } + break; + case Intent.ACTION_USER_PRESENT: // 解锁 + if (screenListener != null) { + screenListener.userPresent(); + } + break; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "onReceive"); + } + } + + // ================ + // = 对外公开方法 = + // ================ + + // 屏幕广播监听 + private static final ScreenReceiver screenReceiver = new ScreenReceiver(); + + /** + * 注册屏幕监听广播 + */ + public static void registerReceiver() { + try { + IntentFilter filter = new IntentFilter(); + // 屏幕状态改变通知 + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_USER_PRESENT); + filter.setPriority(Integer.MAX_VALUE); + // 注册广播 + DevUtils.getContext().registerReceiver(screenReceiver, filter); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "registerReceiver"); + } + } + + /** + * 取消注册屏幕监听广播 + */ + public static void unregisterReceiver() { + try { + DevUtils.getContext().unregisterReceiver(screenReceiver); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "unregisterReceiver"); + } + } + + // = + + // 屏幕监听事件 + private static ScreenListener screenListener; + + /** + * 设置屏幕监听事件 + * @param listener {@link ScreenListener} + */ + public static void setScreenListener(final ScreenListener listener) { + ScreenReceiver.screenListener = listener; + } + + /** + * detail: 屏幕监听事件 + * @author Ttt + */ + public interface ScreenListener { + + /** + * 用户打开屏幕 ( 屏幕变亮 ) + */ + void screenOn(); + + /** + * 锁屏触发 + */ + void screenOff(); + + /** + * 用户解锁触发 + */ + void userPresent(); + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/receiver/SmsReceiver.java b/lib/DevOther/src/main/java/dev/receiver/SmsReceiver.java new file mode 100644 index 0000000000..a0a25c9b21 --- /dev/null +++ b/lib/DevOther/src/main/java/dev/receiver/SmsReceiver.java @@ -0,0 +1,152 @@ +package dev.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.telephony.SmsMessage; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 短信监听广播 + * @author Ttt + *
+ *     所需权限
+ *     
+ * 
+ */ +public final class SmsReceiver extends BroadcastReceiver { + + private SmsReceiver() { + super(); + } + + // 日志 TAG + private static final String TAG = SmsReceiver.class.getSimpleName(); + + @Override + public void onReceive(Context context, Intent intent) { + try { + Object[] pdus = (Object[]) intent.getExtras().get("pdus"); + String originatingAddress = null; + String serviceCenterAddress = null; + if (pdus != null) { + // 消息内容 + String message = ""; + // 循环拼接内容 + for (Object object : pdus) { + SmsMessage sms = SmsMessage.createFromPdu((byte[]) object); + message += sms.getMessageBody(); // 消息内容 - 多条消息, 合并成一条 + originatingAddress = sms.getOriginatingAddress(); + serviceCenterAddress = sms.getServiceCenterAddress(); + // 触发事件 + if (smsListener != null) { + smsListener.onMessage(sms); + } + } + // 触发事件 + if (smsListener != null) { + smsListener.onMessage(message, originatingAddress, serviceCenterAddress); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "onReceive"); + } + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 获取消息数据 + * @param message {@link SmsMessage} + * @return 消息数据 + */ + public static String getMessageData(final SmsMessage message) { + StringBuilder builder = new StringBuilder(); + if (message != null) { + builder.append("\ngetDisplayMessageBody: " + message.getDisplayMessageBody()); + builder.append("\ngetDisplayOriginatingAddress: " + message.getDisplayOriginatingAddress()); + builder.append("\ngetEmailBody: " + message.getEmailBody()); + builder.append("\ngetEmailFrom: " + message.getEmailFrom()); + builder.append("\ngetMessageBody: " + message.getMessageBody()); + builder.append("\ngetOriginatingAddress: " + message.getOriginatingAddress()); + builder.append("\ngetPseudoSubject: " + message.getPseudoSubject()); + builder.append("\ngetServiceCenterAddress: " + message.getServiceCenterAddress()); + builder.append("\ngetIndexOnIcc: " + message.getIndexOnIcc()); + builder.append("\ngetMessageClass: " + message.getMessageClass()); + builder.append("\ngetUserData: " + new String(message.getUserData())); + } + return builder.toString(); + } + + // = + + // 短信监听广播 + private static final SmsReceiver smsReceiver = new SmsReceiver(); + + /** + * 注册短信监听广播 + */ + public static void registerReceiver() { + try { + IntentFilter filter = new IntentFilter(); + // 短信获取监听 + filter.addAction("android.provider.Telephony.SMS_RECEIVED"); + filter.setPriority(Integer.MAX_VALUE); + // 注册广播 + DevUtils.getContext().registerReceiver(smsReceiver, filter); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "registerReceiver"); + } + } + + /** + * 取消注册短信监听广播 + */ + public static void unregisterReceiver() { + try { + DevUtils.getContext().unregisterReceiver(smsReceiver); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "unregisterReceiver"); + } + } + + // = + + // 短信监听事件 + private static SmsListener smsListener; + + /** + * 设置短信监听事件 + * @param listener {@link SmsListener} + */ + public static void setSmsListener(final SmsListener listener) { + SmsReceiver.smsListener = listener; + } + + /** + * detail: 短信监听事件 + * @author Ttt + */ + public static abstract class SmsListener { + + /** + * 最终触发通知 ( 超过长度的短信消息, 最终合并成一条内容体 ) + * @param message 短信内容 + * @param originatingAddress 短信的原始地址 ( 发件人 ) + * @param serviceCenterAddress 短信服务中心地址 + */ + public abstract void onMessage(String message, String originatingAddress, String serviceCenterAddress); + + /** + * 收到消息提醒 ( 超过长度的消息变成两条会触发多次 ) + * @param message {@link SmsMessage} + */ + public void onMessage(SmsMessage message) { + } + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/receiver/TimeReceiver.java b/lib/DevOther/src/main/java/dev/receiver/TimeReceiver.java new file mode 100644 index 0000000000..df802b425a --- /dev/null +++ b/lib/DevOther/src/main/java/dev/receiver/TimeReceiver.java @@ -0,0 +1,123 @@ +package dev.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 时间监听广播 + * @author Ttt + */ +public final class TimeReceiver extends BroadcastReceiver { + + private TimeReceiver() { + super(); + } + + // 日志 TAG + private static final String TAG = TimeReceiver.class.getSimpleName(); + + @Override + public void onReceive(Context context, Intent intent) { + try { + String action = intent.getAction(); + // 打印当前触发的广播 + LogPrintUtils.dTag(TAG, "TimeReceiver onReceive Action: " + action); + // 判断类型 + switch (action) { + case Intent.ACTION_TIMEZONE_CHANGED: // 时区改变 + if (timeListener != null) { + timeListener.onTimeZoneChanged(); + } + break; + case Intent.ACTION_TIME_CHANGED: // 设置时间 + if (timeListener != null) { + timeListener.onTimeChanged(); + } + break; + case Intent.ACTION_TIME_TICK: // 每分钟调用 + if (timeListener != null) { + timeListener.onTimeTick(); + } + break; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "onReceive"); + } + } + + // ================ + // = 对外公开方法 = + // ================ + + // 时间监听广播 + private static final TimeReceiver timeReceiver = new TimeReceiver(); + + /** + * 注册时间监听广播 + */ + public static void registerReceiver() { + try { + IntentFilter filter = new IntentFilter(); + // 监听时间、时区改变通知 + filter.addAction(Intent.ACTION_TIME_TICK); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + filter.setPriority(Integer.MAX_VALUE); + // 注册广播 + DevUtils.getContext().registerReceiver(timeReceiver, filter); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "registerReceiver"); + } + } + + /** + * 取消注册时间监听广播 + */ + public static void unregisterReceiver() { + try { + DevUtils.getContext().unregisterReceiver(timeReceiver); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "unregisterReceiver"); + } + } + + // = + + // 时间监听事件 + private static TimeListener timeListener; + + /** + * 设置时间监听事件 + * @param listener {@link TimeListener} + */ + public static void setTimeListener(final TimeListener listener) { + TimeReceiver.timeListener = listener; + } + + /** + * detail: 时间监听事件 + * @author Ttt + */ + public interface TimeListener { + + /** + * 时区改变 + */ + void onTimeZoneChanged(); + + /** + * 设置时间 + */ + void onTimeChanged(); + + /** + * 每分钟调用 + */ + void onTimeTick(); + } +} diff --git a/lib/DevOther/src/main/java/dev/receiver/WifiReceiver.java b/lib/DevOther/src/main/java/dev/receiver/WifiReceiver.java new file mode 100644 index 0000000000..65cb72f107 --- /dev/null +++ b/lib/DevOther/src/main/java/dev/receiver/WifiReceiver.java @@ -0,0 +1,273 @@ +package dev.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.NetworkInfo; +import android.net.wifi.WifiManager; +import android.os.Message; +import android.os.Parcelable; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.app.wifi.WifiUtils; + +/** + * detail: Wifi 监听广播 + * @author Ttt + *
+ *     所需权限
+ *     
+ *     
+ * 
+ */ +public final class WifiReceiver extends BroadcastReceiver { + + private WifiReceiver() { + super(); + } + + // 日志 TAG + private static final String TAG = WifiReceiver.class.getSimpleName(); + + // ======== + // = 常量 = + // ======== + + private static final int BASE = 302030; + // startScan() 扫描附近 Wifi 结束触发 + public static final int WIFI_SCAN_FINISH = BASE + 1; + // 已连接的 Wifi 强度发生变化 + public static final int WIFI_RSSI_CHANGED = BASE + 2; + // Wifi 认证错误 ( 密码错误等 ) + public static final int WIFI_ERROR_AUTHENTICATING = BASE + 3; + // 连接错误 ( 其他错误 ) + public static final int WIFI_ERROR_UNKNOWN = BASE + 4; + // Wifi 已打开 + public static final int WIFI_STATE_ENABLED = BASE + 5; + // Wifi 正在打开 + public static final int WIFI_STATE_ENABLING = BASE + 6; + // Wifi 已关闭 + public static final int WIFI_STATE_DISABLED = BASE + 7; + // Wifi 正在关闭 + public static final int WIFI_STATE_DISABLING = BASE + 8; + // Wifi 状态未知 + public static final int WIFI_STATE_UNKNOWN = BASE + 9; + // Wifi 连接成功 + public static final int CONNECTED = BASE + 10; + // Wifi 连接中 + public static final int CONNECTING = BASE + 11; + // Wifi 连接失败、断开 + public static final int DISCONNECTED = BASE + 12; + // Wifi 暂停、延迟 + public static final int SUSPENDED = BASE + 13; + // Wifi 未知 + public static final int UNKNOWN = BASE + 14; + + @Override + public void onReceive(Context context, Intent intent) { + try { + // 触发回调通知 ( 每次进入都通知 ) + if (wifiListener != null) wifiListener.onIntoTrigger(); + // 触发意图 + String action = intent.getAction(); + // 打印当前触发的广播 + LogPrintUtils.dTag(TAG, "WifiReceiver onReceive Action: " + action); + // 判断类型 + switch (action) { + case WifiManager.SCAN_RESULTS_AVAILABLE_ACTION: // 当调用 WifiManager 的 startScan() 方法, 扫描结束后, 系统会发出改 Action 广播 + if (wifiListener != null) { + wifiListener.onTrigger(WIFI_SCAN_FINISH); + } + break; + case WifiManager.RSSI_CHANGED_ACTION: // 当前连接的 Wifi 强度发生变化触发 + if (wifiListener != null) { + wifiListener.onTrigger(WIFI_RSSI_CHANGED); + } + break; + case WifiManager.SUPPLICANT_STATE_CHANGED_ACTION: // 发送 Wifi 连接的过程信息, 如果出错 ERROR 信息才会收到, 连接 Wifi 时触发, 触发多次 + // 出现错误状态, 则获取错误状态 + int wifiErrorCode = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, 0); + // 获取错误状态 + switch (wifiErrorCode) { + case WifiManager.ERROR_AUTHENTICATING: // 认证错误, 如密码错误等 + if (wifiListener != null) { + wifiListener.onTrigger(WIFI_ERROR_AUTHENTICATING); + } + break; + default: // 连接错误 ( 其他错误 ) + if (wifiListener != null) { + wifiListener.onTrigger(WIFI_ERROR_UNKNOWN); + } + break; + } + break; + case WifiManager.WIFI_STATE_CHANGED_ACTION: // 监听 Wifi 打开与关闭等状态 + // 获取 Wifi 状态 + int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN); + switch (wifiState) { + case WifiManager.WIFI_STATE_ENABLED: // 已打开 + if (wifiListener != null) { + wifiListener.onTrigger(WIFI_STATE_ENABLED); + } + break; + case WifiManager.WIFI_STATE_ENABLING: // 正在打开 + if (wifiListener != null) { + wifiListener.onTrigger(WIFI_STATE_ENABLING); + } + break; + case WifiManager.WIFI_STATE_DISABLED: // 已关闭 + if (wifiListener != null) { + wifiListener.onTrigger(WIFI_STATE_DISABLED); + } + break; + case WifiManager.WIFI_STATE_DISABLING: // 正在关闭 + if (wifiListener != null) { + wifiListener.onTrigger(WIFI_STATE_DISABLING); + } + break; + case WifiManager.WIFI_STATE_UNKNOWN: // 未知 + if (wifiListener != null) { + wifiListener.onTrigger(WIFI_STATE_UNKNOWN); + } + break; + } + break; + case WifiManager.NETWORK_STATE_CHANGED_ACTION: // Wifi 连接过程的状态 + Parcelable parcelableExtra = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); + if (parcelableExtra != null) { + // 获取连接信息 + NetworkInfo networkInfo = (NetworkInfo) parcelableExtra; + // 获取连接的状态 + NetworkInfo.State state = networkInfo.getState(); + // 通知消息 + Message msg = new Message(); + // 当前连接的 SSID + msg.obj = WifiUtils.getSSID(); + // 判断连接状态 + switch (state) { + case CONNECTED: // 连接成功 + msg.what = CONNECTED; + break; + case CONNECTING: // 连接中 + msg.what = CONNECTING; + break; + case DISCONNECTED: // 连接失败、断开 + msg.what = DISCONNECTED; + break; + case SUSPENDED: // 暂停、延迟 + msg.what = SUSPENDED; + break; + case UNKNOWN: // 未知 + msg.what = UNKNOWN; + break; + } + // 触发回调 + if (wifiListener != null) { + wifiListener.onTrigger(msg.what, msg); + } + } + break; + case WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION: // 判断是否 Wifi 打开了, 变化触发一次 + // 判断是否打开 Wifi + boolean isOpenWifi = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false); + // 触发回调 + if (wifiListener != null) { + wifiListener.onWifiSwitch(isOpenWifi); + } + break; + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "onReceive"); + } + } + + // ================ + // = 对外公开方法 = + // ================ + + // Wifi 监听广播 + private static final WifiReceiver wifiReceiver = new WifiReceiver(); + + /** + * 注册 Wifi 监听广播 + */ + public static void registerReceiver() { + try { + IntentFilter filter = new IntentFilter(); + // 当调用 WifiManager 的 startScan() 方法, 扫描结束后, 系统会发出改 Action 广播 + filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + // 当前连接的 Wifi 强度发生变化触发 + filter.addAction(WifiManager.RSSI_CHANGED_ACTION); + // Wifi 在连接过程的状态返回 + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + // 监听 Wifi 的打开与关闭等状态, 与 Wifi 的连接无关 + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + // 发送 Wifi 连接的过程信息, 如果出错 ERROR 信息才会收到, 连接 Wifi 时触发, 触发多次 + filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); + // 判断是否 Wifi 打开了, 变化触发一次 + filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); + // 注册广播 + DevUtils.getContext().registerReceiver(wifiReceiver, filter); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "registerReceiver"); + } + } + + /** + * 取消注册 Wifi 监听广播 + */ + public static void unregisterReceiver() { + try { + DevUtils.getContext().unregisterReceiver(wifiReceiver); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "unregisterReceiver"); + } + } + + // = + + // Wifi 监听事件 + private static WifiListener wifiListener; + + /** + * 设置 Wifi 监听事件 + * @param listener {@link WifiListener} + */ + public static void setWifiListener(final WifiListener listener) { + WifiReceiver.wifiListener = listener; + } + + /** + * detail: Wifi 监听事件 + * @author Ttt + */ + public static abstract class WifiListener { + + /** + * 触发回调通知 ( 每次进入都通知 ) + */ + public void onIntoTrigger() { + } + + /** + * 触发回调通知 + * @param what 触发类型 + */ + public abstract void onTrigger(int what); + + /** + * 触发回调通知 ( Wifi 连接过程的状态 ) + * @param what 触发类型 + * @param msg 触发信息 + */ + public abstract void onTrigger(int what, Message msg); + + /** + * Wifi 开关状态 + * @param isOpenWifi 是否打开 wifi + */ + public abstract void onWifiSwitch(boolean isOpenWifi); + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/service/AccessibilityListenerService.java b/lib/DevOther/src/main/java/dev/service/AccessibilityListenerService.java new file mode 100644 index 0000000000..27be4e261c --- /dev/null +++ b/lib/DevOther/src/main/java/dev/service/AccessibilityListenerService.java @@ -0,0 +1,241 @@ +package dev.service; + +import android.accessibilityservice.AccessibilityService; +import android.content.Intent; +import android.provider.Settings; +import android.view.accessibility.AccessibilityEvent; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; +import dev.utils.app.AccessibilityUtils; + +/** + * detail: 无障碍功能监听服务 + * @author Ttt + *
+ *     具体配合 {@link AccessibilityUtils} 使用
+ *     AccessibilityService 在 API < 18 的时候使用 AccessibilityService
+ *     

+ * 所需权限 + * + *

+ * 动态配置方式, 只能用来配置动态属性: eventTypes、feedbackType、flags、notificaionTimeout、packageNames + * AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo(); + * serviceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; + * serviceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; + * serviceInfo.packageNames = new String[]{ "com.dev"" }; + * serviceInfo.notificationTimeout=100; + * setServiceInfo(serviceInfo); + *
+ */ +public final class AccessibilityListenerService extends AccessibilityService { + + // 日志 TAG + private static final String TAG = AccessibilityService.class.getSimpleName(); + // 当前服务所持对象 + private static AccessibilityListenerService self; + + /** + * 通过这个函数可以接收系统发送来的 AccessibilityEvent + *
+     *     接收来的 AccessibilityEvent 是经过过滤的, 过滤是在配置工作时设置的
+     * 
+ * @param event {@link AccessibilityEvent} + */ + @Override + public void onAccessibilityEvent(AccessibilityEvent event) { + if (accessibilityListener != null) { + accessibilityListener.onAccessibilityEvent(event, this); + } + } + + /** + * 系统想要中断 AccessibilityService 返给的响应时会调用 + */ + @Override + public void onInterrupt() { + LogPrintUtils.dTag(TAG, "onInterrupt"); + + if (accessibilityListener != null) { + accessibilityListener.onInterrupt(); + } + } + + /** + * 系统成功绑定该服务时被触发, 也就是当你在设置中开启相应的服务, 系统成功的绑定了该服务时会触发, 通常我们可以在这里做一些初始化操作 + */ + @Override + protected void onServiceConnected() { + super.onServiceConnected(); + LogPrintUtils.dTag(TAG, "onServiceConnected"); + } + + // ============ + // = 生命周期 = + // ============ + + @Override + public void onCreate() { + super.onCreate(); + LogPrintUtils.dTag(TAG, "onCreate"); + + if (accessibilityListener != null) { + accessibilityListener.onServiceCreated(this); + } + self = this; + } + + @Override + public void onDestroy() { + super.onDestroy(); + LogPrintUtils.dTag(TAG, "onDestroy"); + + if (accessibilityListener != null) { + accessibilityListener.onServiceDestroy(); + accessibilityListener = null; + } + self = null; + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 获取当前服务所持对象 + * @return {@link AccessibilityListenerService} + */ + public static AccessibilityListenerService getSelf() { + return self; + } + + /** + * 启动服务 + */ + public static void startService() { + try { + DevUtils.getContext().startService(new Intent(DevUtils.getContext(), AccessibilityListenerService.class)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startService"); + } + } + + /** + * 停止服务 + */ + public static void stopService() { + try { + DevUtils.getContext().stopService(new Intent(DevUtils.getContext(), AccessibilityListenerService.class)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopService"); + } + } + + // = + + /** + * 检查是否开启无障碍功能 + *
+     *     未开启则跳转至辅助功能设置页面
+     * 
+ * @return {@code true} open, {@code false} close + */ + public static boolean checkAccessibility() { + return checkAccessibility(DevUtils.getContext().getPackageName()); + } + + /** + * 检查是否开启无障碍功能 + *
+     *     未开启则跳转至辅助功能设置页面
+     * 
+ * @param packageName 应用包名 + * @return {@code true} open, {@code false} close + */ + public static boolean checkAccessibility(final String packageName) { + if (packageName == null) return false; + // 判断辅助功能是否开启 + if (!isAccessibilitySettingsOn(packageName)) { + // 跳转至辅助功能设置页面 + DevUtils.getContext().startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + return false; + } + return true; + } + + /** + * 判断是否开启无障碍功能 + * @param packageName 应用包名 + * @return {@code true} open, {@code false} close + */ + public static boolean isAccessibilitySettingsOn(final String packageName) { + if (packageName == null) return false; + // 无障碍功能开启状态 + int accessibilityEnabled = 0; + try { + accessibilityEnabled = Settings.Secure.getInt(DevUtils.getContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED); + } catch (Settings.SettingNotFoundException e) { + LogPrintUtils.eTag(TAG, e, "isAccessibilitySettingsOn - Settings.Secure.ACCESSIBILITY_ENABLED"); + } + if (accessibilityEnabled == 1) { + try { + String services = Settings.Secure.getString(DevUtils.getContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); + if (services != null) { + return services.toLowerCase().contains(packageName.toLowerCase()); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isAccessibilitySettingsOn - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES"); + } + } + return false; + } + + // = + + // 监听事件 + private static AccessibilityListener accessibilityListener; + + /** + * 设置监听事件 + * @param listener {@link AccessibilityListener} + */ + public static void setAccessibilityListener(final AccessibilityListener listener) { + AccessibilityListenerService.accessibilityListener = listener; + } + + /** + * detail: 监听事件 + * @author Ttt + */ + public static abstract class AccessibilityListener { + + /** + * 通过这个函数可以接收系统发送来的 AccessibilityEvent + *
+         *     接收来的 AccessibilityEvent 是经过过滤的, 过滤是在配置工作时设置的
+         * 
+ * @param event {@link AccessibilityEvent} + * @param service {@link AccessibilityListenerService} + */ + public abstract void onAccessibilityEvent(AccessibilityEvent event, AccessibilityListenerService service); + + /** + * 系统想要中断 AccessibilityService 返给的响应时会调用 + */ + public void onInterrupt() { + } + + /** + * 服务创建通知 + * @param service {@link AccessibilityListenerService} + */ + public void onServiceCreated(AccessibilityListenerService service) { + } + + /** + * 服务销毁通知 + */ + public void onServiceDestroy() { + } + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/service/NotificationService.java b/lib/DevOther/src/main/java/dev/service/NotificationService.java new file mode 100644 index 0000000000..1c8b017e5d --- /dev/null +++ b/lib/DevOther/src/main/java/dev/service/NotificationService.java @@ -0,0 +1,259 @@ +package dev.service; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Intent; +import android.os.Build; +import android.provider.Settings; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.support.v4.app.NotificationManagerCompat; + +import java.util.Set; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: 通知栏监听服务 + * @author Ttt + *
+ *     @see 
+ *     所需权限
+ *     
+ * 
+ */ +@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) +public final class NotificationService extends NotificationListenerService { + + // 日志 TAG + private static final String TAG = NotificationService.class.getSimpleName(); + // 当前服务所持对象 + private static NotificationService self; + + // ============ + // = 通知回调 = + // ============ + + /** + * 当系统收到新的通知后触发回调 + * @param sbn {@link StatusBarNotification} + */ + @Override + public void onNotificationPosted(StatusBarNotification sbn) { + if (self != null && notificationListener != null) { + notificationListener.onNotificationPosted(sbn); + } + } + + /** + * 当系统通知被删掉后触发回调 + * @param sbn {@link StatusBarNotification} + */ + @Override + public void onNotificationRemoved(StatusBarNotification sbn) { + if (self != null && notificationListener != null) { + notificationListener.onNotificationRemoved(sbn); + } + } + + // ============ + // = 生命周期 = + // ============ + + @Override + public void onCreate() { + super.onCreate(); + LogPrintUtils.dTag(TAG, "onCreate"); + + if (notificationListener != null) { + notificationListener.onServiceCreated(this); + } + self = this; + } + + @Override + public void onDestroy() { + super.onDestroy(); + LogPrintUtils.dTag(TAG, "onDestroy"); + + if (notificationListener != null) { + notificationListener.onServiceDestroy(); + notificationListener = null; + } + self = null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + LogPrintUtils.dTag(TAG, "onStartCommand"); + + return notificationListener == null ? START_STICKY : notificationListener.onStartCommand(this, intent, flags, startId); + } + + // ================ + // = 对外公开方法 = + // ================ + + /** + * 获取当前服务所持对象 + * @return {@link NotificationService} + */ + public static NotificationService getSelf() { + return self; + } + + /** + * 启动服务 + */ + public static void startService() { + try { + DevUtils.getContext().startService(new Intent(DevUtils.getContext(), NotificationService.class)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, "startService"); + } + } + + /** + * 停止服务 + */ + public static void stopService() { + try { + DevUtils.getContext().stopService(new Intent(DevUtils.getContext(), NotificationService.class)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, "stopService"); + } + } + + // = + + /** + * 检查是否有获取通知栏信息权限并跳转设置页面 + * @return {@code true} yes, {@code false} no + */ + public static boolean checkAndIntentSetting() { + if (!isNotificationListenerEnabled()) { + startNotificationListenSettings(); + return false; + } + return true; + } + + /** + * 判断是否有获取通知栏信息权限 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNotificationListenerEnabled() { + return isNotificationListenerEnabled(DevUtils.getContext().getPackageName()); + } + + /** + * 判断是否有获取通知栏信息权限 + * @param packageName 应用包名 + * @return {@code true} yes, {@code false} no + */ + public static boolean isNotificationListenerEnabled(final String packageName) { + Set packageNames = NotificationManagerCompat.getEnabledListenerPackages(DevUtils.getContext()); + return packageNames.contains(packageName); + } + + /** + * 跳转到设置页面, 开启获取通知栏信息权限 + */ + public static void startNotificationListenSettings() { + try { + Intent intent; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS); + } else { + intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); + } + if (!(DevUtils.getContext() instanceof Activity)) { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + DevUtils.getContext().startActivity(intent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startNotificationListenSettings"); + } + } + + // = + +// cancelAllNotifications() 删除系统中所有可被清除的通知 +// getActiveNotifications() 返回当前系统所有通知到 StatusBarNotification[] + + /** + * 取消通知方法 + *
+     *     cancelNotification(String key) 是 API >= 21 才可以使用的, 利用 StatusBarNotification 的 getKey() 方法来获取 key 并取消通知
+     *     cancelNotification(String pkg, String tag, int id) 在 API < 21 时可以使用, 在 API >= 21 时使用此方法来取消通知将无效 ( 被废弃 )
+     * 
+ * @param sbn {@link StatusBarNotification} + */ + public void cancelNotification(final StatusBarNotification sbn) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + cancelNotification(sbn.getKey()); + } else { + cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId()); + } + } + + // = + + // 通知栏监听事件 + private static NotificationListener notificationListener; + + /** + * 设置通知栏监听事件 + * @param listener {@link NotificationListener} + */ + public static void setNotificationListener(final NotificationListener listener) { + NotificationService.notificationListener = listener; + } + + /** + * detail: 通知栏监听事件 + * @author Ttt + */ + public interface NotificationListener { + + /** + * 服务创建通知 + * @param service {@link NotificationService} + */ + void onServiceCreated(NotificationService service); + + /** + * 服务销毁通知 + */ + void onServiceDestroy(); + + /** + * 触发服务指令 + * @param service {@link NotificationService} + * @param intent {@link Intent} + * @param flags Additional data about this start request. + * @param startId A unique integer representing this specific request to start. Use with {@link #stopSelfResult(int)}. + * @return The return value indicates what semantics the system should + * use for the service's current started state. It may be one of the + * constants associated with the {@link #START_CONTINUATION_MASK} bits. + */ + int onStartCommand(NotificationService service, Intent intent, int flags, int startId); + + /** + * 当系统收到新的通知后触发回调 + *
+         *     当 API > 18 时, 利用 Notification.extras 来获取通知内容, extras 是在 API 19 时被加入的
+         *     当 API = 18 时, 利用反射获取 Notification 中的内容
+         * 
+ * @param sbn {@link StatusBarNotification} + */ + void onNotificationPosted(StatusBarNotification sbn); + + /** + * 当系统通知被删掉后触发回调 + * @param sbn {@link StatusBarNotification} + */ + void onNotificationRemoved(StatusBarNotification sbn); + } +} \ No newline at end of file diff --git a/lib/DevOther/src/main/java/dev/temp/ChineseUtils.java b/lib/DevOther/src/main/java/dev/temp/ChineseUtils.java new file mode 100644 index 0000000000..b23a413cde --- /dev/null +++ b/lib/DevOther/src/main/java/dev/temp/ChineseUtils.java @@ -0,0 +1,50 @@ +package dev.temp; + +import java.util.Random; + +import dev.utils.LogPrintUtils; + +/** + * detail: 随机生成汉字工具类 + * @author Ttt + */ +public final class ChineseUtils { + + private ChineseUtils() { + } + + // 日志 TAG + private static final String TAG = ChineseUtils.class.getSimpleName(); + + /** + * 获取随机汉字 + * @param number 汉字个数 + * @return 随机汉字 + */ + public static String getRandomWord(final int number) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < number; i++) { + builder.append(getRandomWord()); + } + return builder.toString(); + } + + /** + * 获取一个随机汉字 + * @return 一个随机汉字 + */ + public static String getRandomWord() { + Random random = new Random(); + int heightPos = 176 + Math.abs(random.nextInt(39)); + int lowPos = 161 + Math.abs(random.nextInt(93)); + byte[] bytes = new byte[2]; + bytes[0] = Integer.valueOf(heightPos).byteValue(); + bytes[1] = Integer.valueOf(lowPos).byteValue(); + try { + return new String(bytes, "GBK"); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getRandomWord"); + } + return ""; + } +} diff --git a/lib/DevOther/src/main/res/values/strings.xml b/lib/DevOther/src/main/res/values/strings.xml new file mode 100644 index 0000000000..8542005550 --- /dev/null +++ b/lib/DevOther/src/main/res/values/strings.xml @@ -0,0 +1,2 @@ + + diff --git a/lib/DevOther/src/main/res/xml/accessibility_config.xml b/lib/DevOther/src/main/res/xml/accessibility_config.xml new file mode 100644 index 0000000000..40858501a7 --- /dev/null +++ b/lib/DevOther/src/main/res/xml/accessibility_config.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/lib/README.md b/lib/README.md new file mode 100644 index 0000000000..f98c9683ca --- /dev/null +++ b/lib/README.md @@ -0,0 +1,14 @@ +# Lib + +该目录主要存储 DevUtils project 全部 lib module,封装快捷使用的工具类及 API 方法调用,隐藏内部逻辑判断对外提供便捷辅助类、统一回调类、Bean、Event 以及 Engine 兼容框架等 + + +## 目录结构 + +``` +- lib | 根目录 + - DevApp | Android 工具类库 + - DevAssist | 封装逻辑代码, 实现多个快捷功能辅助类、以及 Engine 兼容框架等 + - DevJava | Java 工具类库 (不依赖 android api) + - DevOther | 第三方库封装、以及部分特殊工具类等,方便 copy 封装类使用 +``` \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 2d6df85eb9..37f88d9bab 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,5 @@ -include ':app', ':DevLibUtils' +include ':app', ':DevApp', ':DevJava', ':DevAssist', ':DevOther' +project(':DevApp').projectDir = new File('lib/DevApp') +project(':DevJava').projectDir = new File('lib/DevJava') +project(':DevAssist').projectDir = new File('lib/DevAssist') +project(':DevOther').projectDir = new File('lib/DevOther') \ No newline at end of file