diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..30aa626c23 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/markdown-exported-files.xml b/.idea/markdown-exported-files.xml new file mode 100644 index 0000000000..5d1f1293cf --- /dev/null +++ b/.idea/markdown-exported-files.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml new file mode 100644 index 0000000000..076726f8a8 --- /dev/null +++ b/.idea/markdown-navigator.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/markdown-navigator/profiles_settings.xml b/.idea/markdown-navigator/profiles_settings.xml new file mode 100644 index 0000000000..57927c5a72 --- /dev/null +++ b/.idea/markdown-navigator/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 13c46297a6..a8d1aa613a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -24,7 +24,7 @@ - + diff --git a/.idea/modules.xml b/.idea/modules.xml index 1c811baddb..ef53dacb34 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,6 +3,7 @@ + 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 index ce976bd73d..3571e7e7d7 100644 --- a/DevLibUtils/README.md +++ b/DevLibUtils/README.md @@ -1,3 +1,4 @@ + ## 使用 > ##### 只需要在 Application 中调用 `DevUtils.init()` 进行初始化就行 @@ -5,29 +6,39 @@ ## 目录结构 ``` -- 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 | 数据校验工具类 +- dev.utils | 根目录 + - app | app相关工具类 + - anim | 动画工具类 + - assist | 辅助类 + - manager | 管理器 如: ActivityManager + - cache | 缓存工具类 + - camera1 | 摄像头相关 android.hardware.Camera + - image | 图片相关处理 + - info | App信息, PackageInfo 等 + - logger | 日志库 DevLogger + - player | 多媒体(视频、音频)播放封装 + - share | SharedPreferences 封装 + - toast | Toast + - toaster | Toaster 处理无通知权限 + - wifi | wifi、热点工具类 + - common | java工具类, 不依赖android api + - assist | 各种快捷辅助类 + - search | 搜索相关(文件搜索等) + - cipher | 编/解码工具类 + - encrypt | 加密工具类 + - random | 随机概率算法工具类 + - thread | 线程相关 + - validator | 数据校验工具类 ``` + ## 事项 -- 内部存在两个日志工具类 +- 内部存在两个日志工具类(工具类内部调用), 对外使用 [DevLogger](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/logger/DevLogger.md) + ```java +// 整个工具类内部日志信息, 都通过以下两个工具类输出打印, 并且通过 DevUtils.openLog() 控制开关 + // dev.utils.app - App 打印日志工具类 LogPrintUtils // dev.utils.common - Java Common 日志打印工具类 @@ -36,130 +47,222 @@ JCLogUtils - 需要开启日志, 单独调用 ```java -// 打开 lib 内部日志 +// 打开 lib 内部日志 - 线上环境, 不调用方法就行 DevUtils.openLog(); // 标示 debug 模式 DevUtils.openDebug(); ``` +- 工具类部分模块配置与使用 - [Use and Config](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/USE_CONFIG.md) + - 部分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) +- dev.utils | 根目录 + - [app](#devutilsapp) | app相关工具类 + - [anim](#devutilsappanim) | 动画工具类 + - [assist](#devutilsappassist) | 辅助类 + - [manager](#devutilsappassistmanager) | 管理器 如: ActivityManager + - [cache](#devutilsappcache) | 缓存工具类 + - [camera1](#devutilsappcamera1) | 摄像头相关 android.hardware.Camera + - [image](#devutilsappimage) | 图片相关处理 + - [info](#devutilsappinfo) | App信息, PackageInfo 等 + - [logger](#devutilsapplogger) | 日志库 DevLogger + - [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) | 数据校验工具类 -| 方法 | 注释 | -| :-: | :-: | -| 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`** -### `dev.utils.app` * **无障碍功能工具类 ->** [AccessibilityUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AccessibilityUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | +| getService | 获取 AccessibilityService 对象 | +| setService | 设置 AccessibilityService 对象 | | checkAccessibility | 检查是否开启无障碍功能 | | isAccessibilitySettingsOn | 判断是否开启无障碍功能 | -| printAccessibilityEvent | 打印Event 日志 | +| printAccessibilityEvent | 打印 AccessibilityEvent 信息日志 | +| findAccessibilityNodeInfosByText | 查找符合条件的节点 | +| findAccessibilityNodeInfosByViewId | 查找符合条件的节点 | +| performClick | 点击指定的节点 | +| performLongClick | 长按指定的节点 | +| preformActionBack | 触发返回键 | +| preformActionHome | 触发Home键 | +| preformActionPowerDialog | 启动长按电源按钮 Dialog | +| preformActionLockScreen | 锁定屏幕 -> 非锁屏 | +| preformActionTakeScreenshot | 截图 | +| preformActionNotifications | 打开通知栏 | +| preformActionRecents | 最近打开应用列表 | +| preformActionQuickSettings | 打开设置 | +| preformActionSplitScreen | 分屏 | +| preformAction | 模拟对应 Action 操作 | +| performGlobalAction | 模拟全局对应 Action 操作 | * **Acitivty 工具类 ->** [ActivityUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ActivityUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | +| getActivityByView | 返回 View context 所属的 Activity | | isActivityExists | 判断是否存在指定的Activity | | startHomeActivity | 回到桌面 -> 同点击Home键效果 | -| getLauncherActivity | 跳转到桌面 | -| getActivityIcon | 返回Activity 对应的图标 | -| getActivityLogo | 返回Activity 对应的Logo | +| getLauncherActivity | 获取 Launcher activity | +| getActivityIcon | 返回 Activity 对应的图标 | +| getActivityLogo | 返回 Activity 对应的Logo | +| getActivityToLauncher | 获取对应包名应用启动 Activity | +| getLauncherCategoryHomeToResolveInfo | 获取系统桌面信息 | +| getLauncherCategoryHomeToPackageName | 获取系统桌面信息 -> packageName | +| getLauncherCategoryHomeToActivityName | 获取系统桌面信息 -> activityName | +| getLauncherCategoryHomeToPackageAndName | 获取系统桌面信息 -> package/activityName | -* **AlarmManager (全局定时器/闹钟)指定时长或以周期形式执行某项操作 ->** [AlarmUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AlarmUtils.java) +* **ADB shell 工具类 ->** [ADBUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 | 静默安装 App | +| uninstallApp | 卸载 App | +| uninstallAppSilent | 静默卸载 App | +| 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 权限) | +| reboot2Recovery | 重启引导到 recovery (需要 root 权限) | +| reboot2Bootloader | 重启引导到 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/DevLibUtils/src/main/java/dev/utils/app/AlarmUtils.java) + +| 方法 | 注释 | +| :- | :- | | startAlarmIntent | 开启定时器 | | stopAlarmIntent | 关闭定时器 | | startAlarmService | 开启轮询服务 | -| stopAlarmService | 停止启轮询服务 | +| stopAlarmService | 停止轮询服务 | +| startAlarmBroadcast | 开启轮询广播 | +| stopAlarmBroadcast | 停止轮询广播 | +| startAlarmActivity | 开启轮询 Activity | +| stopAlarmActivity | 停止轮询 Activity | * **分析记录工具类 ->** [AnalysisRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AnalysisRecordUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | init | 初始化操作 | | record | 日志记录 | | isHandler | 是否处理日志记录 | | setHandler | 设置是否处理日志记录 | +| isAppendSpace | 是否追加空格 | +| setAppendSpace | 设置是否追加空格 | | getLogFolderName | 获取文件日志名 | | setLogFolderName | 设置日志文件夹名 | | getLogStoragePath | 获取日志存储路径 | | setLogStoragePath | 设置日志存储路径 | -| getStoragePath | getStoragePath | -| getFileName | getFileName | -| getFileFunction | getFileFunction | -| getFileIntervalTime | getFileIntervalTime | -| getFolderName | getFolderName | +| getStoragePath | 获取存储路径 | +| getFileName | 获取日志文件名 | +| getFileFunction | 获取日志文件记录的功能 | +| getFileIntervalTime | 获取日志文件记录间隔时间 | +| getFolderName | 获取日志文件夹名 | | obtain | 获取记录分析文件信息 | | getLogPath | 获取日志地址 | | getIntervalTimeFolder | 获取时间间隔 - 文件夹 | @@ -168,9 +271,10 @@ DevUtils.openDebug(); * **App通用工具类 ->** [AppCommonUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AppCommonUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | getUUID | 获取设备唯一id | | getRandomUUID | 获取随机数 唯一id | +| getFormatRes | 获取 R.string 资源的格式化字符串 | | isFroyo | 是否在2.2版本及以上 | | isGingerbread | 是否在2.3版本及以上 | | isGingerbreadMR1 | 是否在2.3.3版本及以上 | @@ -179,88 +283,93 @@ DevUtils.openDebug(); | 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版本及以上 | -| 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 | +| convertSDKVersion | 转换SDK版本 (convertSDKVersion(14) => Android 4.0.0-2) | + + +* **App(Android 工具类) ->** [AppUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AppUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getWindowManager | 获取 WindowManager | +| getView | 获取 View | +| getResources | 获取 Resources | +| getString | 获取 String | +| getTheme | 获取 Resources.Theme | +| getAssets | 获取 AssetManager | +| getDrawable | 获取 Drawable | +| getColor | 获取 Color | +| getColorStateList | 获取 ColorStateList | +| getSystemService | 获取 SystemService | +| getPackageManager | 获取 PackageManager | +| getConfiguration | 获取 Configuration | +| getDisplayMetrics | 获取 DisplayMetrics | +| getContentResolver | 获取 ContentResolver | +| 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值 | +| 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值 | +| getAppSignatureSHA256 | 获取应用签名的的 SHA256 值 | +| getAppSignatureMD5 | 获取应用签名的的 MD5 值 | | openPDFFile | 启动本地应用打开PDF | -| openWordFile | 启动本地应用打开PDF | +| openWordFile | 启动本地应用打开Word | | 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 | 减去状态栏同等高度 | +| subtractMarginTopEqualStatusBarHeight | 添加状态栏同等高度到View的顶部 | | setStatusBarColor | 设置状态栏颜色 | | setStatusBarAlpha | 设置状态栏透明度 | +| setStatusBarCustom | 设置自定义状态栏 | | setStatusBarColor4Drawer | 设置状态栏的颜色 | | setStatusBarAlpha4Drawer | 设置状态栏透明度 | | getActionBarHeight | 返回 ActionBase 高度 | | setNotificationBarVisibility | 设置通知栏是否显示 | | getNavBarHeight | 获取 NavigationView 高度 | | setNavBarVisibility | 设置导航栏是否可见(图标显示) | -| setNavBarImmersive | 设置沉浸模式 | -| isNavBarVisible | 判断顶部状态栏(图标)是否显示 | -| setNavBar | 设置是否显示状态栏图标等 | +| isNavBarVisible | 判断导航栏是否可见 | +| setNavBarColor | 设置导航栏颜色 | +| getNavBarColor | 设置导航栏颜色 | +| isSupportNavBar | 判断是否支持导航栏 | * **亮度相关工具类 ->** [BrightnessUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/BrightnessUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | isAutoBrightnessEnabled | 判断是否开启自动调节亮度 | | setAutoBrightnessEnabled | 设置是否开启自动调节亮度 | | getBrightness | 获取屏幕亮度 | @@ -269,31 +378,17 @@ DevUtils.openDebug(); | getWindowBrightness | 获取窗口亮度 | -* **摄像头相关工具类 ->** [CameraUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/CameraUtils.java) +* **本应用数据清除管理器 ->** [CleanUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/CleanUtils.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 | 清除自定义路径下的文件,使用需小心,请不要误删。而且只支持目录下的文件删除 | +| :- | :- | +| 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 | 清除本应用所有的数据 | | getFolderSize | 获取文件夹大小 | | getCacheSize | 获取缓存文件大小 | @@ -303,7 +398,7 @@ DevUtils.openDebug(); * **点击工具类 ->** [ClickUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ClickUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | isFastDoubleClick | 判断两次点击的间隔 小于默认间隔时间(1秒), 则认为是多次无效点击 | | initConfig | 初始化配置信息 | | putConfig | 添加配置信息 | @@ -313,7 +408,7 @@ DevUtils.openDebug(); * **剪贴板相关工具类 ->** [ClipboardUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ClipboardUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | copyText | 复制文本到剪贴板 | | getText | 获取剪贴板的文本 | | copyUri | 复制uri到剪贴板 | @@ -325,7 +420,7 @@ DevUtils.openDebug(); * **ContentResolver 工具类 ->** [ContentResolverUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ContentResolverUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | notifyMediaStore | 通知刷新本地资源 | | insertImageIntoMediaStore | 添加图片到系统相册(包含原图、相册图, 会存在两张) - 想要一张, 直接调用 notifyMediaStore() | | insertVideoIntoMediaStore | 添加视频到系统相册 | @@ -335,14 +430,14 @@ DevUtils.openDebug(); * **获取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) | +| getMaxCpuFreq | 获取 CPU 最大频率(单位KHZ) | +| getMinCpuFreq | 获取 CPU 最小频率(单位KHZ) | +| getCurCpuFreq | 实时获取 CPU 当前频率(单位KHZ) | | getCoresNumbers | 获取 CPU 几核 | | getCpuName | 获取CPU名字 | | getCMDOutputString | 获取 CMD 指令回调数据 | @@ -351,7 +446,7 @@ DevUtils.openDebug(); * **数据库工具类 (导入导出等) ->** [DBUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/DBUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | startExportDatabase | 导出数据库 | | startImportDatabase | 导入数据库 | | getDBPath | 获取数据库路径 | @@ -360,42 +455,45 @@ DevUtils.openDebug(); * **设备相关工具类 ->** [DeviceUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/DeviceUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | getSysLanguage | 获取当前操作系统的语言 | | getDeviceInfo | 获取设备信息 | -| getDeviceInfo2 | 获取设备信息 | -| handleDeviceInfo | 处理设备信息 | +| handlerDeviceInfo | 处理设备信息 | | getSDKVersionName | 获取设备系统版本号 | | getSDKVersion | 获取当前SDK 版本号 | | getAndroidId | 获取Android id | | isDeviceRooted | 判断设备是否 root | +| isAdbEnabled | 返回是否启用了 ADB | +| getABIs | 获取支持的指令集 如: [arm64-v8a, armeabi-v7a, armeabi] | | getMacAddress | 获取设备 MAC 地址 | | getManufacturer | 获取设备厂商 如 Xiaomi | | getModel | 获取设备型号 如 RedmiNote4X | -| shutdown | 关机 需要 root 权限或者系统权限 | -| reboot | 重启 需要 root 权限或者系统权限 | -| reboot2Recovery | 重启到 recovery 需要 root 权限 | -| reboot2Bootloader | 重启到 bootloader 需要 root 权限 | -| getBaseband_Ver | BASEBAND-VER 基带版本 | -| getLinuxCore_Ver | CORE-VER 内核版本 | +| 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 | +| createAlertDialog | 创建提示 Dialog (原生样式) | +| createProgressDialog | 创建加载中 Dialog (原生样式) | +| autoCloseDialog | 自动关闭dialog | +| autoClosePopupWindow | 自动关闭 PopupWindow | * **EditText 工具类 ->** [EditTextUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/EditTextUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | setMaxLengthAnText | 设置长度限制,并且设置内容 | | setMaxLength | 设置长度限制 | | getSelectionStart | 获取光标位置 | @@ -409,11 +507,11 @@ DevUtils.openDebug(); | setKeyListener | 设置输入限制 | | getNumberAndEnglishKeyListener | 限制只能输入字母和数字,默认弹出英文输入法 | | getNumberKeyListener | 限制只能输入数字,默认弹出数字列表 | -| getMarkId | getMarkId | -| isOperate | isOperate | -| setOperate | setOperate | -| getOperateState | getOperateState | -| setOperateState | setOperateState | +| getMarkId | 获取标记id | +| isOperate | 判断是否操作中 | +| setOperate | 设置是否操作中 | +| getOperateState | 获取操作状态 | +| setOperateState | 设置操作状态 | | beforeTextChanged | 在文本变化前调用 | | onTextChanged | 在文本变化后调用 | | afterTextChanged | 在文本变化后调用 | @@ -422,21 +520,23 @@ DevUtils.openDebug(); * **编码工具类 ->** [EncodeUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/EncodeUtils.java) | 方法 | 注释 | -| :-: | :-: | -| urlEncode | url编码 - UTF-8 | +| :- | :- | +| urlEncode | url 编码 - UTF-8 | | urlDecode | url 解码 - UTF-8 | | base64Encode | base64 编码 => 去除\n 替换 + 和 - 号 | | base64Encode2String | base64 编码 | | base64Decode | base64 解码 | | base64DecodeToString | base64 解码 | -| htmlEncode | html编码 | -| htmlDecode | html解码 | +| htmlEncode | html 编码 | +| htmlDecode | html 解码 | +| binEncode | 返回二进制编码后的字符串 | +| binDecode | 返回 UTF-8 字符串的二进制数据 | * **错误信息处理工具类 ->** [ErrorUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ErrorUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | getErrorMsg | 获取错误信息 | | getThrowableMsg | 将异常栈信息转为字符串 | | getThrowableNewLinesMsg | 获取错误信息(有换行) | @@ -445,18 +545,18 @@ DevUtils.openDebug(); * **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 | 处理可变参数 | +| :- | :- | +| init | 初始化调用方法 | +| saveErrorLog | 保存 App 错误日志 | +| saveLog | 保存 App 日志 | +| handlerVariable | 处理可变参数 | * **手电筒工具类 ->** [FlashlightUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/FlashlightUtils.java) | 方法 | 注释 | -| :-: | :-: | -| getInstance | getInstance | +| :- | :- | +| getInstance | 获取 FlashlightUtils 实例 ,单例模式 | | register | 注册摄像头 | | unregister | 注销摄像头 | | setFlashlightOn | 打开闪光灯 | @@ -468,7 +568,7 @@ DevUtils.openDebug(); * **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 中执行任务 | @@ -478,7 +578,7 @@ DevUtils.openDebug(); * **Html 工具类 ->** [HtmlUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/HtmlUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | setHtmlText | 设置内容, 最终做处理 | | addHtmlColor | 为给定的字符串添加HTML颜色标记 | | addHtmlBold | 为给定的字符串添加HTML加粗标记 | @@ -492,8 +592,9 @@ DevUtils.openDebug(); * **Intent(意图) 相关工具类 ->** [IntentUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/IntentUtils.java) | 方法 | 注释 | -| :-: | :-: | -| getInstallAppIntent | 获取安装 App(支持 8.0)的意图 | +| :- | :- | +| isIntentAvailable | 判断 Intent 是否可用 | +| getInstallAppIntent | 获取安装 App(支持 8.0)的意图 | | getUninstallAppIntent | 获取卸载 App 的意图 | | getLaunchAppIntent | 获取打开 App 的意图 | | getLaunchAppDetailsSettingsIntent | 获取 App 具体设置的意图 | @@ -513,7 +614,7 @@ DevUtils.openDebug(); * **软键盘相关辅助类 ->** [KeyBoardUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/KeyBoardUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | setDelayMillis | 设置延迟时间 | | openKeyboard | 打开软键盘 | | closeKeyboard | 关闭软键盘 | @@ -524,31 +625,30 @@ DevUtils.openDebug(); | 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 | +| getKeyguardManager | 获取 KeyguardManager | +| setKeyguardManager | 设置 KeyguardManager | | disableKeyguard | 屏蔽系统的屏保 | | reenableKeyguard | 使能显示锁屏界面,如果你之前调用了disableKeyguard()方法取消锁屏界面,那么会马上显示锁屏界面。 | | release | 释放资源 | -| newKeyguardLock | newKeyguardLock | -| getKeyguardLock | getKeyguardLock | -| setKeyguardLock | setKeyguardLock | +| newKeyguardLock | 生成一个新的 KeyguardLock, 并且设置 Tag | +| getKeyguardLock | 获取 KeyguardManager.KeyguardLock | +| setKeyguardLock | 设置 KeyguardManager.KeyguardLock | * **事件工具类 => 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 对象内部事件对象 | @@ -558,13 +658,13 @@ DevUtils.openDebug(); * **定位相关工具类 ->** [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 | 注销 | +| register | 注册 - 使用完记得调用{@link #unregister()} | +| unregister | 注销监听 | | getAddress | 根据经纬度获取地理位置 | | getCountryName | 根据经纬度获取所在国家 | | getLocality | 根据经纬度获取所在地 | @@ -579,29 +679,34 @@ DevUtils.openDebug(); * **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版本信息 | +| :- | :- | +| getMetaData | 获取 Application Meta Data | +| getMetaDataInActivity | 获取 Activity Meta Data | +| getMetaDataInService | 获取 Service Meta Data | +| getMetaDataInReceiver | 获取 Receiver Meta Data | +| getMetaDataInProvider | 获取 ContentProvider 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) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | +| printMemInfo | 打印内存信息 | | getMemoryInfo | 获取内存信息 | -| printMemoryInfo | Print Memory info. | -| getAvailMemory | Get available memory info. | +| printMemoryInfo | 打印内存信息 | +| getAvailMemory | 获取可用内存信息 | | getTotalMemory | 获取总内存大小 | | getMemoryAvailable | 获取可用内存大小 | -| getMemInfoIype | 获取 type info | +| getMemInfoIype | 通过不同 type 获取对应的内存信息 | * **网络管理工具类 ->** [NetWorkUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/NetWorkUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | getMobileDataEnabled | 获取移动网络打开状态(默认属于未打开) | | setMobileDataEnabled | 设置移动网络开关(无判断是否已开启移动网络) - 实际无效果, 非系统应用无权限 | | isConnect | 判断是否连接了网络 | @@ -614,20 +719,25 @@ DevUtils.openDebug(); | isWifiAvailable | 判断wifi数据是否可用 | | getNetworkOperatorName | 获取网络运营商名称 - 中国移动、如中国联通、中国电信 | | getNetworkType | 获取当前网络类型 | +| 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/DevLibUtils/src/main/java/dev/utils/app/NotificationUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | getNotificationManager | 获取通知栏管理类 | | cancelAll | 移除通知 - 移除所有通知(只是针对当前Context下的Notification) | | cancel | 移除通知 - 移除标记为id的通知 (只是针对当前Context下的所有Notification) | | notify | 进行通知 | -| crePendingIntent | 获取跳转id | -| creNotification | 创建通知栏对象 | +| createPendingIntent | 获取跳转id | +| createNotification | 创建通知栏对象 | | obtain | 获取 Led 配置参数 | | isEmpty | 判断是否为null | @@ -635,53 +745,57 @@ DevUtils.openDebug(); * **工具类: 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 | +| getBaseVersion | 获取 Rom 版本, 如 MIUI 9 获取的是 9 | +| getVersion | 获取 Rom 具体版本号, 如 MIUI 9 8.4.26 获取的是 8.4.26 | * **路径相关工具类 ->** [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 | +| :- | :- | +| 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. | * **权限请求工具类 ->** [PermissionUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/PermissionUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | isGranted | 判断是否授予了权限 | | shouldShowRequestPermissionRationale | 是否拒绝了权限 - 拒绝过一次, 再次申请时, 弹出选择不再提醒并拒绝才会触发 true | | permission | 申请权限初始化 | @@ -690,18 +804,17 @@ DevUtils.openDebug(); | 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 | 判断设备是否是手机 | +| getMEID | 获取 MEID 移动设备识别码 | | getIMEI | 获取 IMEI 码 | | getIMSI | 获取 IMSI 码 | | getIMSIIDName | 获取IMSI处理过后的简称 | @@ -722,35 +835,34 @@ DevUtils.openDebug(); | 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 | +| getMtkTeleInfo | 获取 MTK 神机的双卡 IMSI、IMSI 信息 | +| getMtkTeleInfo2 | 获取 MTK 神机的双卡 IMSI、IMSI 信息 | +| getQualcommTeleInfo | 获取 高通 神机的双卡 IMSI、IMSI 信息 | +| getSpreadtrumTeleInfo | 获取 展讯 神机的双卡 IMSI、IMSI 信息 | * **轮询工具类 ->** [PollingUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/PollingUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | startPolling | 开启轮询 | | stopPolling | 停止轮询 | | startPollingService | 开启轮询服务 | -| stopPollingService | 停止启轮询服务 | +| 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 | +| getWakeLock | 获取 PowerManager.WakeLock | +| setWakeLock | 设置 PowerManager.WakeLock | +| getPowerManager | 获取 PowerManager | +| setPowerManager | 设置 PowerManager | | setBright | 设置屏幕常亮 | | setWakeLockToBright | 设置WakeLock 常亮 | @@ -758,9 +870,13 @@ DevUtils.openDebug(); * **进程相关工具类 ->** [ProcessUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ProcessUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | +| kill | 销毁自身进程 | +| isCurProcess | 判断是否当前进程 | +| getCurProcessName | 获取当前进程的名字 | | getProcessName | 获取进程号对应的进程名 | -| getCurProcessName | 获得当前进程的名字 | +| getPid | 根据包名获取进程id | +| getRunningAppProcessInfo | 根据 pid 获取进程信息 | | getForegroundProcessName | 获取前台线程包名 | | getAllBackgroundProcesses | 获取后台服务进程 | | killAllBackgroundProcesses | 杀死所有的后台服务进程 | @@ -770,27 +886,27 @@ DevUtils.openDebug(); * **资源文件工具类 ->** [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 | +| :- | :- | +| getString | 获取字符串 | +| getColor | 获取 Color | +| getDrawable | 获取 Drawable | +| getResourceId | 根据资源名获取资源id | +| getDrawableId2 | 获取 drawable id | +| getLayoutId | 获取 layout 布局文件 id | +| getStringId | 获取 string id | +| getDrawableId | 获取 drawable id | +| getMipmapId | 获取 mipmap id | +| getStyleId | 获取 style id | +| getStyleableId | 获取 styleable id | +| getAnimId | 获取 anim id | | getId | 获取 id | -| getColorId | color | +| getColorId | 获取 color id | | readBytesFromAssets | 获取 Assets 资源文件数据 | | readStringFromAssets | 读取字符串 来自 Assets文件 | | readBytesFromRaw | 从res/raw 中获取内容。 | | readStringFromRaw | 读取字符串 来自Raw 文件 | -| geFileToListFromAssets | 获取 Assets 资源文件数据(返回ArrayList 一行的全部内容属于一个索引) | -| geFileToListFromRaw | 从res/raw 中获取内容。(返回ArrayList 一行的全部内容属于一个索引) | +| geFileToListFromAssets | 获取 Assets 资源文件数据(返回List 一行的全部内容属于一个索引) | +| geFileToListFromRaw | 从res/raw 中获取内容。(返回List一行的全部内容属于一个索引) | | saveAssetsFormFile | 从Assets 资源中获取内容并保存到本地 | | saveRawFormFile | 从res/raw 中获取内容并保存到本地 | @@ -798,16 +914,16 @@ DevUtils.openDebug(); * **屏幕相关工具类 ->** [ScreenUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ScreenUtils.java) | 方法 | 注释 | -| :-: | :-: | -| getScreenWidth | 获取屏幕的宽度(单位:px) | -| getScreenHeight | 获取屏幕的高度(单位:px) | -| getScreenWidthHeightToPoint | 通过上下文获取屏幕宽度高度 | -| getScreenWidthHeight | 通过上下文获取屏幕宽度高度 | +| :- | :- | +| getScreenWidth | 获取屏幕的宽度(单位:px) | +| getScreenHeight | 获取屏幕的高度(单位:px) | +| getScreenWidthHeightToPoint | 通过 Context 获取屏幕宽度高度 | +| getScreenWidthHeight | 通过 Context 获取屏幕宽度高度 | | getScreenSize | 获取屏幕分辨率 | | getScreenSizeOfDevice | 获取屏幕英寸 例5.5英寸 | -| getDensity | 通过上下文获取屏幕密度 | -| getDensityDpi | 通过上下文获取屏幕密度Dpi | -| getScaledDensity | 通过上下文获取屏幕缩放密度 | +| getDensity | 通过 Context 获取屏幕密度 | +| getDensityDpi | 通过 Context 获取屏幕密度Dpi | +| getScaledDensity | 通过 Context 获取屏幕缩放密度 | | getXDpi | 获取 X轴 dpi | | getYDpi | 获取 Y轴 dpi | | getWidthDpi | 获取 宽度比例 dpi 基准 | @@ -821,11 +937,11 @@ DevUtils.openDebug(); | getScreenRotation | 获取屏幕旋转角度 | | isScreenLock | 判断是否锁屏 | | isTablet | 判断是否是平板 | -| getStatusHeight | 获得状态栏的高度(无关 android:theme 获取状态栏高度) | -| getStatusBarHeight | 获取应用区域 TitleBar 高度 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) | -| setSleepDuration | 设置进入休眠时长 - 需添加权限 | +| getStatusHeight | 获取状态栏的高度(无关 android:theme 获取状态栏高度) | +| getStatusBarHeight | 获取应用区域 TitleBar 高度 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) | +| setSleepDuration | 设置进入休眠时长 | | getSleepDuration | 获取进入休眠时长 | -| snapShotWithStatusBar | 获取当前屏幕截图,包含状态栏 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) | +| snapShotWithStatusBar | 获取当前屏幕截图,包含状态栏 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) | | snapShotWithoutStatusBar | 获取当前屏幕截图,不包含状态栏 (如果 android:theme 全屏了,则截图无状态栏) | | getNavigationBarHeight | 获取底部导航栏高度 | | checkDeviceHasNavigationBar | 检测是否具有底部导航栏 | @@ -834,20 +950,20 @@ DevUtils.openDebug(); * **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) | +| getSDCardFile | 获取SD卡路径(File对象) | +| getSDCardPath | 获取SD卡路径(无添加 -> / -> File.separator) | | isSDCardEnablePath | 判断 SD 卡是否可用 | | getSDCardPaths | 获取 SD 卡路径 | | getAllBlockSize | 返回对应路径的空间总大小 | | getAvailableBlocks | 返回对应路径的空闲空间(byte 字节大小) | | getAlreadyBlock | 返回对应路径,已使用的空间大小 | | getBlockSizeInfos | 返回对应路径的空间大小信息 | -| getSDTotalSize | 获得 SD 卡总大小 | -| getSDAvailableSize | 获得 SD 卡剩余容量,即可用大小 | -| getRomTotalSize | 获得机身内存总大小 | -| getRomAvailableSize | 获得机身可用内存 | +| getSDTotalSize | 获取 SD 卡总大小 | +| getSDAvailableSize | 获取 SD 卡剩余容量,即可用大小 | +| getRomTotalSize | 获取机身内存总大小 | +| getRomAvailableSize | 获取机身可用内存 | | getDiskCacheDir | 获取缓存地址 | | getCacheFile | 获取缓存资源地址 | | getCachePath | 获取缓存资源地址 | @@ -856,7 +972,7 @@ DevUtils.openDebug(); * **服务相关工具类 ->** [ServiceUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ServiceUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | isServiceRunning | 判断服务是否运行 | | getAllRunningService | 获取所有运行的服务 | | startService | 启动服务 | @@ -868,9 +984,9 @@ DevUtils.openDebug(); * **Shape 工具类 ->** [ShapeUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ShapeUtils.java) | 方法 | 注释 | -| :-: | :-: | -| getDrawable | getDrawable | -| setDrawable | setDrawable | +| :- | :- | +| getDrawable | 获取 GradientDrawable | +| setDrawable | 设置 Drawable 背景 | | newBuilder | 创建新的 Shape Builder 对象 | | newBuilderToLeft | 创建新的 Shape Builder 对象 | | newBuilderToRight | 创建新的 Shape Builder 对象 | @@ -888,57 +1004,94 @@ DevUtils.openDebug(); * **Shell 相关工具类 ->** [ShellUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ShellUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | execCmd | 是否是在 root 下执行命令 | +| isSuccess | 判断是否执行成功 | +| isSuccess2 | 判断是否执行成功(判断 errorMsg) | +| isSuccess3 | 判断是否执行成功(判断 successMsg) | +| isSuccess4 | 判断是否执行成功(判断 successMsg) , 并且 successMsg 是否包含某个字符串 | * **创建删除快捷图标工具类 ->** [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) +* **签名工具类(获取 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 | +| signatureMD5 | 返回 MD5 | +| signatureSHA1 | 返回 SHA1 加密字符串 | +| signatureSHA256 | 返回 SHA256 加密字符串 | | isDebuggable | 判断签名是debug签名还是release签名 | -| getX509Certificate | 获取App 证书对象 | +| getX509Certificate | 获取 App 证书对象 | | printSignatureName | 打印签名信息 | -| getSignaturesFromApk | 从APK中读取签名 | -| getCertificateFromApk | 从APK中读取签名 | +| 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(像素) | +| dipConvertPxf | 根据手机的分辨率从 dp 的单位 转成为 px(像素) - float | | pxConvertDip | 根据手机的分辨率从 px(像素) 的单位 转成为 dp | +| pxConvertDipf | 根据手机的分辨率从 px(像素) 的单位 转成为 dp - float | | pxConvertSp | 根据手机的分辨率从 px(像素) 的单位 转成为 sp | +| pxConvertSpf | 根据手机的分辨率从 px(像素) 的单位 转成为 sp - float | | spConvertPx | 根据手机的分辨率从 sp 的单位 转成为 px | +| spConvertPxf | 根据手机的分辨率从 sp 的单位 转成为 px - float | | dipConvertPx2 | 根据手机的分辨率从 dp 的单位 转成为 px(像素) 第二种 | +| dipConvertPx2f | 根据手机的分辨率从 dp 的单位 转成为 px(像素) 第二种 - float | | spConvertPx2 | 根据手机的分辨率从 sp 的单位 转成为 px 第二种 | +| spConvertPx2f | 根据手机的分辨率从 sp 的单位 转成为 px 第二种 - float | | applyDimension | 各种单位转换 - 该方法存在于 TypedValue | -| forceGetViewSize | 获取视图的尺寸 | +| forceGetViewSize | 在 onCreate 中获取视图的尺寸 - 需回调 onGetSizeListener 接口,在 onGetSize 中获取 view 宽高 | | measureView | 测量视图尺寸 | | getMeasuredWidth | 获取测量视图宽度 | | getMeasuredHeight | 获取测量视图高度 | -| onGetSize | onGetSize | +| onGetSize | 获取到 View 尺寸 通知 | + + +* **Snackbar 工具类 ->** [SnackbarUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 的下方 | * **颜色状态列表 工具类 ->** [StateListUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/StateListUtils.java) | 方法 | 注释 | -| :-: | :-: | -| getColorStateList | 通过上下文获取 | +| :- | :- | +| getColorStateList | 通过 Context 获取 ColorStateList | | createColorStateList | 创建 颜色状态列表 => createColorStateList("#ffffffff", "#ff44e6ff") | | newSelector | 创建 Drawable选择切换 list => view.setBackground(Drawable) | @@ -946,9 +1099,10 @@ DevUtils.openDebug(); * **TextView 工具类 ->** [TextViewUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/TextViewUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | getTextView | 获取TextView | | getText | 获取文本 | +| setBold | 设置是否加粗 | | setTextColor | 设置字体颜色 | | setText | 设置内容 | | setHtmlText | 设置 Html 内容 | @@ -964,24 +1118,25 @@ DevUtils.openDebug(); * **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. | +| getFilePathByUri | 通过 Uri 获取 文件路径 | * **震动相关工具类 ->** [VibrationUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/VibrationUtils.java) | 方法 | 注释 | -| :-: | :-: | +| :- | :- | | vibrate | 震动 | -| cancel | 取消振动 | +| cancel | 取消震动 | * **View 操作相关工具类 ->** [ViewUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/ViewUtils.java) | 方法 | 注释 | -| :-: | :-: | -| getContext | 获取上下文 | +| :- | :- | +| getContext | 获取 Context | | isEmpty | 判断View 是否为null | | isVisibility | 判断View 是否显示 | | isVisibilitys | 判断 View 是否都显示显示 | @@ -998,10 +1153,1622 @@ DevUtils.openDebug(); | removeSelfFromParent | 把自身从父View中移除 | | isTouchInView | 判断触点是否落在该View上 | | requestLayoutParent | View 改变请求 | -| measureView | 测量 view | +| measureView | 测量 View | | getViewWidth | 获取view的宽度 | | getViewHeight | 获取view的高度 | -| getActivity | 获取view的上下文 | +| getActivity | 获取 View 的 Context | | calcListViewItemHeight | 计算ListView Item 高度 | | calcGridViewItemHeight | 计算GridView Item 高度 | -| getItemHeighet | 获取单独一个Item 高度 | \ No newline at end of file +| getItemHeighet | 获取单独一个Item 高度 | + + +## **`dev.utils.app.anim`** + + +* **动画工具类 ->** [AnimationUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/anim/AnimationUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getRotateAnimation | 获取一个旋转动画 | +| getRotateAnimationByCenter | 获取一个根据视图自身中心点旋转的动画 | +| getAlphaAnimation | 获取一个透明度渐变动画 | +| getHiddenAlphaAnimation | 获取一个由完全显示变为不可见的透明度渐变动画 | +| getShowAlphaAnimation | 获取一个由不可见变为完全显示的透明度渐变动画 | +| getLessenScaleAnimation | 获取一个缩小动画 | +| getAmplificationAnimation | 获取一个放大动画 | +| translate | 视图移动 | +| shake | 视图摇晃 | + + +* **视图动画工具箱(AnimationUtils 基础上封装),提供简单的控制视图的动画的工具方法 ->** [ViewAnimationUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/src/main/java/dev/utils/app/assist/AsyncExecutor.java) + +| 方法 | 注释 | +| :- | :- | +| shutdownNow | 立即关闭线程池任务 | +| execute | 将任务投入线程池执行 | +| doInBackground | 后台运行 | +| onPostExecute | 将子线程结果传递到UI线程 | +| onCanceled | 取消任务 | +| abort | 中止任务 | + + +* **播放“bee”的声音, 并且震动 辅助类 ->** [BeepVibrateAssist.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java) + +| 方法 | 注释 | +| :- | :- | +| start | 开始计时任务 | +| onPause | 暂停检测 | +| onResume | 回到 Activity/xx 处理 | +| onDestroy | 页面销毁处理 | + + +* **屏幕传感器(监听是否横竖屏) ->** [ScreenSensorAssist.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java) + +| 方法 | 注释 | +| :- | :- | +| start | 开始监听 | +| stop | 停止监听 | +| isPortrait | 是否竖屏 | +| isAllowChange | 是否允许切屏 | + + +## **`dev.utils.app.assist.manager`** + + +* **应用程序Activity管理类:用于Activity管理和应用程序 ->** [ActivityManager.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/assist/manager/ActivityManager.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 ActivityManager 实例 ,单例模式 | +| getActivity | 通过 Context 获取Activity | +| isFinishing | 判断页面是否关闭 | +| isFinishingCtx | 判断页面是否关闭 | +| getActivityStacks | 获取 Activity 栈 | +| addActivity | 保存 Activity | +| removeActivity | 移除 Activity | +| currentActivity | 获取最后一个(当前)Activity | +| finishActivity | 结束最后一个(当前)Activity | +| finishAllActivityToIgnore | 结束全部Activity 除忽略的页面外 | +| finishAllActivity | 结束所有Activity | +| appExit | 退出应用程序 | +| restartApplication | 重启 App | + + +* **定时器工具类 ->** [TimerManager.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/assist/manager/TimerManager.java) + +| 方法 | 注释 | +| :- | :- | +| 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 | 设置触发次数上限 | + + +## **`dev.utils.app.cache`** + + +* **缓存工具类 ->** [DevCache.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/src/main/java/dev/utils/app/camera1/AutoFocusAssist.java) + +| 方法 | 注释 | +| :- | :- | +| setFocusModes | 设置对焦模式 | +| isAutoFocus | 是否允许自动对焦 | +| setAutoFocus | 设置是否开启自动对焦 | +| onAutoFocus | 对焦回调 {@link Camera.AutoFocusCallback} 重写方法 | +| start | 开始对焦 | +| stop | 停止对焦 | + + +* **摄像头辅助类 ->** [CameraAssist.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 | 是否支持手机闪光灯 | +| stopPreviewNotify | 停止预览通知 | +| startPreviewNotify | 开始预览通知 | + + +* **摄像头 预览、输出大小 辅助类 ->** [CameraSizeAssist.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/camera1/CameraSizeAssist.java) + +| 方法 | 注释 | +| :- | :- | +| getCamera | 获取摄像头 | +| setPreviewSize | 设置预览大小 | +| getPreviewSize | 根据手机支持的预览分辨率计算,设置预览尺寸 | +| setPictureSize | 设置拍照图片大小 | +| getPictureSize | 根据手机支持的拍照分辨率计算 | +| getVideoSize | 根据手机支持的视频录制分辨率计算 | + + +* **摄像头相关工具类 ->** [CameraUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/camera1/CameraUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isSupportReverse | 判断是否支持反转摄像头(是否存在前置摄像头) | +| checkCameraFacing | 检查是否有摄像头 | +| isFrontCamera | 判断是否使用前置摄像头 | +| isBackCamera | 判断是否使用后置摄像头 | +| isUseCameraFacing | 判断使用的视像头 | +| freeCameraResource | 释放摄像头资源 | +| initCamera | 初始化摄像头 | +| open | 打开摄像头 | + + +## **`dev.utils.app.image`** + + +* **图片裁剪工具类 ->** [BitmapCropUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/image/BitmapCropUtils.java) + +| 方法 | 注释 | +| :- | :- | +| cropBitmap | 裁剪图片(默认比例16:9) | + + +* **Bitmap工具类主要包括获取Bitmap和对Bitmap的操作 ->** [BitmapExtendUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/image/BitmapExtendUtils.java) + +| 方法 | 注释 | +| :- | :- | +| calculateInSampleSize | 图片压缩处理(使用Options的方法) | +| getBitmapFromResource | 获取一个指定大小的bitmap | +| getBitmapFromFile | 获取一个指定大小的bitmap | +| getBitmapFromByteArray | 获取一个指定大小的bitmap | +| getBytesFromStream | Stream 转换成 byte[] | +| getBitmapFromStream | 获取一个指定大小的bitmap | +| combineImages | 合并Bitmap | +| combineImagesToSameSize | 合并Bitmap | +| zoom | 放大缩小图片 | +| getRoundedCornerBitmap | 获取圆角图片的方法 | +| createReflectionBitmap | 获取带倒影的图片方法 | +| compressImage | 压缩图片大小 | +| convertGreyImg | 将彩色图转换为灰度图 | +| getRoundBitmap | 转换图片成圆形 | +| createThumbnailBitmap | 创建图片缩略图 | +| createWatermarkBitmap | 生成水印图片 水印在右下角 | +| codec | 重新编码Bitmap | +| compress | 图片压缩方法:(使用compress的方法) | +| scale | 图片的缩放方法 | +| rotate | 旋转图片 | +| reverseByHorizontal | 水平翻转处理 | +| reverseByVertical | 垂直翻转处理 | +| adjustTone | 更改图片色系,变亮或变暗 | +| convertToBlackWhite | 将彩色图转换为黑白图 | +| getImageDegree | 读取图片属性 图片被旋转的角度 | +| saturation | 饱和度处理 | +| lum | 亮度处理 | +| hue | 色相处理 | +| lumAndHueAndSaturation | 亮度、色相、饱和度处理 | +| nostalgic | 怀旧效果处理 | +| soften | 柔化效果处理 | +| sunshine | 光照效果处理 | +| film | 底片效果处理 | +| sharpen | 锐化效果处理 | +| emboss | 浮雕效果处理 | +| yuvLandscapeToPortrait | 将YUV格式的图片的源数据从横屏模式转为竖屏模式,注意:将源图片的宽高互换一下就是新图片的宽高 | +| safeDecodeStream | 比较安全的 解码(decodeStream) 方法 | + + +* **Bitmap工具类 ->** [BitmapUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/image/BitmapUtils.java) + +| 方法 | 注释 | +| :- | :- | +| intToDrawable | 将10进制颜色(Int)转换为Drawable对象 | +| stringToDrawable | 将16进制颜色(String)转化为Drawable对象 | +| tintIcon | 图片着色 | +| tint9PatchDrawableFrame | .9 图片着色 | +| setBackground | 设置背景 | +| getDrawable | 获取 Drawable | +| getBitmapFromResources | 通过Resources获取Bitmap | +| getDrawableFromResources | 通过Resources获取Drawable | +| getSDCardBitmapStream | 获取本地SDCard 图片 | +| getSDCardBitmapFile | 获取本地SDCard 图片 | +| getBitmap | 获取Bitmap | +| bitmapToByte | Bitmay 转换成byte数组 | +| drawableToByte | Drawable 转换成 byte数组 | +| drawableToByte2 | Drawable 转换成 byte数组 | +| byteToBitmap | byte 数组转换为Bitmap | +| drawableToBitmap | Drawable 转换成 Bitmap | +| bitmapToDrawable | Bitmap 转换成 Drawable | +| byteToDrawable | byte数组转换成Drawable | +| drawable2Bitmap | Drawable 转换 Bitmap | +| drawable3Bitmap | Drawable 转换 Bitmap | +| saveBitmapToSDCardJPEG | 保存图片到SD卡 - JPEG | +| saveBitmapToSDCardPNG | 保存图片到SD卡 - PNG | +| saveBitmapToSDCard | 保存图片到SD卡 - PNG | +| getBitmapFromDrawable | 将Drawable转化为Bitmap | +| bitmapToViewBackGround | 通过View, 获取背景转换Bitmap | +| getBitmapFromView | 通过View, 获取Bitmap -> 绘制整个View | +| getBitmapFromView2 | 把一个View的对象转换成bitmap | +| reckonVideoWidthHeight | 计算视频宽高大小,视频比例xxx*xxx按屏幕比例放大或者缩小 | +| caculateInSampleSize | 根据需求的宽和高以及图片实际的宽和高计算SampleSize | +| getImageViewSize | 根据ImageView获适当的压缩的宽和高 | +| getImageWidthHeight | 获取图片宽度高度(不加载解析图片) | + + +* **毛玻璃效果 ->** [FastBlurUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/image/FastBlurUtils.java) + +| 方法 | 注释 | +| :- | :- | +| blur | 对图片进行毛玻璃化 | + + +* **图片Buf转换 - 转换BMP图片 ->** [ImageBmpUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/image/ImageBmpUtils.java) + +| 方法 | 注释 | +| :- | :- | +| saveBmpImg | 保存Bmp图片 | + + +* **图片处理器 ->** [ImageProcessor.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/image/ImageProcessor.java) + +| 方法 | 注释 | +| :- | :- | +| scale | 缩放处理 | +| scaleByWidth | 缩放处理 | +| scaleByHeight | 缩放处理 | +| reverseByHorizontal | 水平翻转处理 | +| reverseByVertical | 垂直翻转处理 | +| drawableToBitmap | 将给定资源ID的Drawable转换成Bitmap | +| roundCorner | 圆角处理 | +| reflection | 倒影处理 | +| rotate | 旋转处理 | +| saturation | 饱和度处理 | +| lum | 亮度处理 | +| hue | 色相处理 | +| lumAndHueAndSaturation | 亮度、色相、饱和度处理 | +| nostalgic | 怀旧效果处理 | +| blur | 模糊效果处理 | +| soften | 柔化效果处理 | +| sunshine | 光照效果处理 | +| film | 底片效果处理 | +| sharpen | 锐化效果处理 | +| emboss | 浮雕效果处理 | + + +* **图片相关工具类 ->** [ImageUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/image/ImageUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getBitmap | 获取 bitmap | +| scale | 缩放图片 | +| clip | 裁剪图片 | +| skew | 倾斜图片 | +| rotate | 旋转图片 | +| getRotateDegree | 获取图片旋转角度 - 返回 -1 表示异常 | +| toRound | 转为圆形图片 | +| toRoundCorner | 转为圆角图片 | +| addCornerBorder | 添加圆角边框 | +| addCircleBorder | 添加圆形边框 | +| addReflection | 添加倒影 | +| addTextWatermark | 添加文字水印 | +| addImageWatermark | 添加图片水印 | +| toAlpha | 转为 alpha 位图 | +| toGray | 转为灰度图片 | +| fastBlur | 快速模糊 - 先缩小原图,对小图进行模糊,再放大回原先尺寸 | +| renderScriptBlur | renderScript 模糊图片 - API 大于 17 | +| stackBlur | stack 模糊图片 | +| save | 保存图片 | +| isImage | 根据文件名判断文件是否为图片 | +| getImageType | 获取图片类型 | +| compressByScale | 按缩放压缩 | +| compressByQuality | 按质量压缩 | +| compressBySampleSize | 按采样大小压缩 | + + +* **Android 自己的 RenderScript 实现图片模糊 ->** [RSBlurUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/image/RSBlurUtils.java) + +| 方法 | 注释 | +| :- | :- | +| blur | RenderScript 实现图片模糊 | + + +## **`dev.utils.app.info`** + + +* **App 信息实体类 ->** [AppInfoBean.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 信息获取工具类 ->** [AppInfoUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 列表 | +| getApkPermission | 获取 Apk 注册的权限 | +| printApkPermission | 打印 Apk 注册的权限 | + + +## **`dev.utils.app.logger`** + + +* **日志操作类(对外公开直接调用) ->** [DevLogger.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/logger/DevLogger.java) + +| 方法 | 注释 | +| :- | :- | +| other | 使用单次其他日志配置 | +| getLogConfig | 获取日志配置信息 | +| init | 初始化日志配置信息(可以不调用,使用了 App 默认配置) | +| 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/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化调用方法(获取版本号) | +| getReleaseLogConfig | 获取发布 Log 配置(打印线程信息,显示方法总数3,从0开始,不进行排序, 默认属于ERROR级别日志) | +| getDebugLogConfig | 获取调试 Log 配置(打印线程信息,显示方法总数3,从0开始,不进行排序, 默认属于ERROR级别日志) | +| getSortLogConfig | 获取 Log 配置(打印线程信息,显示方法总数3,从0开始,并且美化日志信息, 默认属于DEBUG级别日志) | +| getLogConfig | 获取 Log 配置 | +| saveErrorLog | 保存 App 错误日志 | +| saveLog | 保存 App 日志 | +| saveLogHeadBottom | 保存 App 日志 - 包含头部、底部信息 | + + +## **`dev.utils.app.player`** + + +* **MediaPlayer 统一管理类 ->** [DevMediaManager.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 | 是否播放中(判断null) | +| pause | 暂停操作(判断null) | +| stop | 停止操作(判断null) - 销毁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 | 设置播放配置 uri等 | + + +* **视频播放控制器 ->** [DevVideoPlayerControl.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 | 获取播放进度百分比 | + + +## **`dev.utils.app.share`** + + +* **SPUtils 工具类 - 直接单独使用 ->** [SharedUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/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/DevLibUtils/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/DevLibUtils/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/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiHotUtils.java) + +| 方法 | 注释 | +| :- | :- | +| createWifiConfigToAp | 创建Wifi配置信息(无其他操作,单独返回WifiConfig) => Wifi热点 (就支持 无密码/WPA2 PSK) | +| stratWifiAp | 开启Wifi热点 | +| closeWifiAp | 关闭Wifi热点 | +| getWifiApState | 获取Wifi热点状态 | +| getWifiApConfiguration | 获取Wifi热点配置信息 | +| setWifiApConfiguration | 设置Wifi热点配置信息 | +| isOpenWifiAp | 判断是否打开Wifi热点 | +| closeWifiApCheck | 关闭Wifi热点(判断当前状态) | +| isConnectHot | 是否有连接热点 | +| getHotspotServiceIp | 获取热点主机ip地址 | +| getHotspotAllotIp | 获取连接上的子网关热点IP(一个) | +| getHotspotSplitIpMask | 获取热点拼接后的ip网关掩码 | +| getApWifiSSID | 获取Wifi 热点名 | +| getApWifiPwd | 获取Wifi 热点密码 | +| setOnWifiAPListener | 设置 Wifi 热点监听 | +| onStarted | 开启热点触发 | +| onStopped | 关闭热点回调 | +| onFailed | 失败回调 | + + +* **wifi工具类 ->** [WifiUtils.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 | 获取网络列表 | +| getWifiInfo | 获取WifiInfo对象 | +| getMacAddress | 获取MAC地址 | +| getBSSID | 获取接入点的BSSID | +| getIPAddress | 获取IP地址 | +| getNetworkId | 获取连接的ID | +| getSSID | 获取SSID | +| formatSSID | 判断是否存在\"ssid\",存在则裁剪返回 | +| getPassword | 获取密码(经过处理) | +| isHexWepKey | 判断是否 wep 加密 | +| getWifiType | 获取加密类型(int常量) - 判断String | +| getWifiTypeInt | 获取加密类型(int常量) - 判断int(String) | +| getWifiTypeStr | 获取加密类型(String) | +| isConnNull | 判断是否连接为null - unknown ssid | +| isConnectAphot | 判断是否连接上Wifi(非连接中) | +| getSecurity | 获取Wifi配置,加密类型 | +| isExsitsPwd | 获知Wifi配置,是否属于密码加密类型 | +| isExsits | 查看以前是否也配置过这个网络 | +| delWifiConfig | 删除指定的 Wifi(SSID) 配置信息 | +| quickConnWifi | 快速连接Wifi(不使用静态ip方式) | +| createWifiConfig | 创建Wifi配置信息(无其他操作,单独返回WifiConfig) | +| removeWifiConfig | 移除某个Wifi配置信息 | +| disconnectWifi | 断开指定ID的网络 | + + +## **`dev.utils.common`** + + +* **快捷辅助工具类 ->** [AssistUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/AssistUtils.java) + +| 方法 | 注释 | +| :- | :- | +| randomUUID | 获取随机唯一数 | +| randomUUIDToHashCode | 获取随机唯一数 HashCode | +| getRandomUUID | 获取随机数 唯一id | +| whileMD5 | 循环MD5 加密处理 | + + +* **资金运算工具类 ->** [BigDecimalUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/BigDecimalUtils.java) + +| 方法 | 注释 | +| :- | :- | +| add | 提供精确的加法运算 | +| substract | 提供精确的减法运算 | +| multiply | 提供精确的乘法运算 | +| divide | 提供(相对)精确的除法运算,当发生除不尽的情况时, | +| round | 提供精确的小数位四舍五入处理 | +| remainder | 取余数 | +| formatMoney | 金额分割,四舍五入金额 | +| formatMoney1 | 四舍五入金额 | +| compareBigDecimal | 比较大小 | +| adjustDouble | 获取自己想要的数据格式 | + + +* **字节工具类,提供一些有关字节的便捷方法 ->** [ByteUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/ByteUtils.java) + +| 方法 | 注释 | +| :- | :- | +| byteJiaMi | (01)、位移加密、解密,调同一个方法 | +| hexStrToByteArray | 字符串转数组 | +| cutOut | (02)、从bytes上截取一段 | +| byteToBit | 将字节转换为二进制字符串 | +| bits2Bytes | 二进制字符串, 转换成byte数组 | +| getHex | 字节数组转换成16进制字符串 | +| subBytes | 从一个byte[]数组中截取一部分 | +| byteToObject | byte[] 转为 对象 | +| objectToByte | 对象 转为 byte[] | + + +* **类工具 ->** [ClassUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/ClassUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isBaseDataType | 判断类是否是基础数据类型 | +| newInstance | 根据类获取对象:不再必须一个无参构造 | +| getDefaultPrimiticeValue | 判断 Class 是否为原始类型(boolean、char、byte、short、int、long、float、double) | +| isCollection | 判断是否集合类型 | +| isArray | 是否数组类型 | + + +* **克隆相关工具类 ->** [CloneUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/CloneUtils.java) + +| 方法 | 注释 | +| :- | :- | +| deepClone | 进行克隆 | + + +* **关闭工具类 - (关闭IO流等) ->** [CloseUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/CloseUtils.java) + +| 方法 | 注释 | +| :- | :- | +| closeIO | 关闭 IO | +| closeIOQuietly | 安静关闭 IO | + + +* **颜色工具类 包括常用的色值 ->** [ColorUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/ColorUtils.java) + +| 方法 | 注释 | +| :- | :- | +| percent | 计算百分比值 | +| clamp | 返回的 value 介于 max、min之间,若 value 小于min,返回min,若大于max,返回max | +| alpha | 返回一个颜色中的透明度值(返回10进制) | +| alphaPercent | 返回一个颜色中的透明度百分比值 | +| red | 返回一个颜色中红色的色值(返回10进制) | +| redPercent | 返回一个颜色中红色的百分比值 | +| green | 返回一个颜色中绿色的色值(返回10进制) | +| greenPercent | 返回一个颜色中绿色的百分比值 | +| blue | 返回一个颜色中蓝色的色值(返回10进制) | +| bluePercent | 返回一个颜色中蓝色的百分比值 | +| rgb | 根据对应的 red、green、blue 生成一个颜色值 | +| argb | 根据对应的 alpha, red、green、blue 生成一个颜色值 (含透明度) | +| isRGB | 判断颜色 RGB 是否有效 | +| isARGB | 判断颜色 ARGB 是否有效 | +| setAlpha | 设置透明度 | +| setRed | 改变颜色值中的红色色值 | +| setGreen | 改变颜色值中的绿色色值 | +| setBlue | 改变颜色值中的蓝色色值 | +| parseColor | 解析颜色字符串, 返回对应的颜色值 | +| int2RgbString | 颜色值 转换 RGB颜色 字符串 | +| int2ArgbString | 颜色值 转换 ARGB颜色 字符串 | +| getRandomColor | 获取随机颜色值 | +| judgeColorString | 判断是否为ARGB格式的十六进制颜色,例如:FF990587 | +| setDark | 颜色加深(单独修改 RGB值, 不变动透明度) | +| setLight | 颜色变浅, 变亮(单独修改 RGB值, 不变动透明度) | +| setAlphaDark | 设置透明度加深 | +| setAlphaLight | 设置透明度变浅 | + + +* **转换工具类 ->** [ConverUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/ConverUtils.java) + +| 方法 | 注释 | +| :- | :- | +| toString | char 数组 转 String | +| toInt | 字符串 转 int | +| toBoolean | 字符串 转 boolean | +| toFloat | 字符串 转 float | +| toDouble | 字符串 转 double | +| toLong | 字符串 转 long | +| toChar | 字符串 获取 char(默认第一位) | +| toCharInt | char 转换 unicode 编码 | +| toCharArys | 字符串 获取 char数组 | +| toByteArys | 字符串 获取 byte数组 | +| toHexString | 一个 int 参数的字符串表示形式在基数为16的无符号 int | +| parseInt | 字符串转换对应的进制 | +| hexString2Bytes | 十六进制字符串 转换byte数组 | +| bytes2Bits | 把 bytes 数据, 转换成二进制数据 | +| bits2Bytes | 二进制字符串, 转换成byte数组 | +| bytes2Chars | byte 数组 转换 char 数组, 并且进行补码 | +| chars2Bytes | char 数组 转换 byte 数组 | + + +* **坐标相关工具类 - GPS 纠偏 ->** [CoordinateUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 坐标 | + + +* **日期工具类 ->** [DateUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 格式 | +| getTimeDiffMinute | 获取时间差 - 分钟 | +| getTimeDiffHour | 获取时间差 - 小时 | +| getTimeDiffDay | 获取时间差 - 天 | +| getTimeDiff | 获取时间差 - (传入时间 - 当前时间) | +| getYear | 获取年 | +| getMonth | 获取月 (0 - 11) + 1 | +| getDay | 获取日 | +| getWeek | 获取日期是星期几 | +| get24Hour | 获取时 - 24 | +| get12Hour | 获取时 - 12 | +| getMinute | 获取分 | +| getSecond | 获取秒 | +| convertTime | 转换时间处理, 小于10, 则自动补充 0x | +| isLeapYear | 判断是否闰年 | +| getMonthDayNumberAll | 根据年份、月份,获取对应的天数 (完整天数, 无判断是否属于未来日期) | +| getYearMonthNumber | 根据年份,获取对应的月份 | +| getMonthDayNumber | 根据年份、月份,获取对应的天数 | +| secToTimeRetain | 传入时间,获取时间(00:00:00 格式) - 不处理大于一天 | +| convertTimeArys | 传入时间,时间参数(小时、分钟、秒) | +| millis2FitTimeSpan | 转换时间 | +| isInTimeHHmm | 判断时间是否在[startTime, endTime]区间,注意时间格式要一致 | +| isInTimeHHmmss | 判断时间是否在[startTime, endTime]区间,注意时间格式要一致 | +| isInTime | 判断时间是否在[startTime, endTime]区间,注意时间格式要一致 | +| isInDate | 判断时间是否在[startTime, endTime]区间,注意时间格式要一致 | +| getEndTimeDiffHHmm | 获取指定时间距离该时间第二天的指定时段的时间 (判断凌晨情况) | +| getEndTimeDiff | 获取指定时间距离该时间第二天的指定时段的时间差 (判断凌晨情况) | + + +* **开发常用方法 - 工具类 ->** [DevCommonUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/DevCommonUtils.java) + +| 方法 | 注释 | +| :- | :- | +| percent | 计算百分比值 (最大 100%) | +| percent2 | 计算百分比值 (可超出 100%) | +| clamp | 返回的 value 介于 max、min之间,若 value 小于min,返回min,若大于max,返回max | +| getFormatString | 获取格式化字符串 | +| getFormatString2 | 获取格式化字符串 | +| isHttpRes | 判断是否网络资源 | +| isSpace | 判断字符串是否为 null 或全为空白字符 | +| isEmpty | 判断是否为null | +| isTrimEmpty | 判断字符串是否为 null 或全为空格 | +| isEmptyObjs | 判读是否为null to 可变数组 | +| length | 获取长度,如果字符串为null,则返回0 | +| lengthObjs | 获取可变数组长度 | +| lengthObjsDf | 获取可变数组长度 | +| isLength | 字符串长度匹配 | +| equals | 判断两字符串是否相等 | +| isEquals | 判断多个字符串是否相等, 只有全相等才返回true - 对比大小写 | +| isOrEquals | 判断多个字符串,只要有一个符合条件,则通过 | +| isContains | 判断一堆值中,是否存在符合该条件的(包含) | +| isStartsWith | 判断内容, 是否属于特定字符串数组开头 - 对比大小写 | +| isEndsWith | 判断内容, 是否属于特定字符串数组结尾 - 对比大小写 | +| toClearSpace | 清空全部空格,并返回处理后的字符串 | +| toClearSpaceTrim | 清空前后空格,并返回处理后的字符串 | +| toCheckValue | 检查字符串,如果为null,返回 "" | +| toCheckValues | 检查多个值,并返回第一个非null and "" 的字符串,如果都不符合条件,则返回默认值 | +| toCheckValuesSpace | 检查多个值,并返回第一个非null and "" and 全部不是属于空格 的字符串,如果都不符合条件,则返回默认值 | +| subEllipsize | 裁减超出的内容, 并且追加符号(如 ...) | +| subSymbolHide | 裁剪符号处理 | +| converSymbolHide | 转换符号处理 | +| toReplaceSEWith | 替换(删除 - 替换成"") 字符串中符合 特定标记字符的 startsWith - endsWith | +| toClearSEWiths | (这个方法功能主要把字符符合标记的 头部和尾部都替换成 "") | +| substring | 裁剪字符串 | +| toReplaceStartsWith | 替换开头字符串 | +| toClearStartsWith | 清空属于特定字符串开头的字段 | +| toReplaceEndsWith | 替换结尾字符串 | +| toClearEndsWith | 清空属于特定字符串结尾的字段 | +| replaceStrs | 替换字符串 | +| replaceStr | 替换字符串 | +| replaceStrToNull | 替换字符串 | + + +* **域工具 ->** [FieldUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/FieldUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isSerializable | 判断是否序列化 | +| set | 设置域的值 | +| get | 获取域的值 | +| isLong | 是否 long 类型 | +| isInteger | 是否 Integer 类型 | +| getGenericType | 获取域的泛型类型,如果不带泛型返回null | +| getComponentType | 获取数组的类型 | +| getAllDeclaredFields | 获取全部Field,包括父类 | +| isInvalid | 是静态常量或者内部结构属性 | + + +* **文件IO流工具类 ->** [FileIOUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/FileIOUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setBufferSize | Set the buffer's size. - Default size equals 8192 bytes. | +| writeFileFromIS | Write file from input stream. | +| writeFileFromBytesByStream | Write file from bytes by stream. | +| writeFileFromBytesByChannel | Write file from bytes by channel. | +| writeFileFromBytesByMap | Write file from bytes by map. | +| writeFileFromString | Write file from string. | +| readFile2List | Return the lines in file. | +| readFile2String | Return the string in file. | +| readFile2BytesByStream | Return the bytes in file by stream. | +| readFile2BytesByChannel | Return the bytes in file by channel. | +| readFile2BytesByMap | Return the bytes in file by map. | + + +* **文件操作工具类 ->** [FileUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/FileUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getFile | 获取文件 - to getFileByPath | +| getFileByPath | 获取文件 | +| getFileCreateFolder | 获取路径, 并且进行创建目录 | +| createFolder | 判断某个文件夹是否创建,未创建则创建(纯路径 - 无文件名) | +| createFolderByPath | 创建文件夹目录 - 可以传入文件名 | +| createFolderByPaths | 创建多个文件夹, 如果不存在则创建 | +| createOrExistsDir | 判断目录是否存在,不存在则判断是否创建成功 | +| createOrExistsFile | 判断文件是否存在,不存在则判断是否创建成功 | +| createFileByDeleteOldFile | 判断文件是否存在,存在则在创建之前删除 | +| getPath | 获取文件路径 | +| getAbsolutePath | 获取文件绝对路径 | +| getName | 获取文件名 | +| getFileSuffix | 获取文件后缀名(无.,单独后缀) | +| getFileNotSuffix | 获取文件名(无后缀) | +| getFileNotSuffixToPath | 获取文件名(无后缀) | +| getFileNameNoExtension | 获取全路径中的不带拓展名的文件名 | +| getFileExtension | 获取全路径中的文件拓展名 | +| isFileExists | 检查是否存在某个文件 | +| isFile | 判断是否文件 | +| isDir | 判断是否文件夹 | +| isHide | 判断是否隐藏文件 | +| getFileLastModified | 获取文件最后修改的毫秒时间戳 | +| getFileCharsetSimple | 简单获取文件编码格式 | +| getFileLines | 获取文件行数 | +| getFileSize | 获取文件大小 | +| getDirSize | 获取目录大小 | +| getFileLength | 获取文件大小 | +| getDirLength | 获取目录长度 | +| getFileLengthNetwork | 获取文件长度 - 网络资源 | +| getFileName | 获取全路径中的文件名 | +| getDirName | 获取全路径中的最长目录 | +| rename | 重命名文件 - 同个目录下, 修改文件名 | +| formatFileSize | 传入文件路径, 返回对应的文件大小 | +| formatByteMemorySize | 字节数转合适内存大小 保留 3 位小数 (%.位数f) | +| getFileMD5ToString | 获取文件的 MD5 校验码 | +| getFileMD5 | 获取文件的 MD5 校验码 | +| getFileMD5ToString2 | 获取文件MD5值 - 小写 | +| deleteFile | 删除文件 | +| deleteFiles | 删除多个文件 | +| deleteFolder | 删除文件夹 | +| saveFile | 保存文件 | +| appendFile | 追加文件:使用FileWriter | +| readFileBytes | 读取文件 | +| readFile | 读取文件 | +| copyFile | 复制单个文件 | +| copyFolder | 复制文件夹 | +| moveFile | 移动(剪切)文件 | +| moveFolder | 移动(剪切)文件夹 | +| copyDir | 复制目录 | +| moveDir | 移动目录 | +| deleteDir | 删除目录 | +| deleteAllInDir | 删除目录下所有东西 | +| deleteFilesInDir | 删除目录下所有文件 | +| deleteFilesInDirWithFilter | 删除目录下所有过滤的文件 | +| listFilesInDir | 获取目录下所有文件 - 不递归进子目录 | +| listFilesInDirWithFilter | 获取目录下所有过滤的文件 - 不递归进子目录 | +| onReplace | 是否覆盖/替换文件 | + + +* **十六进制处理 ->** [HexUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/HexUtils.java) + +| 方法 | 注释 | +| :- | :- | +| encodeHex | 将字节数组转换为十六进制字符数组 | +| encodeHexStr | 将字节数组转换为十六进制字符串 | +| decodeHex | 将十六进制字符数组转换为字节数组 | + + +* **Http 参数工具类 ->** [HttpParamsUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/HttpParamsUtils.java) + +| 方法 | 注释 | +| :- | :- | +| splitParams | 拆分参数 | +| joinReqParams | 拼接请求参数 - value => String | +| joinReqParamsObj | 拼接请求参数 - value => Object | +| toStringMap | toString 快捷方法, 拼接打印 String | +| toConvertObjToMS | 进行转换对象处理(请求发送对象) | +| toConvertObjToMO | 进行转换对象处理(请求发送对象) | +| urlEncode | url编码 - utf-8 | + + +* **HttpURLConnection 网络工具类 ->** [HttpURLConnectionUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/HttpURLConnectionUtils.java) + +| 方法 | 注释 | +| :- | :- | +| doGetAsyn | 异步的Get请求 | +| doPostAsyn | 异步的Post请求 | +| request | 发送请求 | +| getNetTime | 获取网络时间 - 默认使用百度链接 | +| onResponse | 请求响应回调 | +| onFail | 请求失败 | + + +* **对象相关工具类 ->** [ObjectUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/ObjectUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断对象是否为空 | +| isNotEmpty | 判断对象是否非空 | +| equals | 判断对象是否相等 | +| requireNonNull | 检查对象非空 | +| getOrDefault | 获取非空或默认对象 | +| hashCode | 获取对象哈希值 | +| getObjectTag | 获取一个对象的独一无二的标记 | +| converObj | 获取转换对象 | + + +* **快捷通用 ->** [QuickCommonUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/QuickCommonUtils.java) + +| 方法 | 注释 | +| :- | :- | +| converHideMobile | 转换手机号 | +| timeRecord | 耗时时间记录 | +| getFormatString | 获取格式化字符串(可变参数) | +| getFormatString2 | 获取格式化字符串(可变参数) | +| getOperateTime | 获取操作时间 | +| sleepOperate | 堵塞操作 | + + +* **随机生成工具类 ->** [RandomUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/src/main/java/dev/utils/common/Reflect2Utils.java) + +| 方法 | 注释 | +| :- | :- | +| getProperty | 获取某个对象的公共属性 | +| getStaticProperty | 获取某类的静态公共属性 | +| invokeMethod | 执行某对象方法 | +| invokeStaticMethod | 执行某类的静态方法 | +| newInstance | 新建实例 | +| isInstance | 是不是某个类的实例 | +| getByArray | 获取数组中的某个元素 | +| getDeclaredField | 通过反射获取全部字段 | +| getDeclaredFieldParentObj | 获取父类中的变量对象 | +| getDeclaredFieldParent | 循环向上转型, 获取对象的 DeclaredField | +| setFieldMethod | 设置反射的方法 | +| setFieldValue | 设置反射的字段 | + + +* **反射相关工具类 ->** [ReflectUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/ReflectUtils.java) + +| 方法 | 注释 | +| :- | :- | +| reflect | 设置要反射的类 | +| newInstance | 实例化反射对象 | +| field | 设置反射的字段 | +| getObject | 获取Object 对象 | +| setEnumVal | 设置枚举值 | +| getDeclaredField | 通过反射获取全部字段 | +| getDeclaredFieldBase | 循环向上转型, 获取对象的 DeclaredField | +| method | 设置反射的方法 | +| proxy | 根据类, 代理创建并返回对象 | +| type | 获取类型 | +| get | 获取反射想要获取的 | +| hashCode | 获取 HashCode | +| equals | 判断反射的两个对象是否一样 | +| toString | 获取反射获取的对象 toString | + + +* **计算比例方法 ->** [ScaleUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/ScaleUtils.java) + +| 方法 | 注释 | +| :- | :- | +| calcScaleToWidth | 计算缩放比例 - 根据宽度比例转换高度 | +| calcScaleToHeight | 计算缩放比例 - 根据高度比例转换宽度 | +| calcWidthHeightToScale | 通过宽度,高度,根据对应的比例 -> 转换成对应的比例宽度高度 - 智能转换 | +| calcWidthToScale | 以宽度为基准 -> 转换对应比例的高度 | +| calcHeightToScale | 以高度为基准 -> 转换对应比例的宽度 | + + +* **单例工具类 ->** [SingletonUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/SingletonUtils.java) + +| 方法 | 注释 | +| :- | :- | +| newInstance | 实现实例抽象方法 | +| getInstance | 获取实例方法 | + + +* **流操作工具类 ->** [StreamUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/StreamUtils.java) + +| 方法 | 注释 | +| :- | :- | +| input2OutputStream | Input stream to output stream. | +| output2InputStream | Output stream to input stream. | +| inputStream2Bytes | Input stream to bytes. | +| bytes2InputStream | Bytes to input stream. | +| outputStream2Bytes | Output stream to bytes. | +| bytes2OutputStream | Bytes to output stream. | +| inputStream2String | Input stream to string. | +| string2InputStream | String to input stream. | +| outputStream2String | Output stream to string. | +| string2OutputStream | String to output stream. | + + +* **字符串工具类 ->** [StringUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/StringUtils.java) + +| 方法 | 注释 | +| :- | :- | +| length | 获取长度,如果字符串为null,则返回0 | +| isEmpty | 判断是否为null | +| toClearSpace | 清空全部空格,并返回处理后的字符串 | +| toClearSpaceTrim | 清空前后空格,并返回处理后的字符串 | +| toGBKEncode | 字符串进行 GBK 编码 | +| toGBK2312Encode | 字符串进行 GBK2312 编码 | +| toUTF8Encode | 字符串进行 UTF-8 编码 | +| toStrEncode | 进行字符串编码 | +| toUrlEncode | 进行 URL 编码,默认UTF-8 | +| toASCII | 将字符串转移为ASCII码 | +| toUnicode | 将字符串转移为Unicode码 | +| toUnicodeString | 将字符串转移为Unicode码 | +| toDBC | 转化为半角字符 | +| toSBC | 转化为全角字符 如: a => a A => A | +| byteArrayToHexString | byte[]数组转换为16进制的字符串 | +| toHexString | 进行转换 | +| hexStringToByteArray | 16进制表示的字符串转换为字节数组 | +| checkCheseToString | 检测String是否全是中文 | +| isChinese | 判定输入汉字 | +| upperFirstLetter | 首字母大写 | +| lowerFirstLetter | 首字母小写 | +| reverse | 反转字符串 | +| concat | 字符串连接,将参数列表拼接为一个字符串 | +| concatSpiltWith | 字符串连接,将参数列表拼接为一个字符串 | +| underScoreCase2CamelCase | 下划线命名转为驼峰命名 | +| camelCase2UnderScoreCase | 驼峰命名法转为下划线命名 | +| sqliteEscape | 数据库字符转义 | + + +* **压缩相关工具类 ->** [ZipUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/src/main/java/dev/utils/common/assist/Averager.java) + +| 方法 | 注释 | +| :- | :- | +| add | 添加一个数字 | +| clear | 清除全部 | +| size | 返回参与均值计算的数字个数 | +| getAverage | 获取平均数 | +| print | 打印数字列 | + + +* **Base64 工具类 ->** [Base64.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/assist/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 | + + +* **时间均值计算器, 只能用于单线程计时。 ->** [TimeAverager.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/assist/TimeAverager.java) + +| 方法 | 注释 | +| :- | :- | +| start | 一个计时开始 | +| end | 一个计时结束 | +| endAndRestart | 一个计时结束,并且启动下次计时。 | +| average | 求全部计时均值 | +| print | 打印全部时间值 | +| clear | 清除数据 | + + +* **时间计时器 ->** [TimeCounter.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/assist/TimeCounter.java) + +| 方法 | 注释 | +| :- | :- | +| start | 开始计时 | +| durationRestart | 获取持续的时间并重新启动。 | +| duration | 获取持续的时间 | +| getStartTime | 获取开始时间 | + + +* **时间堵塞保留 ->** [TimeKeeper.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/assist/TimeKeeper.java) + +| 方法 | 注释 | +| :- | :- | +| getKeepTimeMillis | 获取预计堵塞时间 | +| setKeepTimeMillis | 设置预计堵塞时间 | +| startNow | 开始计时 | +| waitForEnd | 设置等待一段时间后, 通知方法 | +| onEnd | 结束触发通知方法 | + + +## **`dev.utils.common.assist.search`** + + +* **文件广度优先搜索算法 (多线程 + 队列搜索) ->** [FileBreadthFirstSearchUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setSearchHandler | 设置搜索处理接口 | +| getQueueSameTimeNumber | 获取任务队列同时进行数量 | +| setQueueSameTimeNumber | 任务队列同时进行数量 | +| isRunning | 是否搜索中 | +| stop | 停止搜索 | +| isStop | 是否停止搜索 | +| getStartTime | 获取开始搜索时间 | +| getEndTime | 获取结束搜索时间 | +| getDelayTime | 获取延迟校验时间 | +| setDelayTime | 设置延迟校验时间 | +| query | 查询 | +| isHandlerFile | 判断是否处理该文件 | +| isAddToList | 是否添加到集合 | +| OnEndListener | 搜索结束监听 | + + +* **文件深度优先搜索算法 (搜索某个目录下的全部文件) ->** [FileDepthFirstSearchUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java) + +| 方法 | 注释 | +| :- | :- | +| setSearchHandler | 设置搜索处理接口 | +| isRunning | 是否搜索中 | +| stop | 停止搜索 | +| isStop | 是否停止搜索 | +| getStartTime | 获取开始搜索时间 | +| getEndTime | 获取结束搜索时间 | +| query | 查询 | +| isHandlerFile | 判断是否处理该文件 | +| isAddToList | 是否添加到集合 | +| OnEndListener | 搜索结束监听 | + + +## **`dev.utils.common.cipher`** + + +* **Baes64 编解码, 进行加密 ->** [Base64Cipher.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/cipher/Base64Cipher.java) + +| 方法 | 注释 | +| :- | :- | +| decrypt | 解码 | +| encrypt | 编码 | + + +* **加密工具类 ->** [CipherUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/cipher/CipherUtils.java) + +| 方法 | 注释 | +| :- | :- | +| encrypt | 加密工具类 | +| decrypt | 解密方法 | + + +* **解密/解码接口 ->** [Decrypt.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/cipher/Decrypt.java) + +| 方法 | 注释 | +| :- | :- | +| decrypt | 解密/解码方法 | + + +* **加密/编码接口 ->** [Encrypt.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/cipher/Encrypt.java) + +| 方法 | 注释 | +| :- | :- | +| encrypt | 加密/编码方法 | + + +## **`dev.utils.common.encrypt`** + + +* **AES对称加密(Advanced Encryption Standard,高级数据加密标准,AES算法可以有效抵制针对DES的攻击算法,对称加密算法) ->** [AESUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/encrypt/AESUtils.java) + +| 方法 | 注释 | +| :- | :- | +| initKey | 生成密钥 | +| encrypt | AES 加密 | +| decrypt | AES 解密 | + + +* **CRC 工具类 ->** [CRCUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/encrypt/CRCUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getCRC32 | 获取 CRC32 值(返回Long,一定几率上唯一) | +| getCRC32Str | 获取 CRC32 值(做了处理,返回String) | +| getFileCrc32 | 获取文件CRC32 值 | + + +* **DES对称加密(Data Encryption Standard,数据加密标准,对称加密算法) ->** [DESUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/encrypt/DESUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getDESKey | 返回可逆算法DES的密钥 | +| encrypt | DES 加密 | +| decrypt | DES 解密 | + + +* **加/解密工具类 ->** [EncryptUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/encrypt/EncryptUtils.java) + +| 方法 | 注释 | +| :- | :- | +| encryptMD2ToString | MD2 加密 | +| encryptMD2 | MD2 加密 | +| encryptMD5ToString | MD5 加密 | +| encryptMD5 | MD5 加密 | +| encryptMD5File2String | MD5 加密文件 | +| encryptMD5File | MD5 加密文件 | +| encryptSHA1ToString | SHA1 加密 | +| encryptSHA1 | SHA1 加密 | +| encryptSHA224ToString | SHA224 加密 | +| encryptSHA224 | SHA224 加密 | +| encryptSHA256ToString | SHA256 加密 | +| encryptSHA256 | SHA256 加密 | +| encryptSHA384ToString | SHA384 加密 | +| encryptSHA384 | SHA384 加密 | +| encryptSHA512ToString | SHA512 加密 | +| encryptSHA512 | SHA512 加密 | +| encryptHmacMD5ToString | HmacMD5 加密 | +| encryptHmacMD5 | HmacMD5 加密 | +| encryptHmacSHA1ToString | HmacSHA1 加密 | +| encryptHmacSHA1 | HmacSHA1 加密 | +| encryptHmacSHA224ToString | HmacSHA224 加密 | +| encryptHmacSHA224 | HmacSHA224 加密 | +| encryptHmacSHA256ToString | HmacSHA256 加密 | +| encryptHmacSHA256 | HmacSHA256 加密 | +| encryptHmacSHA384ToString | HmacSHA384 加密 | +| encryptHmacSHA384 | HmacSHA384 加密 | +| encryptHmacSHA512ToString | HmacSHA512 加密 | +| encryptHmacSHA512 | HmacSHA512 加密 | +| encryptDES2Base64 | DES 加密 | +| encryptDES2HexString | DES 加密 | +| encryptDES | DES 加密 | +| decryptBase64DES | DES 解密 | +| decryptHexStringDES | DES 解密 | +| decryptDES | DES 解密 | +| encrypt3DES2Base64 | 3DES 加密 | +| encrypt3DES2HexString | 3DES 加密 | +| encrypt3DES | 3DES 加密 | +| decryptBase64_3DES | 3DES 解密 | +| decryptHexString3DES | 3DES 解密 | +| decrypt3DES | 3DES 解密 | +| encryptAES2Base64 | AES 加密 | +| encryptAES2HexString | AES 加密 | +| encryptAES | AES 加密 | +| decryptBase64AES | AES 解密 | +| decryptHexStringAES | AES 解密 | +| decryptAES | AES 解密 | +| encryptRSA2Base64 | RSA 加密 | +| encryptRSA2HexString | RSA 加密 | +| encryptRSA | RSA 加密 | +| decryptBase64RSA | RSA 解密 | +| decryptHexStringRSA | RSA 解密 | +| decryptRSA | RSA 解密 | + + +* **解码,编码 ->** [EscapeUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/encrypt/EscapeUtils.java) + +| 方法 | 注释 | +| :- | :- | +| escape | 编码 | +| unescape | 解码 说明:本方法保证 不论参数s是否经过escape()编码,均能获取正确的“解码”结果 | + + +* **MD5加密 不可逆(Message Digest,消息摘要算法) ->** [MD5Utils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/encrypt/MD5Utils.java) + +| 方法 | 注释 | +| :- | :- | +| md5 | 加密内容 - 32位大小MD5 - 小写 | +| md5Upper | 加密内容 - 32位大小MD5 - 大写 | +| toHexString | 进行转换 | +| getFileMD5 | 获取文件MD5值 - 小写 | + + +* **SHA 加密工具类 ->** [SHAUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/encrypt/SHAUtils.java) + +| 方法 | 注释 | +| :- | :- | +| sha1 | 加密内容 SHA1 | +| sha224 | 加密内容 SHA224 | +| sha256 | 加密内容 SHA256 | +| sha384 | 加密内容 SHA384 | +| sha512 | 加密内容 SHA512 | +| getFileSHA1 | 获取文件 Sha1 值 | +| getFileSHA256 | 获取文件 Sha256 值 | + + +* **3DES对称加密(Triple DES、DESede,进行了三重DES加密的算法,对称加密算法) ->** [TripleDESUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java) + +| 方法 | 注释 | +| :- | :- | +| initKey | 生成密钥 | +| encrypt | 3DES 加密 | +| decrypt | 3DES 解密 | + + +* **异或加密工具类 ->** [XorUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/encrypt/XorUtils.java) + +| 方法 | 注释 | +| :- | :- | +| encryptAsFix | 固定 key 的方式加密 | +| encrypt | 非固定 key 的方式加密 | +| decrypt | 解密 | + + +## **`dev.utils.common.random`** + + +* **随机概率采样算法 ->** [AliasMethod.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/random/AliasMethod.java) + +| 方法 | 注释 | +| :- | :- | +| next | 获取随机索引(对应几率索引) | + + +## **`dev.utils.common.thread`** + + +* **线程池管理 - 开发类 ->** [DevThreadManager.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadManager.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 DevThreadManager 实例 ,单例模式 | +| initConfig | 初始化配置信息 | +| putConfig | 添加配置信息 | +| removeConfig | 移除配置信息 | + + +* **线程池 - 开发类 ->** [DevThreadPool.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 | 延迟并以固定休息时间循环执行命令 | + + +## **`dev.utils.common.validator`** + + +* **银行卡管理 ->** [BankCheckUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/validator/BankCheckUtils.java) + +| 方法 | 注释 | +| :- | :- | +| checkBankCard | 校验银行卡卡号 是否合法 | +| getBankCardCheckCode | 从不含校验位的银行卡卡号采用 Luhm 校验算法获取校验位 | +| getNameOfBank | 通过银行卡 的前六位确定 判断银行开户行及卡种 | + + +* **居民身份证工具类 ->** [IDCardUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/validator/IDCardUtils.java) + +| 方法 | 注释 | +| :- | :- | +| getPowerSum | 将身份证的每位和对应位的加权因子相乘之后,再获取和值 | +| getCheckCode18 | 将power和值与1 1取模获取余数进行校验码判断 | +| converCharToInt | 将字符数组转换成数字数组 | +| validateIdCard18 | 身份证校验规则,验证18位身份编码是否合法 | +| validateIdCard15 | 身份证校验规则,验证15位身份编码是否合法 | +| convert15CardTo18 | 将15位身份证号码转换为18位 | +| validateTWCard | 验证台湾身份证号码 | +| validateHKCard | 验证香港身份证号码(存在Bug,部份特殊身份证无法检查) | +| validateIdCard10 | 判断 10 位数的身份证号, 是否合法 | +| validateCard | 验证身份证是否合法 | +| getAgeByIdCard | 根据身份编号获取年龄 | +| getBirthByIdCard | 根据身份编号获取生日 | +| getBirthdayByIdCard | 根据身份编号获取生日 | +| getYearByIdCard | 根据身份编号获取生日年 | +| getMonthByIdCard | 根据身份编号获取生日月 | +| getDateByIdCard | 根据身份编号获取生日天 | +| getGenderByIdCard | 根据身份编号获取性别 | +| getProvinceByIdCard | 根据身份编号获取户籍省份 | + + +* **校验工具类 ->** [ValidatorUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/validator/ValidatorUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断是否为null | +| isNumber | 检验数字 | +| isLetter | 判断字符串是不是全是字母 | +| isNumberLetter | 判断字符串是不是只含字母和数字 | +| isSpec | 检验特殊符号 | +| isWx | 检验微信号 | +| isRealName | 检验真实姓名 | +| isNickName | 校验昵称 | +| isUserName | 校验用户名 | +| isPassword | 校验密码 | +| isEmail | 校验邮箱 | +| isUrl | 校验URL | +| isIPAddress | 校验IP地址 | +| isIP | IP地址校验 | +| isChinese | 校验汉字(无符号,纯汉字) | +| isChineseAll | 判断字符串是不是全是中文 | +| isContainChinese | 判断字符串中包含中文、包括中文字符标点等 | + + +* **检验身份证工具类 ->** [ValiToIDCardUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToIDCardUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断是否为null | +| isIDCard | 校验身份证 | +| isHKIDCard | 校验身份证 -> 香港 | +| isAMIDCard | 校验身份证 -> 澳门 | +| isTWIDCard | 校验身份证 -> 台湾 | + + +* **检验联系(手机号,座机)工具类 ->** [ValiToPhoneUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java) + +| 方法 | 注释 | +| :- | :- | +| isEmpty | 判断是否为null | +| isPhoneCheck | 中国手机号格式验证,在输入可以调用该方法,点击发送验证码,使用 isPhone | +| isPhone | 是否中国手机号 | +| isPhoneToChinaTelecom | 是否中国电信手机号码 | +| isPhoneToChinaUnicom | 是否中国联通手机号码 | +| isPhoneToChinaMobile | 是否中国移动手机号码 | +| isPhoneToHkMobile | 判断是否香港手机号 | +| isPhoneCallNum | 验证电话号码的格式 | \ No newline at end of file diff --git a/DevLibUtils/build.gradle b/DevLibUtils/build.gradle index 7084b5e1df..f7f592a467 100644 --- a/DevLibUtils/build.gradle +++ b/DevLibUtils/build.gradle @@ -1,15 +1,15 @@ apply plugin: 'com.android.library' -def dev_utils_version = "1.0.0"; +def dev_utils_version = "1.4.1"; android { - compileSdkVersion 27 - buildToolsVersion "27.0.3" + compileSdkVersion 28 + buildToolsVersion "28.0.3" defaultConfig { - minSdkVersion 19 - targetSdkVersion 27 - versionCode 1 + minSdkVersion 14 + targetSdkVersion 28 + versionCode 141 versionName dev_utils_version /** @@ -39,9 +39,10 @@ android { } 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' + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:support-v4:28.0.0' + implementation 'com.android.support:design:28.0.0' } // == 打包, 包含注释 == @@ -99,13 +100,14 @@ artifacts { def _BASENAME = "DevLibUtils"; def _VERSION = "_v" + android.defaultConfig.versionName; def _DestinationPath = "build"; //生成jar包的位置 -def zipFile = file('build/intermediates/bundles/debug/classes.jar'); //待打包文件位置 +//def zipFile = file('build/intermediates/bundles/debug/classes.jar'); //待打包文件位置 +def zipFile = file('build/intermediates/intermediate-jars/debug/classes.jar'); //待打包文件位置 -task deleteBuild(type:Delete){ +task deleteBuild(type:Delete) { delete _DestinationPath + _BASENAME + _VERSION + ".jar" } -task makeJar(type:Jar){ +task makeJar(type:Jar) { from zipTree(zipFile) from fileTree(dir:'src/main',includes:['assets/**']) //将assets目录打入jar包 baseName = _BASENAME + _VERSION diff --git a/DevLibUtils/proguard-rules.pro b/DevLibUtils/proguard-rules.pro index f1b424510d..4bf8de3fd0 100644 --- a/DevLibUtils/proguard-rules.pro +++ b/DevLibUtils/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/DevLibUtils/src/main/java/dev/DevUtils.java b/DevLibUtils/src/main/java/dev/DevUtils.java index b8fcb54e16..fc0aa56ac5 100644 --- a/DevLibUtils/src/main/java/dev/DevUtils.java +++ b/DevLibUtils/src/main/java/dev/DevUtils.java @@ -1,11 +1,22 @@ package dev; +import android.annotation.SuppressLint; 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 android.text.TextUtils; + +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; @@ -13,9 +24,11 @@ import dev.utils.app.AnalysisRecordUtils; import dev.utils.app.FileRecordUtils; import dev.utils.app.HandlerUtils; +import dev.utils.app.KeyBoardUtils; 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; /** * detail: 开发工具类 @@ -27,63 +40,59 @@ public final class DevUtils { private DevUtils() { } -// // DevUtils 实例 -// private static DevUtils INSTANCE = new DevUtils(); -// -// /** 获取 DevUtils 实例 ,单例模式 */ -// public static DevUtils getInstance() { -// return INSTANCE; -// } - // --- - + /** 日志 TAG */ + private static final String TAG = DevUtils.class.getSimpleName(); /** 全局 Application 对象 */ private static Application sApplication; - /** 全局上下文 - getApplicationContext() */ + /** 全局 Context - getApplicationContext() */ private static Context sContext; /** 获取当前线程,主要判断是否属于主线程 */ private static Thread sUiThread; - /** 全局Handler,便于子线程快捷操作等 */ + /** 全局 Handler,便于子线程快捷操作等 */ private static Handler sHandler; /** 是否内部debug模式 */ private static boolean debug = false; /** * 默认初始化方法 - 必须调用 - Application.onCreate 中调用 - * @param context 上下文 + * @param context */ public static void init(Context context) { - // 设置全局上下文 + // 设置全局 Context initContext(context); // 初始化全局 Application initApplication(context); + // 注册 Activity 生命周期监听 + registerActivityLifecycleCallbacks(sApplication); + // 保存当前线程信息 + sUiThread = Thread.currentThread(); + // 初始化全局Handler - 主线程 + sHandler = new Handler(Looper.getMainLooper()); + // == 初始化工具类相关 == // 初始化Shared 工具类 SharedUtils.init(context); + // 初始化缓存类 + DevCache.get(context); + // 初始化Handler工具类 + HandlerUtils.init(context); // 初始化记录文件配置 - FileRecordUtils.appInit(); + FileRecordUtils.init(); // 初始化记录工具类 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); + DevLoggerUtils.init(context); + // 初始化 Toast + DevToast.init(sApplication); } /** - * 初始化全局上下文 + * 初始化全局 Context * @param context */ private static void initContext(Context context) { // 如果为null, 才进行判断处理 - if (DevUtils.sContext == null){ + if (DevUtils.sContext == null) { // 防止传进来的为null if (context == null) { return; @@ -98,14 +107,14 @@ private static void initContext(Context context) { */ private static void initApplication(Context context) { // 如果为null, 才进行判断处理 - if (DevUtils.sApplication == null){ - if (context == null){ + if (DevUtils.sApplication == null) { + if (context == null) { return; } Application mApplication = null; try { mApplication = (Application) context.getApplicationContext(); - } catch (Exception e){ + } catch (Exception e) { } // 防止传进来的为null if (mApplication == null) { @@ -116,7 +125,7 @@ private static void initApplication(Context context) { } /** - * 获取全局上下文 + * 获取全局 Context * @return */ public static Context getContext() { @@ -124,12 +133,12 @@ public static Context getContext() { } /** - * 获取上下文(判断null,视情况返回全局上下文) + * 获取 Context(判断null,视情况返回全局 Context) * @param context */ public static Context getContext(Context context) { // 进行判断 - if (context != null){ + if (context != null) { return context; } return DevUtils.sContext; @@ -139,16 +148,50 @@ public static Context getContext(Context context) { * 获取全局 Application * @return */ - public static Application getApplication(){ - return DevUtils.sApplication; + public static Application getApplication() { + if (DevUtils.sApplication != null) return DevUtils.sApplication; + try { + Application app = getApplicationByReflect(); + if (app != null) { + init(app); // 初始化操作 + } + return app; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getApplication"); + } + return null; + } + + // = + + /** + * 反射获取 Application + * @return + */ + 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 */ - public static Handler getHandler(){ - if (sHandler == null){ + public static Handler getHandler() { + if (sHandler == null) { // 初始化全局Handler - 主线程 sHandler = new Handler(Looper.getMainLooper()); //Looper.myLooper(); } @@ -172,7 +215,7 @@ public static void runOnUiThread(Runnable action) { * @param action * @param delayMillis */ - public static void runOnUiThread(Runnable action, long delayMillis){ + public static void runOnUiThread(Runnable action, long delayMillis) { sHandler.postDelayed(action, delayMillis); } @@ -201,98 +244,633 @@ public static boolean isDebug() { return debug; } - // ================== - // ==== Activity ==== - // ================== + // == 工具类版本 == + + /** + * 获取工具类版本 - VERSION_NAME + * @return + */ + public static String getUtilsVersion() { + return BuildConfig.VERSION_NAME; + } + + /** + * 获取工具类版本 - VERSION_CODE + * @return + */ + public static int getUtilsVersionCode() { + return BuildConfig.VERSION_CODE; + } + + // ======================= + // ==== Activity 监听 ==== + // ======================= + + /** ActivityLifecycleCallbacks 实现类, 监听 Activity */ + private static final ActivityLifecycleImpl ACTIVITY_LIFECYCLE = new ActivityLifecycleImpl(); + // Activity 过滤判断接口 + private static ActivityLifecycleFilter activityLifecycleFilter; + /** 权限 Activity class name */ + public static final String PERMISSION_ACTIVITY_CLASS_NAME = "dev.utils.app.PermissionUtils$PermissionActivity"; /** * 注册绑定Activity 生命周期事件处理 * @param application */ - private static void registerActivityLifecycleCallbacks(Application application){ - if (application != null){ - // 先移除旧的监听 - application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks); - // 绑定新的监听 - application.registerActivityLifecycleCallbacks(lifecycleCallbacks); + private static void registerActivityLifecycleCallbacks(Application application) { + // 先移除监听 + unregisterActivityLifecycleCallbacks(application); + // 防止为null + if (application != null) { + try { + // 绑定新的监听 + application.registerActivityLifecycleCallbacks(ACTIVITY_LIFECYCLE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "registerActivityLifecycleCallbacks"); + } } } - /** 保留当前(前台) Activity */ - private static Activity sCurActivity = null; + /** + * 解除注册 Activity 生命周期事件处理 + * @param application + */ + private static void unregisterActivityLifecycleCallbacks(Application application) { + if (application != null) { + try { + // 先移除旧的监听 + application.unregisterActivityLifecycleCallbacks(ACTIVITY_LIFECYCLE); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "unregisterActivityLifecycleCallbacks"); + } + } + } + + // == 对外公开方法 == + + /** + * 获取 Activity 生命周期 相关信息获取接口类 + * @return + */ + public static ActivityLifecycleGet getActivityLifecycleGet() { + return ACTIVITY_LIFECYCLE; + } /** - * 对Activity的生命周期事件进行集中处理。 + * 获取 Activity 生命周期 事件监听接口类 + * @return + */ + public static ActivityLifecycleNotify getActivityLifecycleNotify() { + return ACTIVITY_LIFECYCLE; + } + + /** + * 获取 Top Activity + * @return + */ + public static Activity getTopActivity() { + return ACTIVITY_LIFECYCLE.getTopActivity(); + } + + /** + * 设置 Activity 生命周期 过滤判断接口 + * @param activityLifecycleFilter + */ + public static void setActivityLifecycleFilter(ActivityLifecycleFilter activityLifecycleFilter) { + DevUtils.activityLifecycleFilter = activityLifecycleFilter; + } + + // == 接口相关 == + + /** + * detail: 对Activity的生命周期事件进行集中处理。 ActivityLifecycleCallbacks 实现方法 + * Created by Ttt * http://blog.csdn.net/tongcpp/article/details/40344871 */ - private static Application.ActivityLifecycleCallbacks lifecycleCallbacks = new Application.ActivityLifecycleCallbacks() { - @Override + private static class ActivityLifecycleImpl implements Application.ActivityLifecycleCallbacks, ActivityLifecycleGet, ActivityLifecycleNotify { + + // 保存未销毁的 Activity + final LinkedList mActivityList = new LinkedList<>(); + // App 状态改变事件 + final Map mStatusListenerMap = new ConcurrentHashMap<>(); + // Activity 销毁事件 + final Map> mDestroyedListenerMap = 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.absActivityLifecycle != null){ + DevUtils.absActivityLifecycle.onActivityCreated(activity, savedInstanceState); + } } @Override public void onActivityStarted(Activity activity) { + if (!mIsBackground) { + setTopActivity(activity); + } + if (mConfigCount < 0) { + ++mConfigCount; + } else { + ++mForegroundCount; + } + if (DevUtils.absActivityLifecycle != null){ + DevUtils.absActivityLifecycle.onActivityStarted(activity); + } } @Override public void onActivityResumed(Activity activity) { - // 保存当前Activity - DevUtils.sCurActivity = activity; + setTopActivity(activity); + // Activity 准备可见, 设置为非后台 Activity + if (mIsBackground) { + mIsBackground = false; + postStatus(true); + } + + if (DevUtils.absActivityLifecycle != null){ + DevUtils.absActivityLifecycle.onActivityResumed(activity); + } } @Override public void onActivityPaused(Activity activity) { - + if (DevUtils.absActivityLifecycle != null){ + DevUtils.absActivityLifecycle.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.absActivityLifecycle != null){ + DevUtils.absActivityLifecycle.onActivityStopped(activity); + } } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { - + if (DevUtils.absActivityLifecycle != null){ + DevUtils.absActivityLifecycle.onActivitySaveInstanceState(activity, outState); + } } @Override public void onActivityDestroyed(Activity activity) { + mActivityList.remove(activity); + // 通知 Activity 销毁 + consumeOnActivityDestroyedListener(activity); + // 修复软键盘内存泄漏 在 Activity.onDestroy() 中使用 + KeyBoardUtils.fixSoftInputLeaks(activity); + + if (DevUtils.absActivityLifecycle != null){ + DevUtils.absActivityLifecycle.onActivityDestroyed(activity); + } + } + // == 内部处理判断方法 == + + /** + * 保存 Activity 栈顶 + * @param activity + */ + private void setTopActivity(final Activity activity) { + // 判断是否过滤 Activity + if (ACTIVITY_LIFECYCLE_FILTER.filter(activity)) return; + // 判断是否已经包含该 Activity + if (mActivityList.contains(activity)) { + if (!mActivityList.getLast().equals(activity)) { + mActivityList.remove(activity); + mActivityList.addLast(activity); + } + } else { + mActivityList.addLast(activity); + } + } + + /** + * 反射获取栈顶 Activity + * @return + */ + private Activity getTopActivityByReflect() { + try { + @SuppressLint("PrivateApi") + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null); + Field activitiesField = activityThreadClass.getDeclaredField("mActivityList"); + 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 + */ + @Override + public Activity getTopActivity() { + if (!mActivityList.isEmpty()) { + final Activity topActivity = mActivityList.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 + */ + @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 + */ + @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 + */ + @Override + public boolean isBackground() { + return mIsBackground; + } + + /** + * 获取 Activity 总数 + * @return + */ + @Override + public int getActivityCount() { + return mActivityList.size(); + } + + // == ActivityLifecycleNotify == + + /** + * 添加 App 状态改变事件监听 + * @param object + * @param listener + */ + @Override + public void addOnAppStatusChangedListener(final Object object, final OnAppStatusChangedListener listener) { + mStatusListenerMap.put(object, listener); + } + + /** + * 移除 App 状态改变事件监听 + * @param object + */ + @Override + public void removeOnAppStatusChangedListener(final Object object) { + mStatusListenerMap.remove(object); + } + + /** + * 移除全部 App 状态改变事件监听 + */ + @Override + public void removeAllOnAppStatusChangedListener() { + mStatusListenerMap.clear(); } - }; + + // = + + /** + * 添加 Activity 销毁通知事件 + * @param activity + * @param listener + */ + @Override + public void addOnActivityDestroyedListener(final Activity activity, final OnActivityDestroyedListener listener) { + if (activity == null || listener == null) return; + Set listeners; + if (!mDestroyedListenerMap.containsKey(activity)) { + listeners = new HashSet<>(); + mDestroyedListenerMap.put(activity, listeners); + } else { + listeners = mDestroyedListenerMap.get(activity); + if (listeners.contains(listener)) return; + } + listeners.add(listener); + } + + /** + * 移除 Activity 销毁通知事件 + * @param activity + */ + @Override + public void removeOnActivityDestroyedListener(final Activity activity) { + if (activity == null) return; + mDestroyedListenerMap.remove(activity); + } + + /** + * 移除全部 Activity 销毁通知事件 + */ + @Override + public void removeAllOnActivityDestroyedListener() { + mDestroyedListenerMap.clear(); + } + + + // == 事件通知相关 == + + /** + * 发送状态改变通知 + * @param isForeground + */ + private void postStatus(final boolean isForeground) { + if (mStatusListenerMap.isEmpty()) return; + // 保存到新的集合, 防止 ConcurrentModificationException + List lists = new ArrayList<>(mStatusListenerMap.values()); + // 遍历通知 + for (OnAppStatusChangedListener listener : lists) { + if (listener != null) { + if (isForeground) { + listener.onForeground(); + } else { + listener.onBackground(); + } + } + } + } + + /** + * 通知 Activity 销毁, 并且消费(移除)监听事件 + * @param activity + */ + private void consumeOnActivityDestroyedListener(final Activity activity) { + try { + // 保存到新的集合, 防止 ConcurrentModificationException + Set sets = new HashSet<>(mDestroyedListenerMap.get(activity)); + // 遍历通知 + for (OnActivityDestroyedListener listener : sets) { + if (listener != null) { + listener.onActivityDestroyed(activity); + } + } + } catch (Exception e) { + } + // 移除已消费的事件 + removeOnActivityDestroyedListener(activity); + } + } /** - * 获取当前Activity - * @return + * detail: Activity 生命周期 相关信息获取接口 + * Created by Ttt */ - public static Activity getCurActivity(){ - return DevUtils.sCurActivity; + public interface ActivityLifecycleGet { + + /** + * 获取最顶部 (当前或最后一个显示) Activity + * @return + */ + Activity getTopActivity(); + + /** + * 判断某个 Activity 是否 Top Activity + * @param activityClassName Activity.class.getCanonicalName() + * @return + */ + boolean isTopActivity(String activityClassName); + + /** + * 判断某个 Class(Activity) 是否 Top Activity + * @param clazz Activity.class or this.getClass() + * @return + */ + boolean isTopActivity(Class clazz); + + /** + * 判断应用是否后台(不可见) + * @return + */ + boolean isBackground(); + + /** + * 获取 Activity 总数 + * @return + */ + int getActivityCount(); } /** - * 判断是否相同的 Activity - * @param activity - * @return + * detail: Activity 生命周期 过滤判断接口 + * Created by Ttt */ - 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){ + public interface ActivityLifecycleFilter { + + /** + * 判断是否过滤该类(不进行添加等操作) + * @param activity + * @return true: return + */ + boolean filter(Activity activity); + } + + /** + * detail: Activity 生命周期 通知接口 + * Created by Ttt + */ + public interface ActivityLifecycleNotify { + + /** + * 添加 App 状态改变事件监听 + * @param object + * @param listener + */ + void addOnAppStatusChangedListener(Object object, OnAppStatusChangedListener listener); + + /** + * 移除 App 状态改变事件监听 + * @param object + */ + void removeOnAppStatusChangedListener(Object object); + + /** + * 移除全部 App 状态改变事件监听 + */ + void removeAllOnAppStatusChangedListener(); + + // = + + /** + * 添加 Activity 销毁通知事件 + * @param activity + * @param listener + */ + void addOnActivityDestroyedListener(Activity activity, OnActivityDestroyedListener listener); + + /** + * 移除 Activity 销毁通知事件 + * @param activity + */ + void removeOnActivityDestroyedListener(Activity activity); + + /** + * 移除全部 Activity 销毁通知事件 + */ + void removeAllOnActivityDestroyedListener(); + } + + /** + * detail: App 状态改变事件 + * Created by Ttt + */ + public interface OnAppStatusChangedListener { + + /** + * 切换到前台 + */ + void onForeground(); + + /** + * 切换到后台 + */ + void onBackground(); + } + + /** + * detail: Activity 销毁事件 + * Created by Ttt + */ + public interface OnActivityDestroyedListener { + + /** + * Activity 销毁通知 + * @param activity + */ + void onActivityDestroyed(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 (activityLifecycleFilter != null) { + return activityLifecycleFilter.filter(activity); + } + } } + return false; } - return false; - } + }; - // == 工具类版本 == + // = + + // ActivityLifecycleCallbacks 抽象类 + private static AbsActivityLifecycle absActivityLifecycle; /** - * 获取工具类版本 - * @return + * 设置 ActivityLifecycle 监听回调 + * @param absActivityLifecycle */ - public static String getUtilsVersion(){ - return BuildConfig.VERSION_NAME; + public static void setAbsActivityLifecycle(AbsActivityLifecycle absActivityLifecycle) { + DevUtils.absActivityLifecycle = absActivityLifecycle; + } + + /** + * detail: ActivityLifecycleCallbacks 抽象类 + * Created by Ttt + */ + public static abstract class AbsActivityLifecycle 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) { + } } } diff --git a/DevLibUtils/src/main/java/dev/utils/JCLogUtils.java b/DevLibUtils/src/main/java/dev/utils/JCLogUtils.java index eb43dfb6d1..5f35590e20 100644 --- a/DevLibUtils/src/main/java/dev/utils/JCLogUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/JCLogUtils.java @@ -19,7 +19,7 @@ */ public final class JCLogUtils { - private JCLogUtils(){ + private JCLogUtils() { } // 普通信息模式 @@ -84,7 +84,7 @@ private static void printLog(int logType, String tag, String msg) { break; } // 打印信息 - if (isEmpty(tag)){ + if (isEmpty(tag)) { System.out.println(msg); } else { System.out.println(tag + " : " + msg); @@ -100,8 +100,8 @@ private static void printLog(int logType, String tag, String msg) { private static String createMessage(String message, Object... args) { String result = null; try { - if(message != null){ - if(args == null){ + if(message != null) { + if(args == null) { // 动态参数为null result = "params is null"; } else { @@ -129,8 +129,8 @@ private static String createMessage(String message, Object... args) { private static String splitErrorMessage(Throwable throwable, String message, Object... args) { String result = null; try { - if(throwable != null){ - if(message != null){ + if(throwable != null) { + if(message != null) { result = createMessage(message, args) + " : " + throwable.toString(); } else { result = throwable.toString(); @@ -156,7 +156,7 @@ public static void e(Throwable throwable) { eTag(DEFAULT_DEFAULT_TAG, throwable); } - public void e(String message, Object... args) { + public static void e(String message, Object... args) { e(null, message, args); } @@ -179,37 +179,37 @@ public static void xml(String xml) { // -- 日志打印方法 -- public static void dTag(String tag, String message, Object... args) { - if (JUDGE_PRINT_LOG){ + if (JUDGE_PRINT_LOG) { printLog(DEBUG, tag, createMessage(message, args)); } } public static void eTag(String tag, String message, Object... args) { - if(JUDGE_PRINT_LOG){ + if(JUDGE_PRINT_LOG) { printLog(ERROR, tag, createMessage(message, args)); } } public static void eTag(String tag, Throwable throwable) { - if(JUDGE_PRINT_LOG){ + 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){ + 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){ + if(JUDGE_PRINT_LOG) { printLog(INFO, tag, createMessage(message, args)); } } public static void jsonTag(String tag, String json) { - if (JUDGE_PRINT_LOG){ + if (JUDGE_PRINT_LOG) { // 判断传入JSON格式信息是否为null if (isEmpty(json)) { printLog(ERROR, tag, "Empty/Null json content"); @@ -233,14 +233,14 @@ public static void jsonTag(String tag, String json) { } } catch (Exception e) { String eHint = "null"; - if (e != null){ + if (e != null) { Throwable throwable = e.getCause(); - if (throwable != null){ + if (throwable != null) { eHint = throwable.getMessage(); } else { try { eHint = e.getMessage(); - } catch (Exception e1){ + } catch (Exception e1) { eHint = e1.getMessage(); } } @@ -251,7 +251,7 @@ public static void jsonTag(String tag, String json) { } public static void xmlTag(String tag, String xml) { - if (JUDGE_PRINT_LOG){ + if (JUDGE_PRINT_LOG) { // 判断传入XML格式信息是否为null if (isEmpty(xml)) { printLog(ERROR, tag, "Empty/Null xml content"); @@ -270,14 +270,14 @@ public static void xmlTag(String tag, String xml) { printLog(DEBUG, tag, message); } catch (Exception e) { String eHint = "null"; - if (e != null){ + if (e != null) { Throwable throwable = e.getCause(); - if (throwable != null){ + if (throwable != null) { eHint = throwable.getMessage(); } else { try { eHint = e.getMessage(); - } catch (Exception e1){ + } catch (Exception e1) { eHint = e1.getMessage(); } } diff --git a/DevLibUtils/src/main/java/dev/utils/LogPrintUtils.java b/DevLibUtils/src/main/java/dev/utils/LogPrintUtils.java index f5daf7b262..b5ae37e2ff 100644 --- a/DevLibUtils/src/main/java/dev/utils/LogPrintUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/LogPrintUtils.java @@ -22,7 +22,7 @@ */ public final class LogPrintUtils { - private LogPrintUtils(){ + private LogPrintUtils() { } /** JSON格式内容缩进 */ @@ -91,8 +91,8 @@ private static void printLog(int logType, String tag, String msg) { private static String createMessage(String message, Object... args) { String result = null; try { - if(message != null){ - if(args == null){ + if(message != null) { + if(args == null) { // 动态参数为null result = "params is null"; } else { @@ -120,8 +120,8 @@ private static String createMessage(String message, Object... args) { private static String splitErrorMessage(Throwable throwable, String message, Object... args) { String result = null; try { - if(throwable != null){ - if(message != null){ + if(throwable != null) { + if(message != null) { result = createMessage(message, args) + " : " + throwable.toString(); } else { result = throwable.toString(); @@ -147,7 +147,7 @@ public static void e(Throwable throwable) { eTag(DEFAULT_DEFAULT_TAG, throwable, null); } - public void e(String message, Object... args) { + public static void e(String message, Object... args) { e(null, message, args); } @@ -182,55 +182,55 @@ public static void xml(String xml) { // -- 日志打印方法 -- public static void dTag(String tag, String message, Object... args) { - if (JUDGE_PRINT_LOG){ + 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){ + if(JUDGE_PRINT_LOG) { printLog(Log.ERROR, tag, createMessage(message, args)); } } public static void eTag(String tag, Throwable throwable) { - if(JUDGE_PRINT_LOG){ + 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){ + 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){ + 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){ + 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){ + 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){ + if(JUDGE_PRINT_LOG) { printLog(Log.ASSERT, tag, createMessage(message, args)); } } public static void jsonTag(String tag, String json) { - if (JUDGE_PRINT_LOG){ + if (JUDGE_PRINT_LOG) { // 判断传入JSON格式信息是否为null if (TextUtils.isEmpty(json)) { printLog(Log.ERROR, tag, "Empty/Null json content"); @@ -254,14 +254,14 @@ public static void jsonTag(String tag, String json) { } } catch (Exception e) { String eHint = "null"; - if (e != null){ + if (e != null) { Throwable throwable = e.getCause(); - if (throwable != null){ + if (throwable != null) { eHint = throwable.getMessage(); } else { try { eHint = e.getMessage(); - } catch (Exception e1){ + } catch (Exception e1) { eHint = e1.getMessage(); } } @@ -272,7 +272,7 @@ public static void jsonTag(String tag, String json) { } public static void xmlTag(String tag, String xml) { - if (JUDGE_PRINT_LOG){ + if (JUDGE_PRINT_LOG) { // 判断传入XML格式信息是否为null if (TextUtils.isEmpty(xml)) { printLog(Log.ERROR, tag, "Empty/Null xml content"); @@ -291,14 +291,14 @@ public static void xmlTag(String tag, String xml) { printLog(Log.DEBUG, tag, message); } catch (Exception e) { String eHint = "null"; - if (e != null){ + if (e != null) { Throwable throwable = e.getCause(); - if (throwable != null){ + if (throwable != null) { eHint = throwable.getMessage(); } else { try { eHint = e.getMessage(); - } catch (Exception e1){ + } catch (Exception e1) { eHint = e1.getMessage(); } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/ADBUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ADBUtils.java new file mode 100644 index 0000000000..d2c7e5117b --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/ADBUtils.java @@ -0,0 +1,2247 @@ +package dev.utils.app; + +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.PowerManager; +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 工具类 + * Created by Ttt + * hint: + * + * // Awesome Adb——一份超全超详细的 ADB 用法大全 + * https://github.com/mzlogin/awesome-adb + * + * // Process.waitFor()的返回值含义 + * https://blog.csdn.net/qq_35661171/article/details/79096786 + * + * // adb shell input + * https://blog.csdn.net/soslinken/article/details/49587497 + * + * android 上发送adb 指令,不需要加 adb shell + * + * // https://www.imooc.com/qadetail/198264 + * grep 是 linux 下的命令, windows 用 findstr + * + * 开启 Thread 执行, 非主线程, 否则无响应并无效 + */ +public final class ADBUtils { + + private ADBUtils() { + } + + // 日志 TAG + private static final String TAG = ADBUtils.class.getSimpleName(); + // 正则 - 空格 + private static final String SPACE_STR = "\\s"; + /** 换行字符串 */ + private static final String NEW_LINE_STR = System.getProperty("line.separator"); + + /** + * 判断设备是否 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; + } + + /** + * 请求 Root 权限 + */ + public static void requestRoot() { + ShellUtils.execCmd("exit", true); + } + + /** + * 判断 App 是否授权 Root 权限 + * @return + */ + public static boolean isGrantedRoot() { + ShellUtils.CommandResult result = ShellUtils.execCmd("exit", true); + return result.isSuccess2(); + } + + // == 应用管理 == + + // = 应用列表 = + + /** + * 获取 App 列表(包名) + * @param type + * @return + * https://blog.csdn.net/henni_719/article/details/62222439 + */ + public static List getAppList(String type) { + // adb shell pm list packages [options] + String typeStr = isSpace(type) ? "" : " " + type; + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd("pm list packages" + typeStr, false); + if (result.isSuccess3()) { + try { + String[] arys = result.successMsg.split(NEW_LINE_STR); + return Arrays.asList(arys); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppList type => " + typeStr); + } + } + return null; + } + + /** + * 获取 App 安装列表(包名) + * @return + */ + 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 strFilter + * @return + */ + public static List getAppListToFilter(String strFilter) { + if (isSpace(strFilter)) return null; + return getAppList("| grep " + strFilter.trim()); + } + + /** + * 判断是否安装应用 + * @param packageName + * @return + */ + public static boolean isInstalledApp(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(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 + */ + public static boolean clearAppDataCache(String packageName) { + if (isSpace(packageName)) return false; + // adb shell pm clear + String cmd = "pm clear %s"; + // 执行 shell cmd + 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(String packageName) { + if (isSpace(packageName)) return null; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("dumpsys package " + packageName, false); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 获取 App versionCode + * @param packageName + * @return + */ + public static int getVersionCode(String packageName) { + if (isSpace(packageName)) return 0; + try { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("dumpsys package " + packageName + " | grep version", true); + if (result.isSuccess3()) { + String[] arys = result.successMsg.split(SPACE_STR); + for (String str : arys) { + 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 + */ + public static String getVersionName(String packageName) { + if (isSpace(packageName)) return null; + try { + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd("dumpsys package " + packageName + " | grep version", true); + if (result.isSuccess3()) { + String[] arys = result.successMsg.split(SPACE_STR); + for (String str : arys) { + 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 + */ + public static boolean installApp(String filePath) { + return installApp("-rtsd", filePath); + } + + /** + * 安装应用 + * @param filePath /sdcard/xxx/x.apk + * @return + */ + public static boolean installApp(String params, String filePath) { + if (isSpace(params)) return false; + boolean isRoot = isDeviceRooted(); + +// -l 将应用安装到保护目录 /mnt/asec +// -r 允许覆盖安装 +// -t 允许安装 AndroidManifest.xml 里 application 指定 android:testOnly="true" 的应用 +// -s 将应用安装到 sdcard +// -d 允许降级覆盖安装 +// -g 授予所有运行时权限 +// android:testOnly="true"(ide 绿色三角运行) => https://blog.csdn.net/lihenhao/article/details/79146211 + + // adb install [-lrtsdg] + String cmd = "adb install %s %s"; + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, params, filePath), isRoot); + // 判断是否成功 + if (result.isSuccess4("success")) { + return true; + } + return false; + } + + /** + * 静默安装 App + * @param filePath + * @return + */ + public static boolean installAppSilent(final String filePath) { + return installAppSilent(getFileByPath(filePath), null); + } + + /** + * 静默安装 App + * @param file + * @return + */ + public static boolean installAppSilent(final File file) { + return installAppSilent(file, null); + } + + /** + * 静默安装 App + * @param filePath + * @param params + * @return + */ + public static boolean installAppSilent(final String filePath, final String params) { + return installAppSilent(getFileByPath(filePath), params, isDeviceRooted()); + } + + /** + * 静默安装 App + * @param file + * @param params + * @return + */ + public static boolean installAppSilent(final File file, final String params) { + return installAppSilent(file, params, isDeviceRooted()); + } + + /** + * 静默安装 App + * @param file + * @param params + * @param isRooted + * @return + */ + 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"); + } + + /** + * 卸载 App + * @param packageName + * @return + */ + public static boolean uninstallApp(String packageName) { + return uninstallApp(packageName, false); + } + + /** + * 卸载 App + * @param packageName + * @param isKeepData -k 参数可选,表示卸载应用但保留数据和缓存目录。 + * @return + */ + public static boolean uninstallApp(String packageName, boolean isKeepData) { + if (isSpace(packageName)) return false; + boolean isRoot = isDeviceRooted(); + // adb uninstall [-k] + String cmd = "adb uninstall "; + if (isKeepData) { + cmd += " -k "; + } + cmd += packageName; + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, isRoot); + // 判断是否成功 + if (result.isSuccess4("success")) { + return true; + } + return false; + } + + /** + * 静默卸载 App + * @param packageName + * @return + */ + public static boolean uninstallAppSilent(String packageName) { + return uninstallAppSilent(packageName, false, isDeviceRooted()); + } + + /** + * 静默卸载 App + * @param packageName + * @param isKeepData + * @return + */ + public static boolean uninstallAppSilent(String packageName, boolean isKeepData) { + return uninstallAppSilent(packageName, isKeepData, isDeviceRooted()); + } + + /** + * 静默卸载 App + * @param packageName + * @param isKeepData + * @param isRooted + * @return + */ + public static boolean uninstallAppSilent(String packageName, boolean isKeepData, 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 + */ + public static String getActivityToLauncher(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(), result.successMsg.length()); + // 进行拆分 + String[] arys = subData.split(NEW_LINE_STR); + for (String str : arys) { + if (!TextUtils.isEmpty(str)) { + // 存在包名才处理 + if (str.indexOf(packageName) != -1) { + String[] splitArys = str.split(SPACE_STR); + for (String strData : splitArys) { + if (!TextUtils.isEmpty(strData)) { + // 属于 包名/ 前缀的 + if (strData.indexOf(packageName + "/") != -1) { + // 防止属于 包名/.xx.Main_Activity + if (strData.indexOf("/.") != -1) { + // 包名/.xx.Main_Activity => 包名/包名.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 + */ + 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[] arys = result.successMsg.split(NEW_LINE_STR); + for (String str : arys) { + 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 + */ + public static String getWindowCurrent2() { + String cmd = "dumpsys window windows | grep Current"; + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(cmd, true); + if (result.isSuccess3()) { + try { + // 拆分换行, 并循环 + String[] arys = result.successMsg.split(NEW_LINE_STR); + for (String str : arys) { + if (!TextUtils.isEmpty(str)) { + String[] splitArys = str.split(SPACE_STR); + 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); + // 防止属于 包名/.xx.Main_Activity + if (strData.indexOf("/.") != -1) { + // 包名/.xx.Main_Activity => 包名/包名.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 + */ + public static String getWindowCurrentToPackage(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[] arys = result.successMsg.split(NEW_LINE_STR); + for (String str : arys) { + if (!TextUtils.isEmpty(str)) { + String[] splitArys = str.split(SPACE_STR); + 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); + // 防止属于 包名/.xx.Main_Activity + if (strData.indexOf("/.") != -1) { + // 包名/.xx.Main_Activity => 包名/包名.xx.Main_Activity + strData = strData.replace("/", "/" + packageName); + } + return strData; + } + } + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getWindowCurrentToPackage"); + } + } + return null; + } + + // == 获取当前Activity == + + /** + * 获取当前显示的 Activity + * @return + */ + 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[] arys = result.successMsg.split(NEW_LINE_STR); + for (String str : arys) { + if (!TextUtils.isEmpty(str)) { + String[] splitArys = str.split(SPACE_STR); + if (splitArys != null && splitArys.length != 0) { + for (String splitStr : splitArys) { + if (!TextUtils.isEmpty(splitStr)) { + int start = splitStr.indexOf("/"); + if (start != -1) { + // 获取裁减数据 + String strData = splitStr; + // 防止属于 包名/.xx.Main_Activity + if (strData.indexOf("/.") != -1) { + // 包名/.xx.Main_Activity => 包名/包名.xx.Main_Activity + strData = strData.replace("/", "/" + splitStr.substring(0, start)); + } + return strData; + } + } + } + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getActivityCurrent"); + } + } + return null; + } + + /** + * 获取 activity 栈 + * @return + */ + public static String getActivitys() { + return getActivitys(null); + } + + /** + * 获取 activity 栈 + * @param append + * @return + */ + public static String getActivitys(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 + */ + public static String getActivitysToPackage(String packageName) { + if (isSpace(packageName)) { + return null; + } + return getActivitys("| grep " + packageName); + } + + /** + * 获取对应包名的 Activity 栈 (处理成 List), 最新的 Activity 越靠后 + * @param packageName + * @return + */ + public static List getActivitysToPackageLists(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(SPACE_STR); + if (splitArys != null && splitArys.length != 0) { + for (String splitStr : splitArys) { + int start = splitStr.indexOf(packageName + "/"); + if (start != -1) { + // 获取裁减数据 + String strData = splitStr; + // 防止属于 包名/.xx.XxxActivity + if (strData.indexOf("/.") != -1) { + // 包名/.xx.XxxActivity => 包名/包名.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 + * @return + */ + public static boolean isActivityTopRepeat(String packageName, 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); + // 判断是否该页面结尾 + if (data.endsWith(activity)) { + return true; + } else { + return false; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isActivityTopRepeat"); + } + } + return isRepeat; + } + + /** + * 判断 Activity 栈顶 是否重复 + * @param packageName + * @param activitys + * @return + */ + public static boolean isActivityTopRepeat(String packageName, 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); + // 判断是否该页面结尾 + if (data.endsWith(activity)) { + return true; + } else { + return false; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "isActivityTopRepeat"); + } + } + } + return isRepeat; + } + + // = + + /** + * 获取 Activity 栈顶 重复总数 + * @param packageName + * @param activity + * @return + */ + public static int getActivityTopRepeatCount(String packageName, 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 + * @return + */ + public static int getActivityTopRepeatCount(String packageName, 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 + */ + 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 + */ + public static String getServices(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 + */ + public static boolean startSelfApp() { + return startSelfApp(false); + } + + /** + * 启动自身应用 + * @param closeActivity + * @return + */ + public static boolean startSelfApp(boolean closeActivity) { + try { + // 获取包名 + String packageName = AppUtils.getAppPackageName(); + // 获取 Launcher Activity + String activity = ActivityUtils.getLauncherActivity(); + // 跳转应用启动页(启动应用) + startActivity(packageName + "/" + activity, closeActivity); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startSelfApp"); + } + return false; + } + + /** + * 跳转页面 Activity + * @param packageAndLauncher 包名/包名.页面 + * @param closeActivity 关闭Activity所属的App进程后再启动Activity + * @return + */ + public static boolean startActivity(String packageAndLauncher, boolean closeActivity) { + return startActivity(packageAndLauncher, null, closeActivity); + } + + /** + * 跳转页面 Activity + * @param packageAndLauncher 包名/.xx + * @param append 追加的信息, 例如传递参数等 + * @param closeActivity 关闭Activity所属的App进程后再启动Activity + * @return + */ + public static boolean startActivity(String packageAndLauncher, String append, 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 包名/.xxx + * @return + */ + public static boolean startService(String packageAndService) { + return startService(packageAndService, null); + } + + /** + * 启动服务 + * @param packageAndService 包名/.xxx + * @param append 追加的信息, 例如传递参数等 + * @return + */ + public static boolean startService(String packageAndService, 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 包名/.xxx + * @return + */ + public static boolean stopService(String packageAndService) { + return stopService(packageAndService, null); + } + + /** + * 停止服务 + * @param packageAndService 包名/.xxx + * @param append 追加的信息, 例如传递参数等 + * @return + */ + public static boolean stopService(String packageAndService, 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; + } + + /** + * 发送广播(向所有组件发送) + * @param broadcast + * @return + * 例: + * adb shell am broadcast -a android.intent.action.BOOT_COMPLETED + * 向所有组件广播 BOOT_COMPLETED + */ + public static boolean sendBroadcastToAll(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; + } + + /** + * 发送广播 + * @param packageAndBroadcast 包名/.xxx + * @param broadcast + * @return + * 例: + * adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n org.mazhuang.boottimemeasure/.BootCompletedReceiver + * 只向 org.mazhuang.boottimemeasure/.BootCompletedReceiver 广播 BOOT_COMPLETED + */ + public static boolean sendBroadcast(String packageAndBroadcast, 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 + */ + public static boolean kill(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 + */ + public static boolean sendTrimMemory(int pid, 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 +// */ +// public static boolean pull(String remote, 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 +// */ +// public static boolean push(String local, 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 + * @param y + * @return + */ + public static boolean tap(float x, 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 + * @param y + * @return + */ + public static boolean swipeClick(float x, float y) { + return swipe(x, y, x, y, 100l); + } + + /** + * 按压某个区域 time 大于一定时间变成长按 + * @param x + * @param y + * @param time 按压时间 + * @return + */ + public static boolean swipeClick(float x, float y, long time) { + return swipe(x, y, x, y, time); + } + + /** + * 滑动到某个区域 + * @param x + * @param y + * @param tX + * @param tY + * @param time 滑动时间(毫秒) + * @return + */ + public static boolean swipe(float x, float y, float tX, float tY, 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) tX, (int) tY, time), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "swipe"); + } + return false; + } + + // = text = 模拟输入 + + /** + * 输入文本 - 不支持中文 + * @param txt + * @return + */ + public static boolean text(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 + */ + public static boolean keyevent(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 + */ + public static boolean screencap(String path) { + return screencap(path, 0); + } + + /** + * 屏幕截图 + * @param path /sdcard/xxx/x.png + * @param displayId -d display-id 指定截图的显示屏编号(有多显示屏的情况下)默认0 + * @return + */ + public static boolean screencap(String path, 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 + * @return + */ + public static boolean screenrecord(String path) { + return screenrecord(path, null, -1, -1); + } + + /** + * 录制屏幕 (以 mp4 格式保存到 /sdcard) + * @param path + * @param time + * @return + */ + public static boolean screenrecord(String path, int time) { + return screenrecord(path, null, -1, time); + } + + /** + * 录制屏幕 (以 mp4 格式保存到 /sdcard) + * @param path + * @param size + * @param time + * @return + */ + public static boolean screenrecord(String path, String size, 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 + */ + public static boolean screenrecord(String path, String size, int bitRate, int time) { + if (isSpace(path)) return false; + try { + StringBuffer buffer = new StringBuffer(); + buffer.append("screenrecord"); + if (!isSpace(size)) { + buffer.append(" --size " + size); + } + if (bitRate > 0) { + buffer.append(" --bit-rate " + bitRate); + } + if (time > 0) { + buffer.append(" --time-limit " + time); + } + buffer.append(" " + path); + // 执行 shell + ShellUtils.CommandResult result = ShellUtils.execCmd(buffer.toString(), true); + return result.isSuccess2(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "screenrecord"); + } + return false; + } + + /** + * 查看连接过的 WiFi 密码 + * @return + */ + 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 + */ + public static boolean wifiSwitch(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 + */ + public static boolean setSystemTime(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 + */ + public static boolean setSystemTime2(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 + */ + public static boolean setSystemTime2(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 + */ + 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; + } + + /** + * 重启设备 (需要 root 权限) - 并进行特殊的引导模式 (recovery、 Fastboot) + * @param reason 传递给内核来请求特殊的引导模式,如"recovery" + * 重启到 Fastboot 模式 bootloader + */ + public static void reboot(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 + */ + public static boolean reboot2Recovery() { + ShellUtils.CommandResult result = ShellUtils.execCmd("reboot recovery", true); + return result.isSuccess2(); + } + + /** + * 重启引导到 bootloader (需要 root 权限) + * @return + */ + public static boolean reboot2Bootloader() { + ShellUtils.CommandResult result = ShellUtils.execCmd("reboot bootloader", true); + return result.isSuccess2(); + } + + // == 滑动方法 == + + /** + * 发送事件滑动 + * @param x + * @param y + * @param tX + * @param tY + * @param number 循环次数 + * @return + */ + public static void sendEventSlide(float x, float y, float tX, float tY, 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 = tX > x; + // 判断方向(手势是否从上到下) - View 往上滑, 手势操作往下滑 + boolean isTopToBottom = tY > y; + + // 计算差数 + float diffX = isLeftToRight ? (tX - x) : (x - tX); + float diffY = isTopToBottom ? (tY - y) : (y - tY); + + 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 + */ + 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 + */ + 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 + */ + 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: 1080x1920 + // 如果使用命令修改过,那输出可能是: + // Physical size: 1080x1920 + // Override size: 480x1024 + // 表明设备的屏幕分辨率原本是 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 + */ + 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 + */ + 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; + StringBuffer buffer = new StringBuffer(); + String subStr = result.successMsg.replaceAll("\\.", ""); + subStr = subStr.substring(subStr.indexOf("'") + 1, subStr.indexOf("')")); + // 添加数据 + buffer.append(subStr.substring(0, subStr.indexOf("'"))); + // 从指定索引开始 + index = subStr.indexOf("'", buffer.toString().length() + 1); + // 再次裁减 + subStr = subStr.substring(index + 1, subStr.length()); + // 添加数据 + buffer.append(subStr.substring(0, subStr.indexOf("'"))); + // 从指定索引开始 + index = subStr.indexOf("'", buffer.toString().length() + 1); + // 再次裁减 + subStr = subStr.substring(index + 1, subStr.length()); + // 最后进行添加 + buffer.append(subStr.split(SPACE_STR)[0]); + // 返回对应的数据 + return buffer.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[] arys = str.split(SPACE_STR); + return arys[arys.length - 1]; + } + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIMEI"); + } + } + } + return null; + } + + /** + * 获取 ip 地址 + * @return + */ + 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 + */ + 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 + */ + 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 + */ + public static boolean setScreenSize(int width, int height) { + String cmd = "wm size %sx%s"; + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, width, height), true); + return result.isSuccess2(); + } + + /** + * 恢复原分辨率命令 + * @return + */ + public static boolean resetScreen() { + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd("wm size reset", true); + return result.isSuccess2(); + } + + /** + * 设置屏幕密度 + * @param density + * @return + */ + public static boolean setDensity(int density) { + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd("wm density " + density, true); + return result.isSuccess2(); + } + + /** + * 恢复原屏幕密度 + * @return + */ + public static boolean resetDensity() { + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd("wm density reset", true); + return result.isSuccess2(); + } + + /** + * 显示区域 (设置留白边距) + * @param left + * @param top + * @param right + * @param bottom + * @return + */ + public static boolean setOverscan(int left, int top, int right, int bottom) { + String cmd = "wm overscan %s,%s,%s,%s"; + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(cmd, left, top, right, bottom), true); + return result.isSuccess2(); + } + + /** + * 恢复原显示区域 + * @return + */ + public static boolean resetOverscan() { + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd("wm overscan reset", true); + return result.isSuccess2(); + } + + /** + * 获取亮度是否为自动获取(自动调节亮度) + * @return 1 开启、0 未开启、-1 未知 + */ + public static int getScreenBrightnessMode() { + // 执行 shell cmd + 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 + */ + public static boolean setScreenBrightnessMode(boolean isAuto) { + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd("settings put system screen_brightness_mode " + (isAuto ? 1 : 0), true); + return result.isSuccess3(); + } + + /** + * 获取当前亮度值 + * @return + */ + public static String getScreenBrightness() { + // 执行 shell cmd + 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, suc.length()); + } + if (suc.endsWith("\"")) { + suc = suc.substring(0, suc.length() - 1); + } + return suc; + } + return null; + } + + /** + * 更改亮度值(亮度值在0—255之间) + * @param brightness + * @return + */ + public static boolean setScreenBrightness(int brightness) { + if (brightness < 0) { + return false; + } else if (brightness > 255) { + return false; + } + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd("settings put system screen_brightness " + brightness, true); + return result.isSuccess2(); + } + + /** + * 获取自动锁屏休眠时间 (单位毫秒) + * @return + */ + public static String getScreenOffTimeout() { + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd("settings get system screen_off_timeout", true); + if (result.isSuccess3()) { + return result.successMsg; + } + return null; + } + + /** + * 设置自动锁屏休眠时间 (单位毫秒) + * @param time + * @return + * tips: 设置永不休眠 Integer.MAX_VALUE + */ + public static boolean setScreenOffTimeout(long time) { + if (time <= 0) { + return false; + } + // 执行 shell cmd + 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 cmd + 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; + } + + /** + * 修改日期时间选项中通过网络获取时间的状态, 设置是否开启 + * @return + */ + public static boolean setGlobalAutoTime(boolean isOpen) { + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd("settings put global auto_time " + (isOpen ? 1 : 0), true); + return result.isSuccess3(); + } + + /** + * 关闭 USB 调试模式 + * @return + */ + public static boolean disableADB() { + // 执行 shell cmd + 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 cmd + 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 cmd + ShellUtils.CommandResult result = ShellUtils.execCmd(cmds, true); + return result.result; + } + + /** + * 开启无障碍辅助功能 + * @param packageName 包名 + * @param accessibilityServiceName 无障碍服务名 + * @return + */ + public static boolean openAccessibility(String packageName, String accessibilityServiceName) { + if (isSpace(packageName)) return false; + if (isSpace(accessibilityServiceName)) return false; + + String cmd = "settings put secure enabled_accessibility_services %s/%s"; + // 执行 shell cmd + String[] cmds = new String[2]; + cmds[0] = String.format(cmd, packageName, accessibilityServiceName); + cmds[1] = "settings put secure accessibility_enabled 1"; + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd(cmds, true); + return result.isSuccess2(); + } + + /** + * 关闭无障碍辅助功能 + * @param packageName 包名 + * @param accessibilityServiceName 无障碍服务名 + * @return + */ + public static boolean closeAccessibility(String packageName, String accessibilityServiceName) { + if (isSpace(packageName)) return false; + if (isSpace(accessibilityServiceName)) return false; + + String cmd = "settings put secure enabled_accessibility_services %s/%s"; + // 执行 shell cmd + String[] cmds = new String[2]; + cmds[0] = String.format(cmd, packageName, accessibilityServiceName); + cmds[1] = "settings put secure accessibility_enabled 0"; + // 执行 shell cmd + ShellUtils.CommandResult result = ShellUtils.execCmd(cmds, true); + return result.isSuccess2(); + } + + + // == 内部方法 == + + /** + * 检查是否存在某个文件 + * @param file 文件路径 + * @return 是否存在文件 + */ + private static boolean isFileExists(File file) { + return file != null && file.exists(); + } + + /** + * 获取文件 + * @param filePath + * @return + */ + private static File getFileByPath(String filePath) { + return filePath != null ? new File(filePath) : null; + } + + /** + * 判断字符串是否为 null 或全为空白字符 + * @param str 待校验字符串 + * @return + */ + private static boolean isSpace(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/AccessibilityUtils.java b/DevLibUtils/src/main/java/dev/utils/app/AccessibilityUtils.java index 5f81c2ae9a..091b1ca6bc 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/AccessibilityUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/AccessibilityUtils.java @@ -2,7 +2,9 @@ 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; @@ -21,6 +23,7 @@ * 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 + * https://nesscurie.github.io/2017/03/07/2.Android%E8%BE%85%E5%8A%A9%E5%8A%9F%E8%83%BD%E5%A4%A7%E8%87%B4%E8%A7%A3%E6%9E%90,%E9%80%9A%E8%BF%87adb%E8%BF%90%E8%A1%8C%E7%BA%AFjava%E4%BB%A3%E7%A0%81%E6%89%93%E5%BC%80%E5%BA%94%E7%94%A8%E7%9A%84%E8%BE%85%E5%8A%A9%E5%8A%9F%E8%83%BD/ * ==== * AccessibilityService 在 API < 18 的时候使用 AccessibilityService * @@ -32,23 +35,43 @@ private AccessibilityUtils() { // 日志TAG private static final String TAG = AccessibilityUtils.class.getSimpleName(); + // AccessibilityService 对象 + private static AccessibilityService service = null; + + /** + * 获取 AccessibilityService 对象 + * @return + */ + public static AccessibilityService getService() { + return service; + } + + /** + * 设置 AccessibilityService 对象 + * @param service + */ + public static void setService(AccessibilityService service) { + AccessibilityUtils.service = service; + } + + // = /** * 检查是否开启无障碍功能 * @return */ - public static boolean checkAccessibility(){ + public static boolean checkAccessibility() { return checkAccessibility(DevUtils.getContext().getPackageName()); } /** * 检查是否开启无障碍功能 - * @param pkgName + * @param packageName * @return */ - public static boolean checkAccessibility(String pkgName) { + public static boolean checkAccessibility(String packageName) { // 判断辅助功能是否开启 - if (!AccessibilityUtils.isAccessibilitySettingsOn(pkgName)) { + if (!AccessibilityUtils.isAccessibilitySettingsOn(packageName)) { // 引导至辅助功能设置页面 DevUtils.getContext().startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); return false; @@ -58,10 +81,10 @@ public static boolean checkAccessibility(String pkgName) { /** * 判断是否开启无障碍功能 - * @param pkgName + * @param packageName * @return */ - public static boolean isAccessibilitySettingsOn(String pkgName) { + public static boolean isAccessibilitySettingsOn(String packageName) { int accessibilityEnabled = 0; try { accessibilityEnabled = Settings.Secure.getInt(DevUtils.getContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED); @@ -72,7 +95,7 @@ public static boolean isAccessibilitySettingsOn(String pkgName) { try { String services = Settings.Secure.getString(DevUtils.getContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); if (services != null) { - return services.toLowerCase().contains(pkgName.toLowerCase()); + return services.toLowerCase().contains(packageName.toLowerCase()); } } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isAccessibilitySettingsOn - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES"); @@ -84,20 +107,20 @@ public static boolean isAccessibilitySettingsOn(String pkgName) { // == 快捷方法 == /** - * 打印Event 日志 + * 打印 AccessibilityEvent 信息日志 * @param event */ - public static void printAccessibilityEvent(AccessibilityEvent event){ + public static void printAccessibilityEvent(AccessibilityEvent event) { printAccessibilityEvent(event, TAG); } /** - * 打印Event 日志 + * 打印 AccessibilityEvent 信息日志 * @param event * @param tag */ - public static void printAccessibilityEvent(AccessibilityEvent event, String tag){ - if (!LogPrintUtils.isPrintLog()){ + public static void printAccessibilityEvent(AccessibilityEvent event, String tag) { + if (!LogPrintUtils.isPrintLog()) { return; } LogPrintUtils.dTag(tag,"-------------------------------------------------------------"); @@ -149,20 +172,41 @@ public static void printAccessibilityEvent(AccessibilityEvent event, String tag) // === 其他处理 === + /** + * 查找符合条件的节点 + * @param text + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static List findAccessibilityNodeInfosByText(String text) { + return findAccessibilityNodeInfosByText(service, text); + } + /** * 查找符合条件的节点 * @param service * @param text * @return */ - private List findAccessibilityNodeInfosByText(AccessibilityService service, String text) { + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static List findAccessibilityNodeInfosByText(AccessibilityService service, String text) { // 获取根节点 - AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow(); + AccessibilityNodeInfo nodeInfo = service.getRootInActiveWindow(); // 取得当前激活窗体的根节点 - if (accessibilityNodeInfo == null) + if (nodeInfo == null) return null; // 通过文字找到当前的节点 - return accessibilityNodeInfo.findAccessibilityNodeInfosByText(text); + return nodeInfo.findAccessibilityNodeInfosByText(text); + } + + /** + * 查找符合条件的节点 + * @param id + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + public static List findAccessibilityNodeInfosByViewId(String id) { + return findAccessibilityNodeInfosByViewId(service, id); } /** @@ -171,36 +215,49 @@ private List findAccessibilityNodeInfosByText(Accessibili * @param id * @return */ - private List findAccessibilityNodeInfosByViewId(AccessibilityService service, String id) { + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + public static List findAccessibilityNodeInfosByViewId(AccessibilityService service, String id) { // 获取根节点 - AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow(); + AccessibilityNodeInfo nodeInfo = service.getRootInActiveWindow(); // 取得当前激活窗体的根节点 - if (accessibilityNodeInfo == null) + if (nodeInfo == null) return null; // 通过文字找到当前的节点 - return accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id); + return nodeInfo.findAccessibilityNodeInfosByViewId(id); + } + + /** + * 查找符合条件的节点 + * @param text + * @param className + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static List findAccessibilityNodeInfosByText(String text, String className) { + return findAccessibilityNodeInfosByText(service, text, className); } /** * 查找符合条件的节点 * @param service * @param text - * @param claName + * @param className * @return */ - private ArrayList findAccessibilityNodeInfosByText(AccessibilityService service, String text, String claName) { - ArrayList lists = new ArrayList<>(); + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static List findAccessibilityNodeInfosByText(AccessibilityService service, String text, String className) { + List lists = new ArrayList<>(); // 获取根节点 - AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow(); + AccessibilityNodeInfo nodeInfo = service.getRootInActiveWindow(); // 取得当前激活窗体的根节点 - if (accessibilityNodeInfo == null) + if (nodeInfo == null) return lists; // 通过文字找到当前的节点 - List nodes = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text); + List nodes = nodeInfo.findAccessibilityNodeInfosByText(text); for (int i = 0; i < nodes.size(); i++) { AccessibilityNodeInfo node = nodes.get(i); // 判断是否符合的类型 - if (node.getClassName().equals(claName) && node.isEnabled()) { + if (node.getClassName().equals(className) && node.isEnabled()) { // 保存符合条件 lists.add(node); } @@ -208,39 +265,339 @@ private ArrayList findAccessibilityNodeInfosByText(Access return lists; } -// //获取根节点 + // = 操作 = + + /** + * 点击指定的节点 + * @param nodeInfo + */ + public static boolean performClick(AccessibilityNodeInfo nodeInfo) { + if (nodeInfo != null) { + if (nodeInfo.isClickable()) { + return preformAction(nodeInfo, AccessibilityNodeInfo.ACTION_CLICK); + } + } + return false; + } + + /** + * 点击指定的节点 + * 如果当前节点不可点击, 可以尝试往上追溯, 点击父节点, 直到该节点可以点击为止 + * @param nodeInfo + * @param clickParent 如果当前节点不可点击, 是否往上追溯点击父节点, 直到点击成功或没有父节点 + */ + public static void performClick(AccessibilityNodeInfo nodeInfo, boolean clickParent) { + performClick(nodeInfo, clickParent, false); + } + + /** + * 点击指定的节点 + * 如果当前节点不可点击, 可以尝试往上追溯, 点击父节点, 直到该节点可以点击为止 + * @param nodeInfo + * @param clickParent 如果当前节点不可点击, 是否往上追溯点击父节点, 直到点击成功或没有父节点 + * @param clickAll 判断是否点击全部 + */ + public static void performClick(AccessibilityNodeInfo nodeInfo, boolean clickParent, boolean clickAll) { + 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 + */ + public static boolean performLongClick(AccessibilityNodeInfo nodeInfo) { + if (nodeInfo != null) { + if (nodeInfo.isClickable()) { + return preformAction(nodeInfo, AccessibilityNodeInfo.ACTION_LONG_CLICK); + } + } + return false; + } + + /** + * 长按指定的节点 + * 如果当前节点不可点击, 可以尝试往上追溯, 点击父节点, 直到该节点可以点击为止 + * @param nodeInfo + * @param clickParent 如果当前节点不可点击, 是否往上追溯点击父节点, 直到点击成功或没有父节点 + */ + public static void performLongClick(AccessibilityNodeInfo nodeInfo, boolean clickParent) { + performLongClick(nodeInfo, clickParent, false); + } + + /** + * 长按指定的节点 + * 如果当前节点不可点击, 可以尝试往上追溯, 点击父节点, 直到该节点可以点击为止 + * @param nodeInfo + * @param clickParent 如果当前节点不可点击, 是否往上追溯点击父节点, 直到点击成功或没有父节点 + * @param clickAll 判断是否点击全部 + */ + public static void performLongClick(AccessibilityNodeInfo nodeInfo, boolean clickParent, boolean clickAll) { + 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 + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionBack() { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_BACK); + } + + /** + * 触发返回键 + * @param service + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionBack(AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_BACK); + } + + /** + * 触发Home键 + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionHome() { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_HOME); + } + + /** + * 触发Home键 + * @param service + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionHome(AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_HOME); + } + + /** + * 启动长按电源按钮 Dialog + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionPowerDialog() { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_POWER_DIALOG); + } + + /** + * 启动长按电源按钮 Dialog + * @param service + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionPowerDialog(AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_POWER_DIALOG); + } + + /** + * 锁定屏幕 -> 非锁屏 + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionLockScreen() { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN); + } + + /** + * 锁定屏幕 -> 非锁屏 + * @param service + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionLockScreen(AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN); + } + + /** + * 截图 + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionTakeScreenshot() { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT); + } + + /** + * 截图 + * @param service + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionTakeScreenshot(AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT); + } + + /** + * 打开通知栏 + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionNotifications() { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS); + } + + /** + * 打开通知栏 + * @param service + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionNotifications(AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS); + } + + /** + * 最近打开应用列表 + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionRecents() { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_RECENTS); + } + + /** + * 最近打开应用列表 + * @param service + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionRecents(AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_RECENTS); + } + + /** + * 打开设置 + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionQuickSettings() { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS); + } + + /** + * 打开设置 + * @param service + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionQuickSettings(AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS); + } + + /** + * 分屏 + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionSplitScreen() { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN); + } + + /** + * 分屏 + * @param service + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean preformActionSplitScreen(AccessibilityService service) { + return performGlobalAction(service, AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN); + } + + + // == 内部封装 == + + /** + * 模拟对应 Action 操作 + * @param nodeInfo + */ + public static boolean preformAction(AccessibilityNodeInfo nodeInfo, int action) { + if (nodeInfo != null) { + return nodeInfo.performAction(action); + } + return false; + } + + /** + * 模拟全局对应 Action 操作 + * @param service + * @param action + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + public static boolean performGlobalAction(AccessibilityService service, int action) { + if (service != null) { + return service.performGlobalAction(action); + } + return false; + } + +// // 获取根节点 // AccessibilityNodeInfo rootNode = getRootInActiveWindow(); -// //匹配Text获取节点 +// // 匹配Text获取节点 // List list1 = rootNode.findAccessibilityNodeInfosByText("match_text"); -// //匹配id获取节点 +// // 匹配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); - +// // 向上滑动 +// performGlobalAction(service, AccessibilityService.GESTURE_SWIPE_UP); // disableSelf() 禁用当前服务,也就是在服务可以通过该方法停止运行 -// findFoucs(int falg) 查找拥有特定焦点类型的控件 -// getRootInActiveWindow() 如果配置能够获取窗口内容,则会返回当前活动窗口的根结点 +// getSystemService(String name) 获取系统服务 +// onServiceConnected() 系统成功绑定该服务时被触发,也就是当你在设置中开启相应的服务,系统成功的绑定了该服务时会触发,通常我们可以在这里做一些初始化操作 // getSeviceInfo() 获取当前服务的配置信息 +// setServiceInfo(AccessibilityServiceInfo info) 设置当前服务的配置信息 // onAccessibilityEvent(AccessibilityEvent event) 有关AccessibilityEvent事件的回调函数.系统通过sendAccessibiliyEvent()不断的发送AccessibilityEvent到此处 // performGlobalAction(int action) 执行全局操作,比如返回,回到主页,打开最近等操作 -// setServiceInfo(AccessibilityServiceInfo info) 设置当前服务的配置信息 -// getSystemService(String name) 获取系统服务 +// findFoucs(int falg) 查找拥有特定焦点类型的控件 +// getRootInActiveWindow() 如果配置能够获取窗口内容,则会返回当前活动窗口的根结点 // 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 index 45d88a48bd..9fa1a9536b 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ActivityUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ActivityUtils.java @@ -3,7 +3,9 @@ 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; @@ -31,9 +33,31 @@ private ActivityUtils() { // 日志TAG private static final String TAG = ActivityUtils.class.getSimpleName(); + /** + * 返回 View context 所属的 Activity + * @param view + * @return + */ + public static Activity getActivityByView(@NonNull 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.e(TAG, e, "getActivityByView"); + } + } + return null; + } + /** * 判断是否存在指定的Activity - * @param context 上下文 + * @param context * @param packageName 包名 * @param className activity全路径类名 * @return @@ -53,7 +77,7 @@ public static boolean isActivityExists(Context context, String packageName, Stri result = false; } } - } catch (Exception e){ + } catch (Exception e) { result = false; LogPrintUtils.eTag(TAG, e, "isActivityExists"); } @@ -66,11 +90,12 @@ public static boolean isActivityExists(Context context, String packageName, Stri public static void startHomeActivity() { Intent homeIntent = new Intent(Intent.ACTION_MAIN); homeIntent.addCategory(Intent.CATEGORY_HOME); + homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); DevUtils.getContext().startActivity(homeIntent); } /** - * 跳转到桌面 + * 获取 Launcher activity * @return */ public static String getLauncherActivity() { @@ -78,26 +103,28 @@ public static String getLauncherActivity() { } /** - * 跳转到桌面 - * @param pkg + * 获取 launcher activity + * @param packageName * @return */ - public static String getLauncherActivity(@NonNull final String pkg) { + public static String getLauncherActivity(@NonNull final String packageName) { 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; + List lists = pm.queryIntentActivities(intent, 0); + for (ResolveInfo resolveinfo : lists) { + if (resolveinfo != null && resolveinfo.activityInfo != null) { + if (resolveinfo.activityInfo.packageName.equals(packageName)) { + return resolveinfo.activityInfo.name; + } } } return null; } /** - * 返回Activity 对应的图标 + * 返回 Activity 对应的图标 * @param clz * @return */ @@ -106,7 +133,7 @@ public static Drawable getActivityIcon(final Class clz) { } /** - * 返回Activity 对应的图标 + * 返回 Activity 对应的图标 * @param activityName * @return */ @@ -120,7 +147,7 @@ public static Drawable getActivityIcon(final ComponentName activityName) { } /** - * 返回Activity 对应的Logo + * 返回 Activity 对应的Logo * @param clz * @return */ @@ -129,7 +156,7 @@ public static Drawable getActivityLogo(final Class clz) { } /** - * 返回Activity 对应的Logo + * 返回 Activity 对应的Logo * @param activityName * @return */ @@ -142,6 +169,117 @@ public static Drawable getActivityLogo(final ComponentName activityName) { } } + /** + * 获取对应包名应用启动 Activity + * @param packageName + * @return + */ + public static String getActivityToLauncher(String packageName) { + try { + PackageManager pManager = DevUtils.getContext().getPackageManager(); + // 获取对应的PackageInfo + PackageInfo pInfo = pManager.getPackageInfo(packageName, 0); + + if (pInfo == null) { + return null; + } + + // 创建一个类别为 CATEGORY_LAUNCHER 的该包名的 Intent + Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null); + resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER); + resolveIntent.setPackage(pInfo.packageName); + + // 通过 getPackageManager() 的 queryIntentActivities 方法遍历 + List lists = pManager.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 + */ + public static ResolveInfo getLauncherCategoryHomeToResolveInfo() { + try { + final Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_HOME); + return DevUtils.getContext().getPackageManager().resolveActivity(intent, 0); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getLauncherCategoryHomeToResolveInfo"); + } + return null; + } + + /** + * 获取系统桌面信息 -> packageName + * (注:存在多个桌面时且未指定默认桌面时,该方法返回Null,使用时需处理这个情况) + * @return + */ + public static String getLauncherCategoryHomeToPackageName() { + final ResolveInfo res = getLauncherCategoryHomeToResolveInfo(); + if (res != null && res.activityInfo != null) { + // 有多个桌面程序存在,且未指定默认项时 + if (res.activityInfo.packageName.equals("android")) { + return null; + } else { + return res.activityInfo.packageName; + } + } + return null; + } + + /** + * 获取系统桌面信息 -> activityName + * @return + */ + public static String getLauncherCategoryHomeToActivityName() { + final ResolveInfo res = getLauncherCategoryHomeToResolveInfo(); + if (res != null && res.activityInfo != null) { + // 有多个桌面程序存在,且未指定默认项时 + if (res.activityInfo.packageName.equals("android")) { + return null; + } else { + return res.activityInfo.name; + } + } + return null; + } + + /** + * 获取系统桌面信息 -> package/activityName + * @return + */ + public static String getLauncherCategoryHomeToPackageAndName() { + final ResolveInfo res = getLauncherCategoryHomeToResolveInfo(); + if (res != null && res.activityInfo != null) { + // 有多个桌面程序存在,且未指定默认项时 + if (res.activityInfo.packageName.equals("android")) { + return null; + } else { + // 判断是否.开头 + String name = res.activityInfo.name; + if (name != null) { + // 判断是否 . 开头 + if (name.startsWith(".")) { + name = res.activityInfo.packageName + name; + } + return res.activityInfo.packageName + "/" + name; + } + } + } + return null; + } + // == 以下方法使用介绍 == // https://www.cnblogs.com/tianzhijiexian/p/4087917.html // ActivityOptionsCompat.makeScaleUpAnimation(source, startX, startY, startWidth, startHeight) diff --git a/DevLibUtils/src/main/java/dev/utils/app/AlarmUtils.java b/DevLibUtils/src/main/java/dev/utils/app/AlarmUtils.java index 672d675e5a..fbbff68a81 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/AlarmUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/AlarmUtils.java @@ -1,60 +1,161 @@ 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.support.annotation.RequiresApi; + +import dev.utils.LogPrintUtils; /** - * detail: AlarmManager (全局定时器/闹钟)指定时长或以周期形式执行某项操作 - * @author MaTianyu + * detail: AlarmManager (全局定时器/闹钟)指定时长或以周期形式执行某项操作 + * Created by Ttt * https://www.cnblogs.com/zyw-205520/p/4040923.html */ public final class AlarmUtils { - private AlarmUtils(){ + private AlarmUtils() { } + // 日志 TAG + private static final String TAG = AlarmUtils.class.getSimpleName(); + /** * 开启定时器 + * @param context + * @param triggerAtMillis 执行时间 + * @param pendingIntent 响应动作 */ - @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); + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void startAlarmIntent(Context context, long triggerAtMillis, PendingIntent pendingIntent) { + try { + AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + manager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent); + } else { + manager.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startAlarmIntent"); + } } /** * 关闭定时器 + * @param context + * @param pendingIntent */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) + @RequiresApi(Build.VERSION_CODES.CUPCAKE) public static void stopAlarmIntent(Context context, PendingIntent pendingIntent) { - AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - manager.cancel(pendingIntent); + try { + AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + manager.cancel(pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopAlarmIntent"); + } } /** * 开启轮询服务 + * @param context + * @param triggerAtMillis + * @param cls + * @param action */ - @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); + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void startAlarmService(Context context, long triggerAtMillis, Class cls, String action) { + try { + Intent intent = new Intent(context, cls); + intent.setAction(action); + PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + startAlarmIntent(context, triggerAtMillis, pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "startAlarmService"); + } } /** - * 停止启轮询服务 + * 停止轮询服务 + * @param context + * @param cls + * @param action */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) + @RequiresApi(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); + try { + Intent intent = new Intent(context, cls); + intent.setAction(action); + PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + stopAlarmIntent(context, pendingIntent); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "stopAlarmService"); + } + } + + /** + * 开启轮询广播 + * @param context + * @param triggerAtMillis + * @param intent + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void startAlarmBroadcast(Context context, long triggerAtMillis, 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"); + } + } + + /** + * 停止轮询广播 + * @param context + * @param intent + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void stopAlarmBroadcast(Context context, 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 + * @param context + * @param triggerAtMillis + * @param intent + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void startAlarmActivity(Context context, long triggerAtMillis, 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 + * @param intent + */ + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static void stopAlarmActivity(Context context, 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"); + } } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/AnalysisRecordUtils.java b/DevLibUtils/src/main/java/dev/utils/app/AnalysisRecordUtils.java index 7488589a10..f2e36bd7f0 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/AnalysisRecordUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/AnalysisRecordUtils.java @@ -17,6 +17,7 @@ import java.lang.reflect.Field; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashMap; @@ -36,15 +37,19 @@ private AnalysisRecordUtils() { // 日志TAG private static final String TAG = AnalysisRecordUtils.class.getSimpleName(); - - // 上下文 + // Context private static Context sContext; // 日志文件夹名字(目录名) private static String logFolderName = "LogRecord"; // 日志存储路径 private static String logStoragePath; // 是否处理保存 - private static boolean isHandler = true; + private static boolean handler = true; + // 判断是否加空格 + private static boolean appendSpace = true; + // 正则 - 空格 + private static final String SPACE_STR = "\\s"; + /** * 初始化操作 @@ -59,7 +64,7 @@ public static void init(Context context) { // 初始化 App 信息 getAppInfo(); // 如果为null, 才设置 - if (TextUtils.isEmpty(logStoragePath)){ + if (TextUtils.isEmpty(logStoragePath)) { // 获取根路径 logStoragePath = FileInfo.getDiskCacheDir(sContext); } @@ -75,10 +80,13 @@ public static void init(Context context) { */ public static String record(FileInfo fileInfo, String... args) { // 如果不处理, 则直接跳过 - if (!isHandler){ + if (!handler) { return "record not handler"; } if (fileInfo != null) { + if (!fileInfo.isHandler()) { + return "file record not handler"; + } if (args != null && args.length != 0) { return saveLogRecord(fileInfo, args); } @@ -94,15 +102,31 @@ public static String record(FileInfo fileInfo, String... args) { * @return */ public static boolean isHandler() { - return isHandler; + return handler; } /** * 设置是否处理日志记录 - * @param isHandler + * @param handler + */ + public static void setHandler(boolean handler) { + AnalysisRecordUtils.handler = handler; + } + + /** + * 是否追加空格 + * @return */ - public static void setHandler(boolean isHandler) { - AnalysisRecordUtils.isHandler = isHandler; + public static boolean isAppendSpace() { + return appendSpace; + } + + /** + * 设置是否追加空格 + * @param appendSpace + */ + public static void setAppendSpace(boolean appendSpace) { + AnalysisRecordUtils.appendSpace = appendSpace; } /** @@ -146,15 +170,15 @@ public static void setLogStoragePath(String logStoragePath) { */ private static String saveLogRecord(FileInfo fileInfo, String... args) { // 如果不处理, 则直接跳过 - if (!isHandler){ + if (!handler) { return "record not handler"; } // 文件信息为null, 则不处理 - if (fileInfo == null){ + if (fileInfo == null) { return "info is null"; } // 如果文件地址为null, 则不处理 - if (TextUtils.isEmpty(fileInfo.getFileName())){ + if (TextUtils.isEmpty(fileInfo.getFileName())) { // 文件名为null return "fileName is null"; } @@ -226,6 +250,9 @@ private static String saveLogRecord(FileInfo fileInfo, String... args) { * @return */ private static String splitLog(String... args) { + // 判断是否追加空格 + boolean isSpace = appendSpace; + // = StringBuffer sBuffer = new StringBuffer(); // 增加换行 sBuffer.append(NEW_LINE_STR); @@ -236,6 +263,9 @@ private static String splitLog(String... args) { sBuffer.append(" => "); // 循环追加内容 for (int i = 0, c = args.length; i < c; i++) { + if (isSpace) { // 判断是否追加空格 + sBuffer.append(SPACE_STR); + } // 追加保存内容 sBuffer.append(args[i]); } @@ -244,12 +274,12 @@ private static String splitLog(String... args) { // === 设备信息统计 ==== - // app 信息 + // 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 Map DEVICE_INFO_MAPS = new HashMap<>(); // 换行字符串 private static final String NEW_LINE_STR = System.getProperty("line.separator"); @@ -264,7 +294,7 @@ private static String getDeviceInfo() { // 获取设备信息 getDeviceInfo(DEVICE_INFO_MAPS); // 转换设备信息 - handleDeviceInfo("获取设备信息失败"); + handlerDeviceInfo("获取设备信息失败"); // 返回设备信息 return DEVICE_INFO_STR; } @@ -273,17 +303,34 @@ private static String getDeviceInfo() { * 获取设备信息 * @param dInfoMaps 传入设备信息传出HashMap */ - private static void getDeviceInfo(HashMap dInfoMaps) { + private static void getDeviceInfo(Map dInfoMaps) { // 获取设备信息类的所有申明的字段,即包括public、private和proteced, 但是不包括父类的申明字段。 Field[] fields = Build.class.getDeclaredFields(); // 遍历字段 for (Field field : fields) { try { - // 取消java的权限控制检查 + // 取消 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] + dInfoMaps.put(field.getName(), Arrays.toString((String[]) object)); + } + continue; + } + } catch (Exception e) { + } + } + // 获取类型对应字段的数据, 并保存 dInfoMaps.put(field.getName(), field.get(null).toString()); } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); } } } @@ -292,7 +339,7 @@ private static void getDeviceInfo(HashMap dInfoMaps) { * 处理设备信息 * @param eHint 错误提示,如获取设备信息失败 */ - private static String handleDeviceInfo(String eHint) { + private static String handlerDeviceInfo(String eHint) { try { // 如果不为null,则直接返回之前的信息 if (!TextUtils.isEmpty(DEVICE_INFO_STR)) { @@ -319,13 +366,13 @@ private static String handleDeviceInfo(String eHint) { // 返回设备信息 return DEVICE_INFO_STR; } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleDeviceInfo"); + LogPrintUtils.eTag(TAG, e, "handlerDeviceInfo"); } return eHint; } /** - * 获取app 信息 + * 获取 App 信息 * @return */ private static String getAppInfo() { @@ -393,39 +440,63 @@ public static class FileInfo { // 文件记录间隔时间 如: HH private @TIME int fileIntervalTime = DEFAULT; + // 是否处理日志记录 + private boolean handler = true; + // = 构造函数 = - private FileInfo(String storagePath, String folderName, String fileName, String fileFunction, @TIME int fileIntervalTime) { + private FileInfo(String storagePath, String folderName, String fileName, String fileFunction, @TIME int fileIntervalTime, boolean handler) { this.storagePath = storagePath; this.folderName = folderName; this.fileName = fileName; this.fileFunction = fileFunction; this.fileIntervalTime = fileIntervalTime; + this.handler = handler; } // = get/set 方法 = + /** + * 获取存储路径 + * @return + */ public String getStoragePath() { - if (TextUtils.isEmpty(storagePath)){ + 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; } - public String getFolderName(){ - if (TextUtils.isEmpty(folderName)){ + /** + * 获取日志文件夹名 + * @return + */ + public String getFolderName() { + if (TextUtils.isEmpty(folderName)) { return folderName = getLogFolderName(); } return folderName; @@ -438,7 +509,7 @@ public String getFolderName(){ * @return */ public static FileInfo obtain(String fileName, String fileFunction) { - return new FileInfo(null, null, fileName, fileFunction, DEFAULT); + return new FileInfo(null, null, fileName, fileFunction, DEFAULT, true); } /** @@ -449,7 +520,7 @@ public static FileInfo obtain(String fileName, String fileFunction) { * @return */ public static FileInfo obtain(String folderName, String fileName, String fileFunction) { - return new FileInfo(null, folderName, fileName, fileFunction, DEFAULT); + return new FileInfo(null, folderName, fileName, fileFunction, DEFAULT, true); } /** @@ -461,7 +532,7 @@ public static FileInfo obtain(String folderName, String fileName, String fileFun * @return */ public static FileInfo obtain(String storagePath, String folderName, String fileName, String fileFunction) { - return new FileInfo(storagePath, folderName, fileName, fileFunction, DEFAULT); + return new FileInfo(storagePath, folderName, fileName, fileFunction, DEFAULT, true); } // == @@ -474,7 +545,7 @@ public static FileInfo obtain(String storagePath, String folderName, String file * @return */ public static FileInfo obtain(String fileName, String fileFunction, @TIME int fileIntervalTime) { - return new FileInfo(null, null, fileName, fileFunction, fileIntervalTime); + return new FileInfo(null, null, fileName, fileFunction, fileIntervalTime, true); } /** @@ -486,7 +557,7 @@ public static FileInfo obtain(String fileName, String fileFunction, @TIME int fi * @return */ public static FileInfo obtain(String folderName, String fileName, String fileFunction, @TIME int fileIntervalTime) { - return new FileInfo(null, folderName, fileName, fileFunction, fileIntervalTime); + return new FileInfo(null, folderName, fileName, fileFunction, fileIntervalTime, true); } /** @@ -499,7 +570,21 @@ public static FileInfo obtain(String folderName, String fileName, String fileFun * @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 new FileInfo(storagePath, folderName, fileName, fileFunction, fileIntervalTime, true); + } + + /** + * 获取记录分析文件信息 + * @param storagePath + * @param folderName + * @param fileName + * @param fileFunction + * @param fileIntervalTime + * @param isHandler + * @return + */ + public static FileInfo obtain(String storagePath, String folderName, String fileName, String fileFunction, @TIME int fileIntervalTime, boolean isHandler) { + return new FileInfo(storagePath, folderName, fileName, fileFunction, fileIntervalTime, isHandler); } // = 内部处理方法 = @@ -532,14 +617,14 @@ public String getIntervalTimeFolder() { // 小时格式 String hh_Foramt = getDateNow("HH"); // 判断属于小时格式 - if (iTime == 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){ + 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 { // 属于秒 @@ -554,6 +639,24 @@ public String getIntervalTimeFolder() { return "/Unknown/"; } + /** + * 是否处理日志记录 + * @return + */ + public boolean isHandler() { + return handler; + } + + /** + * 设置是否处理日志记录 + * @param handler + * @return + */ + public FileInfo setHandler(boolean handler) { + this.handler = handler; + return this; + } + /** * 获取当前日期的字符串 * @param format 日期格式,譬如:HH, mm, ss @@ -582,7 +685,7 @@ private String getSavePath(String storagePath, String fPath) { File file = new File(storagePath, fPath); // 防止不存在目录文件,自动创建 createFolder(file); - // 返回头像地址 + // 返回缓存地址 return file.getAbsolutePath(); } diff --git a/DevLibUtils/src/main/java/dev/utils/app/AppCommonUtils.java b/DevLibUtils/src/main/java/dev/utils/app/AppCommonUtils.java index 7b7231c51d..e6c31dd27d 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/AppCommonUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/AppCommonUtils.java @@ -1,24 +1,34 @@ package dev.utils.app; +import android.Manifest; import android.os.Build; +import android.support.annotation.RequiresPermission; import java.util.Random; import java.util.UUID; +import dev.DevUtils; +import dev.utils.LogPrintUtils; + /** * detail: App通用工具类 * Created by Ttt */ public final class AppCommonUtils { - private AppCommonUtils(){ + private AppCommonUtils() { } + // 日志 TAG + private static final String TAG = AppCommonUtils.class.getSimpleName(); + /** * 获取设备唯一id + * * @return */ - public String getUUID(){ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public static String getUUID() { return PhoneUtils.getUUID(); } @@ -26,7 +36,7 @@ public String getUUID(){ * 获取随机数 唯一id * @return */ - public String getRandomUUID(){ + public static String getRandomUUID() { // 获取随机数 String random1 = (900000 + new Random().nextInt(10000)) + ""; // 获取随机数 @@ -39,6 +49,38 @@ public String getRandomUUID(){ return randomUUID.toString(); } + /** + * 获取 R.string 资源的格式化字符串 + * @param resId + * @param objs + */ + public static String getFormatRes(int resId, Object... objs) { + return getFormatRes(false, resId, objs); + } + + /** + * 获取 R.string 资源的格式化字符串 + * @param errorMsg 是否设置异常信息 + * @param resId + * @param objs + */ + public static String getFormatRes(boolean errorMsg, int resId, 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; + } + // == 版本判断处理 == /** @@ -105,6 +147,22 @@ 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版本及以上 @@ -125,7 +183,7 @@ public static boolean isLollipop() { * 是否在6.0版本及以上 * @return 是否在6.0版本及以上 */ - public static boolean isM(){ + public static boolean isM() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; } @@ -133,34 +191,34 @@ public static boolean isM(){ * 是否在7.0版本及以上 * @return 是否在7.0版本及以上 */ - public static boolean isN(){ - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; + 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; + 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; + public static boolean isO() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; } /** - * 转换SDK版本 + * 转换SDK版本 (convertSDKVersion(14) => Android 4.0.0-2) * @param sdkVersion * @return */ - public static String convertSDKVersion(int sdkVersion){ + public static String convertSDKVersion(int sdkVersion) { // https://www.cnblogs.com/maogefff/p/7819076.html - switch (sdkVersion){ + switch (sdkVersion) { case 1: return "Android 1.0"; case 2: diff --git a/DevLibUtils/src/main/java/dev/utils/app/AppUtils.java b/DevLibUtils/src/main/java/dev/utils/app/AppUtils.java index ae1b009e28..e5c8eb0c00 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/AppUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/AppUtils.java @@ -1,5 +1,6 @@ package dev.utils.app; +import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityManager; @@ -21,6 +22,7 @@ import android.support.annotation.DrawableRes; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; +import android.support.annotation.RequiresPermission; import android.support.annotation.StringRes; import android.support.v4.content.ContextCompat; import android.util.DisplayMetrics; @@ -37,7 +39,7 @@ import dev.utils.LogPrintUtils; /** - * detail: App(Android 工具类) + * detail: App(Android 工具类) * Created by Ttt */ public final class AppUtils { @@ -49,7 +51,7 @@ private AppUtils() { private static final String TAG = AppUtils.class.getSimpleName(); /** - * 通过上下文获取 WindowManager + * 获取 WindowManager * @return */ public static WindowManager getWindowManager() { @@ -62,7 +64,7 @@ public static WindowManager getWindowManager() { } // /** -// * 通过上下文获取 DisplayMetrics (获取关于显示的通用信息,如显示大小,分辨率和字体) +// * 获取 DisplayMetrics // * @return // */ // public static DisplayMetrics getDisplayMetrics() { @@ -79,30 +81,14 @@ public static WindowManager getWindowManager() { // 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 + * 获取 View * @param resource * @return */ - public static View getView(@LayoutRes int resource){ + public static View getView(@LayoutRes int resource) { return getView(DevUtils.getContext(), resource); } @@ -112,125 +98,186 @@ public static View getView(@LayoutRes int resource){ * @param resource * @return */ - public static View getView(Context context, @LayoutRes int resource){ + 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getView"); } return null; } + /** + * 获取 Resources + * @return + */ public static Resources getResources() { try { return DevUtils.getContext().getResources(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getResources"); } return null; } + /** + * 获取 String + * @return + */ public static String getString(@StringRes int id) { try { return DevUtils.getContext().getResources().getString(id); - } catch (Exception e){ + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getString"); + } + return null; + } + + /** + * 获取 String + * @return + */ + public static String getString(@StringRes int id, Object... formatArgs) { + try { + return DevUtils.getContext().getResources().getString(id, formatArgs); + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getString"); } return null; } + /** + * 获取 Resources.Theme + * @return + */ public static Resources.Theme getTheme() { try { return DevUtils.getContext().getTheme(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getTheme"); } return null; } + /** + * 获取 AssetManager + * @return + */ public static AssetManager getAssets() { try { return DevUtils.getContext().getAssets(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getAssets"); } return null; } + /** + * 获取 Drawable + * @return + */ public static Drawable getDrawable(@DrawableRes int id) { try { return ContextCompat.getDrawable(DevUtils.getContext(), id); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getDrawable"); } return null; } - public static int getColor( @ColorRes int id) { + /** + * 获取 Color + * @return + */ + public static int getColor(@ColorRes int id) { try { return ContextCompat.getColor(DevUtils.getContext(), id); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getColor"); } return -1; } + /** + * 获取 ColorStateList + * @return + */ public static ColorStateList getColorStateList(int id) { try { return ContextCompat.getColorStateList(DevUtils.getContext(), id); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getColorStateList"); } return null; } - public static T getSystemService(String name){ + /** + * 获取 SystemService + * @return + */ + public static T getSystemService(String name) { try { return (T) DevUtils.getContext().getSystemService(name); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getSystemService"); } return null; } - public static PackageManager getPackageManager(){ + /** + * 获取 PackageManager + * @return + */ + public static PackageManager getPackageManager() { try { return DevUtils.getContext().getPackageManager(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getPackageManager"); } return null; } + /** + * 获取 Configuration + * @return + */ public static Configuration getConfiguration() { try { return DevUtils.getContext().getResources().getConfiguration(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getConfiguration"); } return null; } + /** + * 获取 DisplayMetrics + * @return + */ public static DisplayMetrics getDisplayMetrics() { try { return DevUtils.getContext().getResources().getDisplayMetrics(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getDisplayMetrics"); } return null; } + /** + * 获取 ContentResolver + * @return + */ public static ContentResolver getContentResolver() { try { return DevUtils.getContext().getContentResolver(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getContentResolver"); } return null; } /** - * 获取app的图标 + * 获取 App 的图标 * @return */ public static Drawable getAppIcon() { @@ -238,7 +285,7 @@ public static Drawable getAppIcon() { } /** - * 获取app的图标 + * 获取 App 的图标 * @param packageName * @return */ @@ -255,7 +302,7 @@ public static Drawable getAppIcon(final String packageName) { } /** - * 获取app 包名 + * 获取 App 包名 * @return */ public static String getAppPackageName() { @@ -263,7 +310,7 @@ public static String getAppPackageName() { } /** - * 获取app 名 + * 获取 App 名 * @return */ public static String getAppName() { @@ -271,7 +318,7 @@ public static String getAppName() { } /** - * 获取app 名 + * 获取 App 名 * @param packageName * @return */ @@ -288,7 +335,7 @@ public static String getAppName(final String packageName) { } /** - * 获取app版本名 - 对外显示 + * 获取 App 版本名 - 对外显示 * @return */ public static String getAppVersionName() { @@ -296,8 +343,8 @@ public static String getAppVersionName() { } /** - * 获取app版本名 - 对外显示 - * @param packageName The name of the package. + * 获取 App 版本名 - 对外显示 + * @param packageName * @return */ public static String getAppVersionName(final String packageName) { @@ -312,7 +359,7 @@ public static String getAppVersionName(final String packageName) { } /** - * 获取app版本号 - 内部判断 + * 获取 App 版本号 - 内部判断 * @return */ public static int getAppVersionCode() { @@ -320,8 +367,8 @@ public static int getAppVersionCode() { } /** - * 获取app版本号 - 内部判断 - * @param packageName The name of the package. + * 获取 App 版本号 - 内部判断 + * @param packageName * @return */ public static int getAppVersionCode(final String packageName) { @@ -338,16 +385,16 @@ public static int getAppVersionCode(final String packageName) { // = /** - * 对内设置指定语言 (app 多语言,单独改变app语言) + * 对内设置指定语言 (App 多语言,单独改变 App 语言) * @param locale */ public static void setLanguage(Locale locale) { try { - // 获得res资源对象 + // 获取res资源对象 Resources resources = DevUtils.getContext().getResources(); - // 获得设置对象 + // 获取设置对象 Configuration config = resources.getConfiguration(); - // 获得屏幕参数:主要是分辨率,像素等。 + // 获取屏幕参数:主要是分辨率,像素等。 DisplayMetrics dm = resources.getDisplayMetrics(); // 语言 config.locale = locale; @@ -361,7 +408,7 @@ public static void setLanguage(Locale locale) { // = /** - * 安装 App(支持 8.0)的意图 + * 安装 App(支持 8.0)的意图 * @param filePath The path of file. * @param authority 7.0 及以上安装需要传入清单文件中的}的 authorities 属性 * @return 是否可以跳转 @@ -380,7 +427,7 @@ 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "installApp"); return false; } @@ -411,7 +458,7 @@ public static boolean installApp(final Activity activity, final File file, final if (!isFileExists(file)) return false; try { activity.startActivityForResult(IntentUtils.getInstallAppIntent(file, authority), requestCode); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "installApp"); return false; } @@ -419,33 +466,57 @@ public static boolean installApp(final Activity activity, final File file, final } /** - * 静默安装app + * 静默安装 App * @param filePath * @return */ public static boolean installAppSilent(final String filePath) { - return installAppSilent(getFileByPath(filePath)); + return installAppSilent(filePath, null); } /** - * 静默安装app - * - * @param file The file. - * @return true : success, false : fail + * 静默安装 App + * @param file + * @return */ public static boolean installAppSilent(final File file) { + return installAppSilent(file, null); + } + + /** + * 静默安装 App + * @param filePath + * @param params + * @return + */ + public static boolean installAppSilent(final String filePath, final String params) { + return installAppSilent(getFileByPath(filePath), params, isDeviceRooted()); + } + + /** + * 静默安装 App + * @param file + * @param params + * @return + */ + public static boolean installAppSilent(final File file, final String params) { + return installAppSilent(file, params, isDeviceRooted()); + } + + /** + * 静默安装 App + * + * @param file + * @param params + * @param isRooted + * @return + */ + public static boolean installAppSilent(final File file, final String params, final boolean isRooted) { 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"); - } + 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"); } /** @@ -458,7 +529,7 @@ public static boolean uninstallApp(final String packageName) { if (isSpace(packageName)) return false; try { DevUtils.getContext().startActivity(IntentUtils.getUninstallAppIntent(packageName, true)); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "uninstallApp"); return false; } @@ -475,7 +546,7 @@ public static boolean uninstallApp(final Activity activity, final String package if (isSpace(packageName)) return false; try { activity.startActivityForResult(IntentUtils.getUninstallAppIntent(packageName), requestCode); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "uninstallApp"); return false; } @@ -487,28 +558,32 @@ public static boolean uninstallApp(final Activity activity, final String package * @param packageName * @return */ - public static boolean uninstallAppSilent(final String packageName) { - return uninstallAppSilent(packageName, false); + public static boolean uninstallAppSilent(String packageName) { + return uninstallAppSilent(packageName, false, isDeviceRooted()); } /** - * 静默卸载 app + * 静默卸载 App + * @param packageName + * @param isKeepData + * @return + */ + public static boolean uninstallAppSilent(String packageName, boolean isKeepData) { + return uninstallAppSilent(packageName, isKeepData, isDeviceRooted()); + } + + /** + * 静默卸载 App * @param packageName * @param isKeepData + * @param isRooted * @return */ - public static boolean uninstallAppSilent(final String packageName, final boolean isKeepData) { + public static boolean uninstallAppSilent(String packageName, boolean isKeepData, boolean isRooted) { 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"); - } + 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"); } /** @@ -524,7 +599,7 @@ public static boolean isAppInstalled(@NonNull final String action, @NonNull fin PackageManager pm = DevUtils.getContext().getPackageManager(); ResolveInfo info = pm.resolveActivity(intent, 0); return info != null; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isAppInstalled"); return false; } @@ -540,18 +615,17 @@ public static boolean isAppInstalled(@NonNull final String packageName) { } /** - * 判断是否安装指定包名的APP - * @param context 上下文 + * 判断是否安装指定包名的 App * @param packageName 包路径 * @return */ @SuppressWarnings("unused") - public static boolean isInstalledApp(Context context, String packageName) { + public static boolean isInstalledApp(String packageName) { if (packageName == null || "".equals(packageName)) { return false; } try { - ApplicationInfo info = context.getPackageManager().getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); + ApplicationInfo info = DevUtils.getContext().getPackageManager().getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); return true; } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isInstalledApp"); @@ -560,7 +634,7 @@ public static boolean isInstalledApp(Context context, String packageName) { } /** - * 判断是否存在root 权限 + * 判断是否存在 root 权限 * @return true : yes, false : no */ public static boolean isAppRoot() { @@ -570,7 +644,7 @@ public static boolean isAppRoot() { } /** - * 判断是否app 是否debug模式 + * 判断是否 App 是否debug模式 * @return true : yes, false : no */ public static boolean isAppDebug() { @@ -578,7 +652,7 @@ public static boolean isAppDebug() { } /** - * 判断是否app 是否debug模式 + * 判断是否 App 是否debug模式 * @param packageName * @return true : yes, false : no */ @@ -594,7 +668,7 @@ public static boolean isAppDebug(final String packageName) { } /** - * 判断app 是否系统app + * 判断 App 是否系统app * @return true : yes, false : no */ public static boolean isAppSystem() { @@ -602,7 +676,7 @@ public static boolean isAppSystem() { } /** - * 判断app 是否系统app + * 判断 App 是否系统app * @param packageName * @return true : yes, false : no */ @@ -618,7 +692,7 @@ public static boolean isAppSystem(final String packageName) { } /** - * 判断app 是否在前台 + * 判断 App 是否在前台 * @return true : yes, false : no */ public static boolean isAppForeground() { @@ -631,24 +705,25 @@ public static boolean isAppForeground() { return aInfo.processName.equals(DevUtils.getContext().getPackageName()); } } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isAppForeground"); } return false; } /** - * 判断app 是否在前台 - * => 属于系统权限 + * 判断 App 是否在前台 + * * @param packageName * @return */ + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) public static boolean isAppForeground(@NonNull final String packageName) { return !isSpace(packageName) && packageName.equals(ProcessUtils.getForegroundProcessName()); } /** - * 打开app + * 打开 App * @param packageName */ public static boolean launchApp(final String packageName) { @@ -656,15 +731,15 @@ public static boolean launchApp(final String packageName) { try { DevUtils.getContext().startActivity(IntentUtils.getLaunchAppIntent(packageName, true)); return true; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "launchApp"); } return false; } /** - * 打开app, 并且回传 - * @param activity The activity. + * 打开 App, 并且回传 + * @param activity * @param packageName * @param requestCode */ @@ -673,19 +748,22 @@ public static boolean launchApp(final Activity activity, final String packageNam try { activity.startActivityForResult(IntentUtils.getLaunchAppIntent(packageName), requestCode); return true; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "launchApp"); } return false; } - /** 跳转到 专门的APP 设置详情页面 */ + /** + * 跳转到 专门的 App 设置详情页面 + * @return + */ public static boolean launchAppDetailsSettings() { return launchAppDetailsSettings(DevUtils.getContext().getPackageName()); } /** - * 跳转到 专门的APP 设置详情页面 + * 跳转到 专门的 App 设置详情页面 * @param packageName */ public static boolean launchAppDetailsSettings(final String packageName) { @@ -693,14 +771,14 @@ public static boolean launchAppDetailsSettings(final String packageName) { try { DevUtils.getContext().startActivity(IntentUtils.getLaunchAppDetailsSettingsIntent(packageName, true)); return true; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "launchAppDetailsSettings"); } return false; } /** - * 跳转到 专门的APP 应用商城详情页面 + * 跳转到 专门的 App 应用商城详情页面 * @param marketPkg * @return */ @@ -709,7 +787,7 @@ public static boolean launchAppDetails(final String marketPkg) { } /** - * 跳转到 专门的APP 应用商城详情页面 + * 跳转到 专门的 App 应用商城详情页面 * @param marketPkg * @param marketPkg * @return @@ -719,14 +797,14 @@ public static boolean launchAppDetails(final String packageName, final String ma try { DevUtils.getContext().startActivity(IntentUtils.getlaunchAppDetailIntent(packageName, marketPkg, true)); return true; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "launchAppDetails"); } return false; } /** - * 获取app 路径 /data/data/包名/.apk + * 获取 App 路径 /data/data/包名/.apk * @return */ public static String getAppPath() { @@ -734,7 +812,7 @@ public static String getAppPath() { } /** - * 获取app 路径 /data/data/包名/.apk + * 获取 App 路径 /data/data/包名/.apk * @param packageName * @return */ @@ -753,7 +831,7 @@ public static String getAppPath(final String packageName) { // == /** - * 获取app 签名 + * 获取 App 签名 * @return */ public static Signature[] getAppSignature() { @@ -761,7 +839,7 @@ public static Signature[] getAppSignature() { } /** - * 获取app 签名 + * 获取 App 签名 * @param packageName * @return */ @@ -779,7 +857,7 @@ public static Signature[] getAppSignature(final String packageName) { } /** - * 获取 app sha1值 + * 获取 App sha1值 * @return */ public static String getAppSignatureSHA1() { @@ -787,16 +865,55 @@ public static String getAppSignatureSHA1() { } /** - * 获取 app sha1值 + * 获取 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"); + return getAppSignatureHash(packageName, "SHA1"); } + /** + * 获取应用签名的的 SHA256 值 + * @return + */ + public static String getAppSignatureSHA256() { + return getAppSignatureSHA256(DevUtils.getContext().getPackageName()); + } + + /** + * 获取应用签名的的 SHA256 值 + * @param packageName + * @return + */ + public static String getAppSignatureSHA256(final String packageName) { + return getAppSignatureHash(packageName, "SHA256"); + } + + /** + * 获取应用签名的的 MD5 值 + * @return + */ + public static String getAppSignatureMD5() { + return getAppSignatureMD5(DevUtils.getContext().getPackageName()); + } + + /** + * 获取应用签名的的 MD5 值 + * @param packageName + * @return + */ + public static String getAppSignatureMD5(final String packageName) { + return getAppSignatureHash(packageName, "MD5"); + } + + private static String getAppSignatureHash(final String packageName, final String algorithm) { + if (isSpace(packageName)) return ""; + Signature[] signature = getAppSignature(packageName); + if (signature == null || signature.length <= 0) return ""; + return bytes2HexString(hashTemplate(signature[0].toByteArray(), algorithm)).replaceAll("(?<=[0-9A-F]{2})[0-9A-F]{2}", ":$0"); + } + // == /** @@ -804,7 +921,7 @@ public static String getAppSignatureSHA1(@NonNull final String packageName) { * @param file 文件路径 * @return 是否存在文件 */ - private static boolean isFileExists(final File file){ + private static boolean isFileExists(final File file) { return file != null && file.exists(); } @@ -813,7 +930,7 @@ private static boolean isFileExists(final File file){ * @param filePath * @return */ - private static File getFileByPath(final String filePath){ + private static File getFileByPath(final String filePath) { return filePath != null ? new File(filePath) : null; } @@ -851,31 +968,23 @@ private static boolean isDeviceRooted() { // 十六进制大写转换 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; - } - } + /** + * 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 md = MessageDigest.getInstance(algorithm); + md.update(data); + return md.digest(); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "hashTemplate"); + return null; + } + } /** * 数据加密转换16进制 @@ -898,18 +1007,17 @@ private static String bytes2HexString(final byte[] bytes) { /** * 启动本地应用打开PDF - * @param context 上下文 * @param filePath 文件路径 */ - public static boolean openPDFFile(Context context, String filePath) { + public static boolean openPDFFile(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); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + DevUtils.getContext().startActivity(intent); return true; } } catch (Exception e) { @@ -919,11 +1027,10 @@ public static boolean openPDFFile(Context context, String filePath) { } /** - * 启动本地应用打开PDF - * @param context 上下文 + * 启动本地应用打开Word * @param filePath 文件路径 */ - public static boolean openWordFile(Context context, String filePath) { + public static boolean openWordFile(String filePath) { try { File file = new File(filePath); if (file.exists()) { @@ -932,7 +1039,7 @@ public static boolean openWordFile(Context context, String filePath) { intent.addCategory("android.intent.category.DEFAULT"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(path, "application/msword"); - context.startActivity(intent); + DevUtils.getContext().startActivity(intent); return true; } } catch (Exception e) { @@ -943,10 +1050,9 @@ public static boolean openWordFile(Context context, String filePath) { /** * 调用WPS打开office文档 - * @param context 上下文 * @param filePath 文件路径 */ - public static boolean openOfficeByWPS(Context context, String filePath) { + public static boolean openOfficeByWPS(String filePath) { try { // 检查是否安装WPS String wpsPackageEng = "cn.wps.moffice_eng";// 普通版与英文版一样 @@ -960,7 +1066,7 @@ public static boolean openOfficeByWPS(Context context, String filePath) { Uri uri = Uri.fromFile(new File(filePath)); intent.setData(uri); - context.startActivity(intent); + DevUtils.getContext().startActivity(intent); return true; } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "openOfficeByWPS"); diff --git a/DevLibUtils/src/main/java/dev/utils/app/BarUtils.java b/DevLibUtils/src/main/java/dev/utils/app/BarUtils.java index 53b50df982..196dead857 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/BarUtils.java +++ b/DevLibUtils/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; @@ -23,6 +29,8 @@ import dev.DevUtils; +import static android.Manifest.permission.EXPAND_STATUS_BAR; + /** * detail: 状态栏相关工具类 * Created by Ttt @@ -35,21 +43,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 */ 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 The activity. * @param isVisible True to set status bar visible, false otherwise. */ public static void setStatusBarVisibility(@NonNull final Activity activity, final boolean isVisible) { @@ -58,21 +67,27 @@ public static void setStatusBarVisibility(@NonNull final Activity activity, fina /** * 设置状态栏是否显示 - * @param window The window. + * @param window The 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 + * @return true: yes, falose : no */ public static boolean isStatusBarVisible(@NonNull final Activity activity) { int flags = activity.getWindow().getAttributes().flags; @@ -115,24 +130,47 @@ public static void setStatusBarLightMode(@NonNull final Window window, final boo */ public static void addMarginTopEqualStatusBarHeight(@NonNull 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); } /** - * 减去状态栏同等高度 + * 添加状态栏同等高度到View的顶部 * @param view The view. */ public static void subtractMarginTopEqualStatusBarHeight(@NonNull 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 + */ + 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 + */ + 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); } /** @@ -147,8 +185,8 @@ 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 color The status bar's color. + * @param alpha The status bar's alpha which isn't the same as alpha in the color. */ 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); @@ -157,9 +195,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 isDecor True to add fake status bar in DecorView, + * @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, * 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 +210,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 The status bar's color. */ public static void setStatusBarColor(@NonNull final View fakeStatusBar, @ColorInt final int color) { setStatusBarColor(fakeStatusBar, color, DEFAULT_ALPHA); @@ -181,8 +219,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 The status bar's color. + * @param alpha The status bar's alpha which isn't the same as alpha in the color. */ 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,7 +228,7 @@ 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)); } @@ -214,8 +252,8 @@ 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 alpha The status bar's 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) { @@ -244,35 +282,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 The 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 The status bar's 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 The 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 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. */ - 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) { + 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); transparentStatusBar(activity); @@ -290,23 +342,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 The 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 The 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 The status bar's 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) { @@ -340,7 +393,7 @@ 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)); } } @@ -359,7 +412,7 @@ 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)); } } @@ -368,23 +421,61 @@ private static void addStatusBarAlpha(final Activity activity, final int alpha, * @param activity */ private static void hideColorView(final Activity activity) { - ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); + hideColorView(activity.getWindow()); + } + + /** + * 隐藏 透明度 View + * @param activity + */ + private static void hideAlphaView(final Activity activity) { + hideAlphaView(activity.getWindow()); + } + + /** + * 隐藏颜色View + * @param 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 */ - 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); } + /** + * 显示 View 颜色 + * @param 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 + */ + 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 @@ -472,8 +563,9 @@ public static int getActionBarHeight() { /** * 设置通知栏是否显示 * - * @param isVisible True to set notification bar visible, false otherwise. + * @param isVisible */ + @RequiresPermission(EXPAND_STATUS_BAR) public static void setNotificationBarVisibility(final boolean isVisible) { String methodName; if (isVisible) { @@ -496,7 +588,7 @@ 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) { } } @@ -509,7 +601,7 @@ private static void invokePanels(final String methodName) { * @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); @@ -521,8 +613,9 @@ public static int getNavBarHeight() { /** * 设置导航栏是否可见(图标显示) * @param activity The activity. - * @param isVisible True to set notification bar visible, false otherwise. + * @param isVisible True to set navigation bar visible, false otherwise. */ + @RequiresApi(api = Build.VERSION_CODES.KITKAT) public static void setNavBarVisibility(@NonNull final Activity activity, boolean isVisible) { setNavBarVisibility(activity.getWindow(), isVisible); } @@ -530,94 +623,96 @@ public static void setNavBarVisibility(@NonNull final Activity activity, boolean /** * 设置导航栏是否可见(图标显示) * @param window The window. - * @param isVisible True to set notification bar visible, false otherwise. + * @param isVisible True to set navigation bar visible, false otherwise. */ + @RequiresApi(Build.VERSION_CODES.KITKAT) public static void setNavBarVisibility(@NonNull final Window window, 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. + * @return true: yes, falose : 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. + * @return true: yes, falose : 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 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 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 - * @param isVisible + * 设置导航栏颜色 + * @param activity The activity. + * @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 Activity activity) { + return getNavBarColor(activity.getWindow()); } /** - * 设置是否显示状态栏图标等 + * 获取导航栏颜色 * @param window The window. + * @return the color of navigation bar */ - @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); - } + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + public static int getNavBarColor(@NonNull final Window window) { + return window.getNavigationBarColor(); } + /** + * 判断是否支持导航栏 + * @return true: yes, falose : no + */ + public static boolean isSupportNavBar() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + WindowManager wm = (WindowManager) DevUtils.getContext().getSystemService(Context.WINDOW_SERVICE); + Display display = wm.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; + } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/BrightnessUtils.java b/DevLibUtils/src/main/java/dev/utils/app/BrightnessUtils.java index 052f7bfd5d..e06f213aa3 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/BrightnessUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/BrightnessUtils.java @@ -7,12 +7,15 @@ 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; import dev.DevUtils; import dev.utils.LogPrintUtils; +import static android.Manifest.permission.WRITE_SETTINGS; + /** * detail: 亮度相关工具类 * Created by Ttt @@ -45,6 +48,7 @@ public static boolean isAutoBrightnessEnabled() { * @param enabled true : 打开, false : 关闭 * @return true : 成功, false : 失败 */ + @RequiresPermission(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,7 +64,7 @@ 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; @@ -84,6 +88,7 @@ public static int getBrightness() { * * @param brightness 亮度值 */ + @RequiresPermission(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; diff --git a/DevLibUtils/src/main/java/dev/utils/app/CPUUtils.java b/DevLibUtils/src/main/java/dev/utils/app/CPUUtils.java index 30b6793a03..c312518f14 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/CPUUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/CPUUtils.java @@ -21,7 +21,7 @@ */ public final class CPUUtils { - private CPUUtils(){ + private CPUUtils() { } // 日志TAG @@ -101,7 +101,7 @@ public static String getCpuModel() { } /** - * 获取 CPU 最大频率(单位KHZ) + * 获取 CPU 最大频率(单位KHZ) * @return */ public static String getMaxCpuFreq() { @@ -123,10 +123,10 @@ public static String getMaxCpuFreq() { LogPrintUtils.eTag(TAG, e, "getMaxCpuFreq"); result = "unknown"; } finally { - if (in != null){ + if (in != null) { try { in.close(); - } catch (IOException e){ + } catch (IOException e) { } } } @@ -134,7 +134,7 @@ public static String getMaxCpuFreq() { } /** - * 获取 CPU 最小频率(单位KHZ) + * 获取 CPU 最小频率(单位KHZ) * @return */ public static String getMinCpuFreq() { @@ -156,10 +156,10 @@ public static String getMinCpuFreq() { LogPrintUtils.eTag(TAG, e, "getMinCpuFreq"); result = "unknown"; } finally { - if (in != null){ + if (in != null) { try { in.close(); - } catch (IOException e){ + } catch (IOException e) { } } } @@ -167,7 +167,7 @@ public static String getMinCpuFreq() { } /** - * 实时获取 CPU 当前频率(单位KHZ) + * 实时获取 CPU 当前频率(单位KHZ) * @return */ public static String getCurCpuFreq() { @@ -263,10 +263,10 @@ public static String getCMDOutputString(String[] args) { } catch (IOException e) { LogPrintUtils.eTag(TAG, e, "getCMDOutputString"); } finally { - if (in != null){ + if (in != null) { try { in.close(); - } catch (IOException e){ + } catch (IOException e) { } } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/CleanUtils.java b/DevLibUtils/src/main/java/dev/utils/app/CleanUtils.java index d7e6e7c79b..ace15f62a3 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/CleanUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/CleanUtils.java @@ -15,88 +15,107 @@ */ public final class CleanUtils { - private CleanUtils(){ + private CleanUtils() { } // 日志TAG private static final String TAG = CleanUtils.class.getSimpleName(); - /** - * 清除本应用内部缓存(/data/data/com.xxx.xxx/cache) + * 清除内部缓存 - path /data/data/package/cache + * @return */ - public static void cleanInternalCache() { + public static boolean cleanInternalCache() { try { - deleteFilesByDirectory(DevUtils.getContext().getCacheDir()); - } catch (Exception e){ + return deleteFilesInDir(DevUtils.getContext().getCacheDir()); + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "cleanInternalCache"); } + return false; } /** - * 清除本应用所有数据库(/data/data/com.xxx.xxx/databases) + * 清除内部文件 - path /data/data/package/files + * @return */ - public static void cleanDatabases() { + public static boolean cleanInternalFiles() { try { - deleteFilesByDirectory(new File(DevUtils.getContext().getFilesDir().getPath() + DevUtils.getContext().getPackageName() + "/databases")); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "cleanDatabases"); + return deleteFilesInDir(DevUtils.getContext().getFilesDir()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cleanInternalFiles"); } + return false; } /** - * 清除本应用SharedPreference(/data/data/com.xxx.xxx/shared_prefs) + * 清除内部数据库 - path /data/data/package/databases + * @return */ - public static void cleanSharedPreference() { + public static boolean cleanInternalDbs() { try { - deleteFilesByDirectory(new File(DevUtils.getContext().getFilesDir().getPath() + DevUtils.getContext().getPackageName() + "/shared_prefs")); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "cleanSharedPreference"); + return deleteFilesInDir(new File(DevUtils.getContext().getFilesDir().getParent(), "databases")); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cleanInternalDbs"); } + return false; } /** - * 按名字清除本应用数据库 - * @param dbName 数据库名称 + * 根据名称清除数据库 - path /data/data/package/databases/dbName + * @param dbName + * @return */ - public static void cleanDatabaseByName(String dbName) { + public static boolean cleanInternalDbByName(final String dbName) { try { - DevUtils.getContext().deleteDatabase(dbName); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "cleanDatabaseByName"); + return DevUtils.getContext().deleteDatabase(dbName); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "cleanInternalDbByName"); } + return false; } /** - * 清除/data/data/com.xxx.xxx/files下的内容 + * 清除内部 SP - path /data/data/package/shared_prefs + * @return */ - public static void cleanFiles() { + public static boolean cleanInternalSp() { try { - deleteFilesByDirectory(DevUtils.getContext().getFilesDir()); + return deleteFilesInDir(new File(DevUtils.getContext().getFilesDir().getParent(), "shared_prefs")); } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "cleanFiles"); + LogPrintUtils.eTag(TAG, e, "cleanInternalSp"); } + return false; } /** - * 清除外部cache下的内容(/mnt/sdcard/android/data/com.xxx.xxx/cache) + * 清除外部缓存 - path /storage/emulated/0/android/data/package/cache + * @return */ - public static void cleanExternalCache() { + public static boolean cleanExternalCache() { try { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - deleteFilesByDirectory(DevUtils.getContext().getExternalCacheDir()); + return deleteFilesInDir(DevUtils.getContext().getExternalCacheDir()); } } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "cleanExternalCache"); } + return false; } /** * 清除自定义路径下的文件,使用需小心,请不要误删。而且只支持目录下的文件删除 * @param filePath 文件路径 */ - public static void cleanCustomCache(String filePath) { - deleteFilesByDirectory(getFileByPath(filePath)); + public static boolean cleanCustomDir(String filePath) { + return deleteFilesInDir(getFileByPath(filePath)); + } + + /** + * 清除自定义路径下的文件,使用需小心,请不要误删。而且只支持目录下的文件删除 + * @param file 文件路径 + */ + public static boolean cleanCustomDir(File file) { + return deleteFilesInDir(file); } /** @@ -106,39 +125,18 @@ public static void cleanCustomCache(String filePath) { public static void cleanApplicationData(String... filePaths) { cleanInternalCache(); cleanExternalCache(); - cleanDatabases(); - cleanSharedPreference(); - cleanFiles(); + cleanInternalDbs(); + cleanInternalSp(); + cleanInternalFiles(); try { for (String fPath : filePaths) { - cleanCustomCache(fPath); + cleanCustomDir(fPath); } - } catch (Exception e){ + } 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/ 目录,一般放一些长时间保存的数据 @@ -169,7 +167,7 @@ public static long getFolderSize(File file) { * @param file * @return */ - public static String getCacheSize(File file){ + public static String getCacheSize(File file) { return getFormatSize(getFolderSize(file)); } @@ -207,12 +205,60 @@ public static String getFormatSize(double size) { // == + /** + * 删除目录下所有过滤的文件 + * @param dir 目录 + * @return true : 删除成功, false :删除失败 + */ + private static boolean deleteFilesInDir(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 true; + } + + /** + * 删除目录 + * @param dir 目录 + * @return true : 删除成功, 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 */ - private static File getFileByPath(final String filePath){ + private static File getFileByPath(final String filePath) { return filePath != null ? new File(filePath) : null; } diff --git a/DevLibUtils/src/main/java/dev/utils/app/ClickUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ClickUtils.java index e014e55d2c..6e160911de 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ClickUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ClickUtils.java @@ -1,6 +1,7 @@ package dev.utils.app; import java.util.HashMap; +import java.util.Map; import dev.utils.LogPrintUtils; @@ -10,7 +11,7 @@ */ public final class ClickUtils { - private ClickUtils(){ + private ClickUtils() { } // 日志TAG @@ -23,9 +24,9 @@ private ClickUtils(){ /** 默认间隔时间 */ private static long DF_DIFF = 1000l; // 点击间隔1秒内 /** 配置数据 */ - private static final HashMap mapConfig = new HashMap<>(); + private static final Map mapConfig = new HashMap<>(); /** 点击记录数据 */ - private static final HashMap mapRecords = new HashMap<>(); + private static final Map mapRecords = new HashMap<>(); // === @@ -73,11 +74,11 @@ public static boolean isFastDoubleClick(int tagId, long diff) { * @param tag * @return */ - public static boolean isFastDoubleClick(String tag){ + public static boolean isFastDoubleClick(String tag) { // 获取配置时间 Long config_time = mapConfig.get(tag); // 如果等于null, 则传入默认时间 - if (config_time == null){ + if (config_time == null) { return isFastDoubleClick(tag, DF_DIFF); } return isFastDoubleClick(tag, config_time); @@ -89,10 +90,10 @@ public static boolean isFastDoubleClick(String tag){ * @param diff * @return */ - public static boolean isFastDoubleClick(String tag, long diff){ + public static boolean isFastDoubleClick(String tag, long diff) { // 获取上次点击的时间 Long lastTime = mapRecords.get(tag); - if (lastTime == null){ + if (lastTime == null) { lastTime = 0l; } long cTime = System.currentTimeMillis(); @@ -115,13 +116,13 @@ public static boolean isFastDoubleClick(String tag, long diff){ * @param obj * @return */ - public static boolean isFastDoubleClick(Object obj){ + 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){ + if (config_time == null) { return isFastDoubleClick(tag, DF_DIFF); } return isFastDoubleClick(tag, config_time); @@ -133,12 +134,12 @@ public static boolean isFastDoubleClick(Object obj){ * @param diff * @return */ - public static boolean isFastDoubleClick(Object obj, long diff){ + 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){ + if (lastTime == null) { lastTime = 0l; } long cTime = System.currentTimeMillis(); @@ -160,8 +161,8 @@ public static boolean isFastDoubleClick(Object obj, long diff){ * 初始化配置信息 * @param mapConfig */ - public static void initConfig(HashMap mapConfig){ - if (mapConfig != null){ + public static void initConfig(Map mapConfig) { + if (mapConfig != null) { mapConfig.putAll(mapConfig); } } @@ -171,7 +172,7 @@ public static void initConfig(HashMap mapConfig){ * @param key * @param val */ - public static void putConfig(String key, Long val){ + public static void putConfig(String key, Long val) { mapConfig.put(key, val); } @@ -179,7 +180,7 @@ public static void putConfig(String key, Long val){ * 移除配置信息 * @param key */ - public static void removeConfig(String key){ + public static void removeConfig(String key) { mapConfig.remove(key); } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/ClipboardUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ClipboardUtils.java index 9dc5d825e5..0c13b5c0f9 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ClipboardUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ClipboardUtils.java @@ -32,7 +32,7 @@ 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"); } } @@ -48,7 +48,7 @@ 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; @@ -65,7 +65,7 @@ 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"); } } @@ -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,7 +98,7 @@ 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"); } } @@ -114,7 +114,7 @@ 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; diff --git a/DevLibUtils/src/main/java/dev/utils/app/ContentResolverUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ContentResolverUtils.java index ca8fcb7d5b..c932cf98e7 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ContentResolverUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ContentResolverUtils.java @@ -34,14 +34,14 @@ private ContentResolverUtils() { * @param file * @return */ - public static boolean notifyMediaStore(File file){ - if (file != null){ + public static boolean notifyMediaStore(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"); } } @@ -55,15 +55,15 @@ public static boolean notifyMediaStore(File file){ * @param isNotify * @return */ - public static boolean insertImageIntoMediaStore(File file, String fileName, boolean isNotify){ - if (file != null){ + public static boolean insertImageIntoMediaStore(File file, String fileName, boolean isNotify) { + if (file != null) { try { // 添加到相册 MediaStore.Images.Media.insertImage(DevUtils.getContext().getContentResolver(), file.getAbsolutePath(), TextUtils.isEmpty(fileName) ? file.getName() : fileName, null); - if (isNotify){ + if (isNotify) { notifyMediaStore(file); } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "insertImageIntoMediaStore"); } } @@ -75,7 +75,7 @@ public static boolean insertImageIntoMediaStore(File file, String fileName, bool * @param file * @return */ - public static boolean insertVideoIntoMediaStore(File file){ + public static boolean insertVideoIntoMediaStore(File file) { return insertIntoMediaStore(file, -1, true, "video/3gp"); } diff --git a/DevLibUtils/src/main/java/dev/utils/app/DBUtils.java b/DevLibUtils/src/main/java/dev/utils/app/DBUtils.java index 8684b62fe6..50af954a8f 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/DBUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/DBUtils.java @@ -52,7 +52,7 @@ public static boolean startExportDatabase(String targetFile, String dbName) { * @param destFilePath 目标文件地址 * @return 是否倒出成功 */ - public static boolean startImportDatabase(String srcFilePath, String destFilePath){ + public static boolean startImportDatabase(String srcFilePath, String destFilePath) { // 判断 SDCard 是否挂载 if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { return false; @@ -74,7 +74,7 @@ public static boolean startImportDatabase(String srcFilePath, String destFilePat * @param destFilePath 目标文件地址 * @return 是否倒出成功 */ - public static boolean startImportDatabase(InputStream inputStream, String destFilePath){ + public static boolean startImportDatabase(InputStream inputStream, String destFilePath) { // 判断 SDCard 是否挂载 if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { return false; @@ -96,7 +96,7 @@ public static boolean startImportDatabase(InputStream inputStream, String destFi * 获取数据库路径 * @return */ - public static String getDBPath(){ + public static String getDBPath() { try { // Database 文件地址 String dbPath = Environment.getDataDirectory() + "/data/" + DevUtils.getContext().getPackageName() + "/databases/"; diff --git a/DevLibUtils/src/main/java/dev/utils/app/DeviceUtils.java b/DevLibUtils/src/main/java/dev/utils/app/DeviceUtils.java index dc3ed1e512..295a7778a2 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/DeviceUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/DeviceUtils.java @@ -8,6 +8,9 @@ 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; @@ -20,7 +23,6 @@ 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; @@ -28,6 +30,9 @@ import dev.DevUtils; import dev.utils.LogPrintUtils; +import static android.Manifest.permission.ACCESS_WIFI_STATE; +import static android.Manifest.permission.INTERNET; + /** * detail: 设备相关工具类 * Created by Ttt @@ -38,18 +43,18 @@ private DeviceUtils() { } // 日志TAG - private static final String TAG = DBUtils.class.getSimpleName(); + private static final String TAG = DeviceUtils.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_ABI:获取设备指令集名称(CPU的类型) // android.os.Build.CPU_ABI2:获取第二个指令集名称 // android.os.Build.DEVICE:获取设备驱动名称 -// android.os.Build.DISPLAY:获取设备显示的版本包(在系统设置中显示为版本号)和ID一样 +// android.os.Build.DISPLAY:获取设备显示的版本包(在系统设置中显示为版本号)和ID一样 // android.os.Build.FINGERPRINT:设备的唯一标识。由设备的多个信息拼接合成。 -// android.os.Build.HARDWARE:设备硬件名称,一般和基板名称一样(BOARD) +// android.os.Build.HARDWARE:设备硬件名称,一般和基板名称一样(BOARD) // android.os.Build.HOST:设备主机地址 // android.os.Build.ID:设备版本号。 // android.os.Build.MODEL :获取手机的型号 设备名称。 @@ -82,48 +87,34 @@ public static String getSysLanguage() { * 获取设备信息 * @param dInfoMaps 传入设备信息传出HashMap */ - public static void getDeviceInfo(HashMap dInfoMaps) { - // 获取设备信息类的所有申明的字段,即包括public、private和proteced, 但是不包括父类的申明字段。 + public static void getDeviceInfo(Map dInfoMaps) { + // 获取设备信息类的所有申明的字段,即包括public、private和proteced, 但是不包括父类的申明字段。 Field[] fields = Build.class.getDeclaredFields(); // 遍历字段 for (Field field : fields) { try { - // 取消java的权限控制检查 + // 取消 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; + // 转换 当前设备支持的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] + dInfoMaps.put(field.getName(), Arrays.toString((String[]) object)); + } + continue; + } + } catch (Exception e) { } } - // 获取类型对应字段的数据,并保存 - dInfoMaps.put(field.getName().toLowerCase(), field.get(null).toString()); + // 获取类型对应字段的数据, 并保存 + dInfoMaps.put(field.getName(), field.get(null).toString()); } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "getDeviceInfo2"); + LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); } } } @@ -133,7 +124,7 @@ public static void getDeviceInfo2(HashMap dInfoMaps) { * @param dInfoMaps 设备信息 * @param eHint 错误提示,如获取设备信息失败 */ - public static String handleDeviceInfo(HashMap dInfoMaps, String eHint) { + public static String handlerDeviceInfo(Map dInfoMaps, String eHint) { try { // 初始化StringBuilder,拼接字符串 StringBuilder sBuilder = new StringBuilder(); @@ -142,9 +133,9 @@ public static String handleDeviceInfo(HashMap dInfoMaps, String // 遍历设备信息 while (mapIter.hasNext()) { // 获取对应的key-value - Map.Entry rnEntry = (Map.Entry) mapIter.next(); - String rnKey = (String) rnEntry.getKey(); // key - String rnValue = (String) rnEntry.getValue(); // value + Map.Entry rnEntry = mapIter.next(); + String rnKey = rnEntry.getKey(); // key + String rnValue = rnEntry.getValue(); // value // 保存设备信息 sBuilder.append(rnKey); sBuilder.append(" = "); @@ -153,7 +144,7 @@ public static String handleDeviceInfo(HashMap dInfoMaps, String } return sBuilder.toString(); } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleDeviceInfo"); + LogPrintUtils.eTag(TAG, e, "handlerDeviceInfo"); } return eHint; } @@ -170,7 +161,7 @@ public static String getSDKVersionName() { * 获取当前SDK 版本号 * @return */ - public static int getSDKVersion(){ + public static int getSDKVersion() { return Build.VERSION.SDK_INT; } @@ -180,7 +171,7 @@ public static int getSDKVersion(){ * 在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,当设备被wipe后该值会被重置。 * @return */ - public static String getAndroidId(){ + public static String getAndroidId() { // Android id 默认为null String androidId = null; try { @@ -207,6 +198,35 @@ public static boolean isDeviceRooted() { return false; } + /** + * 返回是否启用了 ADB + * @return + */ + @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; + } + + /** + * 获取支持的指令集 如: [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 }; + } + } + // == /** 特殊mac地址用于判断是否获取失败 */ @@ -214,11 +234,11 @@ public static boolean isDeviceRooted() { /** * 获取设备 MAC 地址 - * hint: - * 需添加权限 - * 需添加权限 + * + * * @return */ + @RequiresPermission(allOf = {INTERNET, ACCESS_WIFI_STATE}) public static String getMacAddress() { String macAddress = getMacAddressByWifiInfo(); if (!CUSTOM_MAC.equals(macAddress)) { @@ -242,11 +262,11 @@ public static String getMacAddress() { /** * 获取设备 MAC 地址 - * hint: - * 需添加权限 + * * @return */ - @SuppressLint({"HardwareIds", "MissingPermission"}) + @SuppressLint({"HardwareIds"}) + @RequiresPermission(ACCESS_WIFI_STATE) private static String getMacAddressByWifiInfo() { try { @SuppressLint("WifiManagerLeak") @@ -263,10 +283,10 @@ private static String getMacAddressByWifiInfo() { /** * 获取设备 MAC 地址 - * hint: - * 需添加权限 + * * @return */ + @RequiresPermission(INTERNET) private static String getMacAddressByNetworkInterface() { try { Enumeration nis = NetworkInterface.getNetworkInterfaces(); @@ -338,7 +358,7 @@ private static InetAddress getInetAddress() { */ private static String getMacAddressByFile() { ShellUtils.CommandResult result = ShellUtils.execCmd("getprop wifi.interface", false); - if (result.result == 0) { + if (result.isSuccess()) { String name = result.successMsg; if (name != null) { result = ShellUtils.execCmd("cat /sys/class/net/" + name + "/address", false); @@ -378,7 +398,7 @@ public static String getModel() { } /** - * 关机 需要 root 权限或者系统权限 + * 关机 (需要 root 权限) * @return */ public static boolean shutdown() { @@ -388,14 +408,14 @@ public static boolean shutdown() { intent.putExtra("android.intent.extra.KEY_CONFIRM", false); DevUtils.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); return true; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "shutdown"); } return false; } /** - * 重启 需要 root 权限或者系统权限 + * 重启设备 (需要 root 权限) * @return */ public static boolean reboot() { @@ -407,16 +427,16 @@ public static boolean reboot() { intent.putExtra("window", 0); DevUtils.getContext().sendBroadcast(intent); return true; - } catch (Exception e){ + } 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) { try { @@ -429,20 +449,27 @@ public static void reboot(final String reason) { } } - /** 重启到 recovery 需要 root 权限 */ - public static void reboot2Recovery() { - ShellUtils.execCmd("reboot recovery", true); + /** + * 重启引导到 recovery (需要 root 权限) + * @return + */ + public static boolean reboot2Recovery() { + ShellUtils.CommandResult result = ShellUtils.execCmd("reboot recovery", true); + return result.isSuccess2(); } - /** 重启到 bootloader 需要 root 权限 */ - public static void reboot2Bootloader() { - ShellUtils.execCmd("reboot bootloader", true); + /** + * 重启引导到 bootloader (需要 root 权限) + * @return + */ + public static boolean reboot2Bootloader() { + ShellUtils.CommandResult result = ShellUtils.execCmd("reboot bootloader", true); + return result.isSuccess2(); } /** - * BASEBAND-VER - * 基带版本 - * return String + * 获取 基带版本 BASEBAND-VER + * @return */ public static String getBaseband_Ver() { String Version = ""; @@ -459,9 +486,8 @@ public static String getBaseband_Ver() { } /** - * CORE-VER - * 内核版本 - * return String + * 获取 内核版本 CORE-VER + * @return */ public static String getLinuxCore_Ver() { String kernelVersion = ""; diff --git a/DevLibUtils/src/main/java/dev/utils/app/DialogUtils.java b/DevLibUtils/src/main/java/dev/utils/app/DialogUtils.java index f67edc7a4b..28699d1e37 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/DialogUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/DialogUtils.java @@ -3,25 +3,33 @@ import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; +import android.content.DialogInterface; +import android.os.Handler; +import android.support.v7.app.AlertDialog; import android.widget.PopupWindow; +import dev.utils.LogPrintUtils; + /** * detail: Dialog 操作相关工具类 * Created by Ttt */ public final class DialogUtils { - private DialogUtils(){ + private DialogUtils() { } + // 日志 TAG + private static final String TAG = DialogUtils.class.getSimpleName(); + // ======== Dialog 相关 ======== /** * 关闭Dialog * @param dialog */ - public static void closeDialog(Dialog dialog){ - if (dialog != null && dialog.isShowing()){ + public static void closeDialog(Dialog dialog) { + if (dialog != null && dialog.isShowing()) { dialog.dismiss(); } } @@ -30,13 +38,13 @@ public static void closeDialog(Dialog dialog){ * 关闭多个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++){ + 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()){ + if (dialog != null && dialog.isShowing()) { dialog.dismiss(); } } @@ -47,8 +55,8 @@ public static void closeDialogs(Dialog... dialogs){ * 关闭PopupWindow * @param popupWindow */ - public static void closePopupWindow(PopupWindow popupWindow){ - if (popupWindow != null && popupWindow.isShowing()){ + public static void closePopupWindow(PopupWindow popupWindow) { + if (popupWindow != null && popupWindow.isShowing()) { popupWindow.dismiss(); } } @@ -57,73 +65,246 @@ public static void closePopupWindow(PopupWindow popupWindow){ * 关闭多个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++){ + 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()){ + if (popupWindow != null && popupWindow.isShowing()) { popupWindow.dismiss(); } } } } - // == + // = + + /** + * detail: Dialog 事件 + * Created by Ttt + */ + public static abstract class DialogListener { + + /** + * 最左边按钮点击事件 + * @param dialog + */ + public void onLeftButton(DialogInterface dialog) { + } + + /** + * 最右边按钮点击事件 + * @param dialog + */ + public abstract void onRightButton(DialogInterface dialog); + + /** + * 关闭通知 + * @param dialog + */ + public void onDismiss(DialogInterface dialog) { + } + } + + /** + * 创建提示 Dialog (原生样式) + * @param context + * @param title dialog 标题 + * @param content dialog 内容 + * @param rightBtn 右边按钮文案 + * @return + */ + public static AlertDialog createAlertDialog(Context context, String title, String content, String rightBtn) { + return createAlertDialog(context, title, content, null, rightBtn, null); + } /** - * 创建加载 Dialog + * 创建提示 Dialog (原生样式) * @param context - * @param title - * @param content + * @param title dialog 标题 + * @param content dialog 内容 + * @param leftBtn 左边按钮文案 + * @param rightBtn 右边按钮文案 * @return */ - public static ProgressDialog creDialog(Context context, String title, String content){ - return creDialog(context, title, content, false);// 不可以使用返回键 + public static AlertDialog createAlertDialog(Context context, String title, String content, String leftBtn, String rightBtn) { + return createAlertDialog(context, title, content, leftBtn, rightBtn, null); } /** - * 创建加载 Dialog + * 创建提示 Dialog (原生样式) * @param context - * @param title - * @param content - * @param isCancel + * @param title dialog 标题 + * @param content dialog 内容 + * @param rightBtn 右边按钮文案 + * @param dialogListener 事件通知 * @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; + public static AlertDialog createAlertDialog(Context context, String title, String content, String rightBtn, DialogListener dialogListener) { + return createAlertDialog(context, title, content, null, rightBtn, dialogListener); } /** - * 创建自动关闭dialog + * 创建提示 Dialog (原生样式) * @param context - * @param title - * @param content + * @param title dialog 标题 + * @param content dialog 内容 + * @param leftBtn 左边按钮文案 + * @param rightBtn 右边按钮文案 + * @param dialogListener 事件通知 * @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) { + public static AlertDialog createAlertDialog(Context context, String title, String content, String leftBtn, 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); + } + } + }); } - try { - if (progressDialog != null) { - progressDialog.dismiss(); - } - } catch (Exception e) { + } + + 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); + } + } + }); } } - }); - thread.start(); - return progressDialog; + + if (dialogListener != null) { + 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 + * @param title dialog 标题 + * @param content dialog 内容 + * @return + */ + public static ProgressDialog createProgressDialog(Context context, String title, String content) { + return createProgressDialog(context, title, content, false, null); + } + + /** + * 创建加载中 Dialog (原生样式) + * @param context + * @param title dialog 标题 + * @param content dialog 内容 + * @param isCancel 是否可以返回键关闭 + * @return + */ + public static ProgressDialog createProgressDialog(Context context, String title, String content, boolean isCancel) { + return createProgressDialog(context, title, content, isCancel, null); + } + + /** + * 创建加载中 Dialog (原生样式) + * @param context + * @param title dialog 标题 + * @param content dialog 内容 + * @param isCancel 是否可以返回键关闭 + * @return + */ + public static ProgressDialog createProgressDialog(Context context, String title, String content, boolean isCancel, DialogInterface.OnCancelListener cancelListener) { + try { + ProgressDialog progressDialog = ProgressDialog.show(context, title, content, isCancel); + progressDialog.setOnCancelListener(cancelListener); + return progressDialog; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "createProgressDialog"); + } + return null; + } + + // = + + /** + * 自动关闭dialog + * @param dialog + * @param time + * @param handler + * @return + */ + public static T autoCloseDialog(final T dialog, final long time, Handler handler) { + // 不为null, 并且显示中 + 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) { + } + } + }, time); + } + } + return dialog; + } + + /** + * 自动关闭 PopupWindow + * @param popupWindow + * @param time + * @param handler + * @return + */ + public static T autoClosePopupWindow(final T popupWindow, final long time, Handler handler) { + // 不为null, 并且显示中 + 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) { + } + } + }, time); + } + } + return popupWindow; } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/EditTextUtils.java b/DevLibUtils/src/main/java/dev/utils/app/EditTextUtils.java index 7745d80fb1..285899490a 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/EditTextUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/EditTextUtils.java @@ -19,7 +19,7 @@ */ public final class EditTextUtils { - private EditTextUtils(){ + private EditTextUtils() { } // 日志TAG @@ -37,8 +37,8 @@ private EditTextUtils(){ * @param maxLength * @return */ - public static EditText setMaxLengthAnText(EditText editText, String content, int maxLength){ - if (editText != null){ + public static EditText setMaxLengthAnText(EditText editText, String content, int maxLength) { + if (editText != null) { // 设置长度限制, 并且设置内容 setText(setMaxLength(editText, maxLength), content); } @@ -51,9 +51,9 @@ public static EditText setMaxLengthAnText(EditText editText, String content, int * @param maxLength * @return */ - public static EditText setMaxLength(EditText editText, int maxLength){ - if (editText != null){ - if (maxLength > 0){ + public static EditText setMaxLength(EditText editText, int maxLength) { + if (editText != null) { + if (maxLength > 0) { // 设置最大长度限制 InputFilter[] filters = { new InputFilter.LengthFilter(maxLength) }; editText.setFilters(filters); @@ -67,8 +67,8 @@ public static EditText setMaxLength(EditText editText, int maxLength){ * @param editText * @return */ - public static int getSelectionStart(EditText editText){ - if (editText != null){ + public static int getSelectionStart(EditText editText) { + if (editText != null) { return editText.getSelectionStart(); } return 0; @@ -79,8 +79,8 @@ public static int getSelectionStart(EditText editText){ * @param editText * @return */ - public static String getText(EditText editText){ - if (editText != null){ + public static String getText(EditText editText) { + if (editText != null) { return editText.getText().toString(); } return ""; @@ -91,7 +91,7 @@ public static String getText(EditText editText){ * @param editText * @return */ - public static int getTextLength(EditText editText){ + public static int getTextLength(EditText editText) { return getText(editText).length(); } @@ -102,7 +102,7 @@ public static int getTextLength(EditText editText){ * @param editText * @param content */ - public static EditText setText(EditText editText, String content){ + public static EditText setText(EditText editText, String content) { return setText(editText, content, true); } @@ -112,12 +112,12 @@ public static EditText setText(EditText editText, String content){ * @param content * @param isSelect 是否设置光标 */ - public static EditText setText(EditText editText, String content, boolean isSelect){ - if (editText != null){ - if (content != null){ + public static EditText setText(EditText editText, String content, boolean isSelect) { + if (editText != null) { + if (content != null) { // 设置文本 editText.setText(content); - if (isSelect){ + if (isSelect) { // 设置光标 editText.setSelection(editText.getText().toString().length()); } @@ -133,8 +133,8 @@ public static EditText setText(EditText editText, String content, boolean isSele * @param isSelect * @return */ - public static EditText insert(EditText editText, String content, boolean isSelect){ - if (editText != null){ + public static EditText insert(EditText editText, String content, boolean isSelect) { + if (editText != null) { return insert(editText, content, editText.getSelectionStart(), isSelect); } return editText; @@ -148,19 +148,19 @@ public static EditText insert(EditText editText, String content, boolean isSelec * @param isSelect * @return */ - public static EditText insert(EditText editText, String content, int start, boolean isSelect){ - if (editText != null){ - if (!TextUtils.isEmpty(content)){ + 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){ + if (isSelect) { // 设置光标 editText.setSelection(editText.getText().toString().length()); } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "insert"); } } @@ -175,7 +175,7 @@ public static EditText insert(EditText editText, String content, int start, bool * @param editText * @return */ - public static EditText setSelectTop(EditText editText){ + public static EditText setSelectTop(EditText editText) { return setSelect(editText, 0); } @@ -184,8 +184,8 @@ public static EditText setSelectTop(EditText editText){ * @param editText * @return */ - public static EditText setSelectBottom(EditText editText){ - if (editText != null){ + public static EditText setSelectBottom(EditText editText) { + if (editText != null) { // 设置光标 editText.setSelection(editText.getText().toString().length()); } @@ -198,13 +198,13 @@ public static EditText setSelectBottom(EditText editText){ * @param select * @return */ - public static EditText setSelect(EditText editText, int select){ - if (editText != null){ - if (select >= 0){ + public static EditText setSelect(EditText editText, int select) { + if (editText != null) { + if (select >= 0) { // 判断是否超过限制 int length = editText.getText().toString().length(); // 如果超过长度, 则设置最后 - if (select > length){ + if (select > length) { // 设置光标 editText.setSelection(length); } else { @@ -225,8 +225,8 @@ public static EditText setSelect(EditText editText, int select){ * @return * setKeyListener(editText, getNumberAndEnglishKeyListener()); */ - public static EditText setKeyListener(EditText editText, KeyListener keyListener){ - if (editText != null){ + public static EditText setKeyListener(EditText editText, KeyListener keyListener) { + if (editText != null) { editText.setKeyListener(keyListener); } return editText; @@ -239,9 +239,9 @@ public static EditText setKeyListener(EditText editText, KeyListener keyListener * @return * setKeyListener(editText, "1234567890"); */ - public static EditText setKeyListener(EditText editText, String digits){ - if (editText != null){ - if (TextUtils.isEmpty(digits)){ + public static EditText setKeyListener(EditText editText, String digits) { + if (editText != null) { + if (TextUtils.isEmpty(digits)) { editText.setKeyListener(null); } else { editText.setKeyListener(DigitsKeyListener.getInstance(digits)); @@ -310,31 +310,51 @@ public static abstract class DevTextWatcher implements TextWatcher { // 标记id - 一定程度上唯一 private final int markId; // 判断是否操作中 - 操作状态 -> 如果是否使用搜索数据等 - private boolean isOperate = false; + private boolean operate = false; // 标记状态, 特殊需求处理 private int operateState = -1; - public DevTextWatcher(){ + public DevTextWatcher() { // 初始化id this.markId = UUID.randomUUID().hashCode(); } + /** + * 获取标记id + * @return + */ public final int getMarkId() { return markId; } + /** + * 判断是否操作中 + * @return + */ public final boolean isOperate() { - return isOperate; + return operate; } + /** + * 设置是否操作中 + * @param operate + */ public final void setOperate(boolean operate) { - isOperate = operate; + this.operate = operate; } + /** + * 获取操作状态 + * @return + */ public final int getOperateState() { return operateState; } + /** + * 设置操作状态 + * @param operateState + */ public final void setOperateState(int operateState) { this.operateState = operateState; } diff --git a/DevLibUtils/src/main/java/dev/utils/app/EncodeUtils.java b/DevLibUtils/src/main/java/dev/utils/app/EncodeUtils.java index 403426b8e8..c062bf0208 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/EncodeUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/EncodeUtils.java @@ -22,7 +22,7 @@ private EncodeUtils() { private static final String TAG = EncodeUtils.class.getSimpleName(); /** - * url编码 - UTF-8 + * url 编码 - UTF-8 * @param input The input. * @return the urlencoded string */ @@ -31,7 +31,7 @@ public static String urlEncode(final String input) { } /** - * url编码 + * url 编码 * @param input The input. * @param charsetName The name of charset. * @return the urlencoded string @@ -75,7 +75,7 @@ public static String urlDecode(final String input, final String charsetName) { // // NO_PADDING 这个参数是略去加密字符串最后的”=” // -// NO_WRAP 这个参数意思是略去所有的换行符(设置后CRLF就没用了) +// NO_WRAP 这个参数意思是略去所有的换行符(设置后CRLF就没用了) // // URL_SAFE 这个参数意思是加密时不使用对URL和文件名有特殊意义的字符来作为加密字符,具体就是以-和_取代+和/ @@ -132,14 +132,14 @@ public static byte[] base64Decode(final byte[] input) { public static String base64DecodeToString(final byte[] input) { try { return new String(base64Decode(input)); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "base64DecodeToString"); } return null; } /** - * html编码 + * html 编码 * @param input The input. * @return html-encode string */ @@ -177,7 +177,7 @@ public static String htmlEncode(final CharSequence input) { } /** - * html解码 + * html 解码 * @param input The input. * @return the string of decode html-encode string */ @@ -189,4 +189,32 @@ public static CharSequence htmlDecode(final String input) { return Html.fromHtml(input); } } + + /** + * 返回二进制编码后的字符串 + * @param input + * @return + */ + public static String binEncode(final String input) { + StringBuilder stringBuilder = new StringBuilder(); + for (char i : input.toCharArray()) { + stringBuilder.append(Integer.toBinaryString(i)); + stringBuilder.append(' '); + } + return stringBuilder.toString(); + } + + /** + * 返回 UTF-8 字符串的二进制数据 + * @param input + * @return + */ + public static String binDecode(final String input) { + String[] splitted = input.split(" "); + StringBuilder sb = new StringBuilder(); + for (String i : splitted) { + sb.append(((char) Integer.parseInt(i.replace(" ", ""), 2))); + } + return sb.toString(); + } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/ErrorUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ErrorUtils.java index ff8ca24f5a..95ccc52803 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ErrorUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ErrorUtils.java @@ -4,31 +4,35 @@ import java.io.StringWriter; import java.io.Writer; +import dev.utils.LogPrintUtils; + /** * detail: 错误信息处理工具类 * Created by Ttt */ public final class ErrorUtils { - private ErrorUtils(){ + private ErrorUtils() { } + // 日志TAG + private static final String TAG = ErrorUtils.class.getSimpleName(); /** 换行字符串 */ 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; +// /** 换行字符串 - 两行 */ +// private static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; /** * 获取错误信息 * @param e * @return */ - public static String getErrorMsg(Exception e){ + public static String getErrorMsg(Exception e) { try { - if (e != null){ + if (e != null) { return e.getMessage(); } - } catch (Exception ex){ + } catch (Exception ex) { return ex.getMessage(); } return "e(null)"; @@ -46,8 +50,13 @@ public static String getThrowableMsg(Throwable e) { e.printStackTrace(new PrintWriter(writer)); return writer.toString(); } - } catch (Exception ex){ - return ex.getMessage(); + } catch (Exception ex) { + LogPrintUtils.eTag(TAG, e, "getThrowableMsg"); + if (ex != null) { + return ex.getMessage(); + } else { + return "ex(null)"; + } } return "e(null)"; } @@ -74,6 +83,7 @@ public static String getThrowableMsg(String eHint, Throwable ex) { return writer.toString(); } } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getThrowableMsg"); } finally { if(printWriter != null) { printWriter.close(); diff --git a/DevLibUtils/src/main/java/dev/utils/app/FileRecordUtils.java b/DevLibUtils/src/main/java/dev/utils/app/FileRecordUtils.java index 9679193688..a60b6d1a13 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/FileRecordUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/FileRecordUtils.java @@ -14,6 +14,7 @@ import java.lang.reflect.Field; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; @@ -28,7 +29,7 @@ */ public final class FileRecordUtils { - private FileRecordUtils(){ + private FileRecordUtils() { } // 日志TAG @@ -36,30 +37,30 @@ private FileRecordUtils(){ // =================== 配置信息 ======================= - /** App版本(如1.0.01) 显示给用户看的 */ - static String APP_VERSION_NAME = ""; + /** App 版本(如1.0.01) 显示给用户看的 */ + private static String APP_VERSION_NAME = ""; /** android:versionCode——整数值,代表应用程序代码的相对版本,也就是版本更新过多少次。(不显示给用户看) */ - static String APP_VERSION_CODE = ""; + private static String APP_VERSION_CODE = ""; /** 设备信息 */ - static String DEVICE_INFO_STR = null; + private static String DEVICE_INFO_STR = null; /** 用来存储设备信息 */ - static HashMap DEVICE_INFO_MAPS = new HashMap(); + private static Map DEVICE_INFO_MAPS = new HashMap<>(); /** 换行字符串 */ - static final String NEW_LINE_STR = System.getProperty("line.separator"); + private static final String NEW_LINE_STR = System.getProperty("line.separator"); /** 换行字符串 - 两行 */ - static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; + private static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; - // ================== APP、设备信息处理 =================== + // ================== App、设备信息处理 =================== /** - * 获取app版本信息 + * 获取 App 版本信息 */ - static String[] getAppVersion() { + private static String[] getAppVersion() { String[] aVersion = null; try { PackageManager pm = DevUtils.getContext().getPackageManager(); @@ -80,15 +81,31 @@ static String[] getAppVersion() { * 获取设备信息 * @param dInfoMaps 传入设备信息传出HashMap */ - static void getDeviceInfo(HashMap dInfoMaps) { + private static void getDeviceInfo(Map dInfoMaps) { // 获取设备信息类的所有申明的字段,即包括public、private和proteced, 但是不包括父类的申明字段。 Field[] fields = Build.class.getDeclaredFields(); // 遍历字段 for (Field field : fields) { try { - // 取消java的权限控制检查 + // 取消 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] + dInfoMaps.put(field.getName(), Arrays.toString((String[]) object)); + } + continue; + } + } catch (Exception e) { + } + } + // 获取类型对应字段的数据, 并保存 dInfoMaps.put(field.getName(), field.get(null).toString()); } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); @@ -100,7 +117,7 @@ static void getDeviceInfo(HashMap dInfoMaps) { * 处理设备信息 * @param eHint 错误提示,如获取设备信息失败 */ - static String handleDeviceInfo(String eHint) { + private static String handlerDeviceInfo(String eHint) { try { // 如果不为null,则直接返回之前的信息 if(!TextUtils.isEmpty(DEVICE_INFO_STR)) { @@ -113,9 +130,9 @@ static String handleDeviceInfo(String eHint) { // 遍历设备信息 while (mapIter.hasNext()) { // 获取对应的key-value - Map.Entry rnEntry = (Map.Entry) mapIter.next(); - String rnKey = (String) rnEntry.getKey(); // key - String rnValue = (String) rnEntry.getValue(); // value + Map.Entry rnEntry = mapIter.next(); + String rnKey = rnEntry.getKey(); // key + String rnValue = rnEntry.getValue(); // value // 保存设备信息 sBuilder.append(rnKey); sBuilder.append(" = "); @@ -127,7 +144,7 @@ static String handleDeviceInfo(String eHint) { // 返回设备信息 return DEVICE_INFO_STR; } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleDeviceInfo"); + LogPrintUtils.eTag(TAG, e, "handlerDeviceInfo"); } return eHint; } @@ -135,14 +152,14 @@ static String handleDeviceInfo(String eHint) { // ================== 时间格式化 =================== /** 日期格式类型 */ - static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; + private static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; /** * 获取当前日期的字符串 * @return 字符串 */ @SuppressLint("SimpleDateFormat") - static String getDateNow() { + private static String getDateNow() { try { Calendar cld = Calendar.getInstance(); DateFormat df = new SimpleDateFormat(yyyyMMddHHmmss); @@ -159,7 +176,7 @@ static String getDateNow() { * 判断某个文件夹是否创建,未创建则创建(不能加入文件名) * @param fPath 文件夹路径 */ - static File createFile(String fPath) { + private static File createFile(String fPath) { try { File file = new File(fPath); // 当这个文件夹不存在的时候则创建文件夹 @@ -180,7 +197,7 @@ static File createFile(String fPath) { * @param fUrl 保存路径(包含文件名.后缀) * @return 是否保存成功 */ - static boolean saveFile(String txt, String fUrl) { + private static boolean saveFile(String txt, String fUrl) { try { // 保存内容到一个文件 FileOutputStream fos = new FileOutputStream(fUrl); @@ -202,7 +219,7 @@ static boolean saveFile(String txt, String fUrl) { * @param ex 错误信息 * @return */ - static String getThrowableMsg(String eHint, Throwable ex) { + private static String getThrowableMsg(String eHint, Throwable ex) { PrintWriter printWriter = null; try { if(ex != null) { @@ -231,7 +248,7 @@ static String getThrowableMsg(String eHint, Throwable ex) { * @param ex 错误信息 * @return */ - static String getThrowableNewLinesMsg(String eHint, Throwable ex) { + private static String getThrowableNewLinesMsg(String eHint, Throwable ex) { PrintWriter printWriter = null; try { if(ex != null) { @@ -269,14 +286,14 @@ static String getThrowableNewLinesMsg(String eHint, Throwable ex) { // ================== 对外公开方法 =================== /** - * App初始化调用方法 + * 初始化调用方法 */ - public static void appInit() { + public static void init() { // 如果版本信息为null,才进行处理 if (TextUtils.isEmpty(APP_VERSION_CODE) || TextUtils.isEmpty(APP_VERSION_NAME)) { - // 获取app版本信息 + // 获取 App 版本信息 String[] aVersion = getAppVersion(); - // 保存app版本信息 + // 保存 App 版本信息 APP_VERSION_NAME = aVersion[0]; APP_VERSION_CODE = aVersion[1]; } @@ -285,14 +302,14 @@ public static void appInit() { // 获取设备信息 getDeviceInfo(DEVICE_INFO_MAPS); // 转换字符串 - handleDeviceInfo(""); + handlerDeviceInfo(""); } } // ========= 保存错误日志信息 ========== /** - * 保存app错误日志 + * 保存 App 错误日志 * @param ex 错误信息 * @param fPath 保存路径 * @param fName 文件名(含后缀) @@ -306,7 +323,7 @@ public static boolean saveErrorLog(Throwable ex, String fPath, String fName, boo } /** - * 保存app错误日志 + * 保存 App 错误日志 * @param ex 错误信息 * @param head 顶部标题 * @param bottom 底部内容 @@ -319,13 +336,13 @@ public static boolean saveErrorLog(Throwable ex, String fPath, String fName, boo */ public static boolean saveErrorLog(Throwable ex, String head, String bottom, String fPath, String fName, boolean isNewLines, boolean printDevice, String... eHint) { // 处理可变参数(错误提示) - eHint = handleVariable(2, eHint); + eHint = handlerVariable(2, eHint); // 日志拼接 StringBuilder sBuilder = new StringBuilder(); // 防止文件夹不存在 createFile(fPath); // 设备信息 - String dInfo = handleDeviceInfo(eHint[0]); + String dInfo = handlerDeviceInfo(eHint[0]); // 如果存在顶部内容,则进行添加 if(!TextUtils.isEmpty(head)) { sBuilder.append(head); @@ -334,7 +351,7 @@ public static boolean saveErrorLog(Throwable ex, String head, String bottom, Str sBuilder.append(NEW_LINE_STR_X2); } // ============ - // 保存App信息 + // 保存 App 信息 sBuilder.append("date: " + getDateNow()); sBuilder.append(NEW_LINE_STR); sBuilder.append("versionName: " + APP_VERSION_NAME); @@ -374,7 +391,7 @@ public static boolean saveErrorLog(Throwable ex, String head, String bottom, Str } /** - * 保存app日志 + * 保存 App 日志 * @param log 日志信息 * @param fPath 保存路径 * @param fName 文件名(含后缀) @@ -382,12 +399,12 @@ public static boolean saveErrorLog(Throwable ex, String head, String bottom, Str * @param eHint 错误提示(无设备信息、失败信息获取失败) * @return */ - public static boolean saveLog(String log, String fPath, String fName, boolean printDevice, String... eHint){ + public static boolean saveLog(String log, String fPath, String fName, boolean printDevice, String... eHint) { return saveLog(log, null, null, fPath, fName, printDevice, eHint); } /** - * 保存app日志 + * 保存 App 日志 * @param log 日志信息 * @param head 顶部标题 * @param bottom 底部内容 @@ -397,15 +414,15 @@ public static boolean saveLog(String log, String fPath, String fName, boolean pr * @param eHint 错误提示(无设备信息、失败信息获取失败) * @return */ - public static boolean saveLog(String log, String head, String bottom, String fPath, String fName, boolean printDevice, String... eHint){ + public static boolean saveLog(String log, String head, String bottom, String fPath, String fName, boolean printDevice, String... eHint) { // 处理可变参数(错误提示) - eHint = handleVariable(2, eHint); + eHint = handlerVariable(2, eHint); // 日志拼接 StringBuilder sBuilder = new StringBuilder(); // 防止文件夹不存在 createFile(fPath); // 设备信息 - String dInfo = handleDeviceInfo(eHint[0]); + String dInfo = handlerDeviceInfo(eHint[0]); // 如果存在顶部内容,则进行添加 if(!TextUtils.isEmpty(head)) { sBuilder.append(head); @@ -414,7 +431,7 @@ public static boolean saveLog(String log, String head, String bottom, String fPa sBuilder.append(NEW_LINE_STR_X2); } // ============ - // 保存App信息 + // 保存 App 信息 sBuilder.append("date: " + getDateNow()); sBuilder.append(NEW_LINE_STR); sBuilder.append("versionName: " + APP_VERSION_NAME); @@ -453,7 +470,7 @@ public static boolean saveLog(String log, String head, String bottom, String fPa * @param vArrays 可变参数数组 * @return */ - public static String[] handleVariable(int length, String[] vArrays) { + public static String[] handlerVariable(int length, String[] vArrays) { // 处理后的数据, String[] hArrays = new String[length]; // 是否统一处理 @@ -492,7 +509,7 @@ public static String[] handleVariable(int length, String[] vArrays) { } } } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleVariable"); + LogPrintUtils.eTag(TAG, e, "handlerVariable"); } return hArrays; } diff --git a/DevLibUtils/src/main/java/dev/utils/app/FlashlightUtils.java b/DevLibUtils/src/main/java/dev/utils/app/FlashlightUtils.java index f759e88006..e608e45fa0 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/FlashlightUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/FlashlightUtils.java @@ -22,8 +22,12 @@ private FlashlightUtils() { } // 日志TAG - private final String TAG = FileRecordUtils.class.getSimpleName(); + private final String TAG = FlashlightUtils.class.getSimpleName(); + /** + * 获取 FlashlightUtils 实例 ,单例模式 + * @return + */ public static FlashlightUtils getInstance() { return LazyHolder.INSTANCE; } @@ -128,7 +132,7 @@ public void setFlashlightOn(Camera camera) { 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"); } } @@ -144,7 +148,7 @@ public void setFlashlightOff(Camera camera) { 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"); } } @@ -162,7 +166,7 @@ public boolean isFlashlightOn(Camera camera) { try { Camera.Parameters parameters = camera.getParameters(); return FLASH_MODE_TORCH.equals(parameters.getFlashMode()); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isFlashlightOn"); } return false; diff --git a/DevLibUtils/src/main/java/dev/utils/app/HtmlUtils.java b/DevLibUtils/src/main/java/dev/utils/app/HtmlUtils.java index 24c67fb056..1e71d316c1 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/HtmlUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/HtmlUtils.java @@ -17,8 +17,8 @@ private HtmlUtils() { * @param textView * @param content */ - public static void setHtmlText(TextView textView, String content){ - if (textView != null && content != null){ + public static void setHtmlText(TextView textView, String content) { + if (textView != null && content != null) { textView.setText(Html.fromHtml(content)); } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/IntentUtils.java b/DevLibUtils/src/main/java/dev/utils/app/IntentUtils.java index 0002476e65..adc651ffc5 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/IntentUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/IntentUtils.java @@ -1,13 +1,16 @@ package dev.utils.app; +import android.Manifest; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; +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; @@ -26,12 +29,21 @@ private IntentUtils() { } /** - * 获取安装 App(支持 8.0)的意图 + * 判断 Intent 是否可用 + * @param intent + * @return + */ + public static boolean isIntentAvailable(final Intent intent) { + return DevUtils.getContext().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; + } + + /** + * 获取安装 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)的意图 + * @return 安装 App(支持 8.0)的意图 */ public static Intent getInstallAppIntent(final String filePath, final String authority) { return getInstallAppIntent(FileUtils.getFileByPath(filePath), authority); @@ -66,8 +78,8 @@ public static Intent getInstallAppIntent(final File file, final String authority 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.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } intent.setDataAndType(data, type); return getIntent(intent, isNewTask); @@ -75,7 +87,7 @@ public static Intent getInstallAppIntent(final File file, final String authority /** * 获取卸载 App 的意图 - * @param packageName The name of the package. + * @param packageName * @return 卸载 App 的意图 */ public static Intent getUninstallAppIntent(final String packageName) { @@ -84,7 +96,7 @@ public static Intent getUninstallAppIntent(final String packageName) { /** * 获取卸载 App 的意图 - * @param packageName The name of the package. + * @param packageName * @param isNewTask 是否开启新的任务栈 * @return 卸载 App 的意图 */ @@ -96,7 +108,7 @@ public static Intent getUninstallAppIntent(final String packageName, final boole /** * 获取打开 App 的意图 - * @param packageName The name of the package. + * @param packageName * @return 打开 App 的意图 */ public static Intent getLaunchAppIntent(final String packageName) { @@ -105,7 +117,7 @@ public static Intent getLaunchAppIntent(final String packageName) { /** * 获取打开 App 的意图 - * @param packageName The name of the package. + * @param packageName * @param isNewTask 是否开启新的任务栈 * @return 打开 App 的意图 */ @@ -117,7 +129,7 @@ public static Intent getLaunchAppIntent(final String packageName, final boolean /** * 获取 App 具体设置的意图 - * @param packageName The name of the package. + * @param packageName * @return App 具体设置的意图 */ public static Intent getLaunchAppDetailsSettingsIntent(final String packageName) { @@ -126,7 +138,7 @@ public static Intent getLaunchAppDetailsSettingsIntent(final String packageName) /** * 获取 App 具体设置的意图 - * @param packageName The name of the package. + * @param packageName * @param isNewTask 是否开启新的任务栈 * @return App 具体设置的意图 */ @@ -138,7 +150,7 @@ public static Intent getLaunchAppDetailsSettingsIntent(final String packageName, /** * 获取 到应用商店app详情界面的意图 - * @param packageName 目标App的包名 + * @param packageName 目标 App 的包名 * @param marketPkg 应用商店包名 ,如果为""则由系统弹出应用商店列表供用户选择,否则调转到目标市场的应用详情界面,某些应用商店可能会失败 */ public static Intent getlaunchAppDetailIntent(final String packageName, final String marketPkg) { @@ -260,7 +272,7 @@ public static Intent getShareImageIntent(final String content, final Uri uri, fi /** * 获取其他应用组件的意图 - * @param packageName The name of the package. + * @param packageName * @param className 全类名 * @return 其他应用组件的意图 */ @@ -270,7 +282,7 @@ public static Intent getComponentIntent(final String packageName, final String c /** * 获取其他应用组件的意图 - * @param packageName The name of the package. + * @param packageName * @param className 全类名 * @param isNewTask 是否开启新的任务栈 * @return 其他应用组件的意图 @@ -281,7 +293,7 @@ public static Intent getComponentIntent(final String packageName, final String c /** * 获取其他应用组件的意图 - * @param packageName The name of the package. + * @param packageName * @param className 全类名 * @param bundle bundle * @return 其他应用组件的意图 @@ -292,7 +304,7 @@ public static Intent getComponentIntent(final String packageName, final String c /** * 获取其他应用组件的意图 - * @param packageName The name of the package. + * @param packageName * @param className 全类名 * @param bundle bundle * @param isNewTask 是否开启新的任务栈 @@ -352,6 +364,7 @@ public static Intent getDialIntent(final String phoneNumber, final boolean isNew * @param phoneNumber 电话号码 * @return 拨打电话意图 */ + @RequiresPermission(Manifest.permission.CALL_PHONE) public static Intent getCallIntent(final String phoneNumber) { return getCallIntent(phoneNumber, false); } @@ -363,8 +376,9 @@ public static Intent getCallIntent(final String phoneNumber) { * @param isNewTask 是否开启新的任务栈 * @return 拨打电话意图 */ + @RequiresPermission(Manifest.permission.CALL_PHONE) public static Intent getCallIntent(final String phoneNumber, final boolean isNewTask) { - Intent intent = new Intent("android.intent.action.CALL", Uri.parse("tel:" + phoneNumber)); + Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNumber)); return getIntent(intent, isNewTask); } @@ -427,9 +441,10 @@ public static void startSysSetting(Activity activity) { // // 跳转到 无线和网络 设置页面(可以设置移动网络,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); + if (activity != null) { + Intent intent = new Intent(Settings.ACTION_SETTINGS); + activity.startActivity(intent); + } } /** diff --git a/DevLibUtils/src/main/java/dev/utils/app/KeyBoardUtils.java b/DevLibUtils/src/main/java/dev/utils/app/KeyBoardUtils.java index 64f032209c..56b3b0899e 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/KeyBoardUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/KeyBoardUtils.java @@ -28,7 +28,6 @@ private KeyBoardUtils() { // 日志TAG private static final String TAG = KeyBoardUtils.class.getSimpleName(); - /** 默认延迟时间 */ private static int DELAY_MILLIS = 300; /** 键盘显示 */ @@ -36,7 +35,6 @@ private KeyBoardUtils() { /** 键盘隐藏 */ public static final int KEYBOARD_HIDE = 931; - /** * 避免输入法面板遮挡 manifest.xml 中 activity 中设置 * android:windowSoftInputMode="adjustPan" @@ -59,13 +57,13 @@ public static void setDelayMillis(int delayMillis) { /** * 打开软键盘 - * @param mEditText 输入框 + * @param editText 输入框 */ - public static void openKeyboard(EditText mEditText) { - if (mEditText != null) { + public static void openKeyboard(EditText editText) { + if (editText != null) { try { - InputMethodManager imm = (InputMethodManager) mEditText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(mEditText, InputMethodManager.SHOW_FORCED); + 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"); @@ -75,31 +73,31 @@ public static void openKeyboard(EditText mEditText) { /** * 打开软键盘 - * @param mEditText - * @param vHandler + * @param editText + * @param handler */ - public static void openKeyboard(final EditText mEditText, Handler vHandler){ - openKeyboard(mEditText, vHandler, DELAY_MILLIS); + public static void openKeyboard(final EditText editText, Handler handler) { + openKeyboard(editText, handler, DELAY_MILLIS); } /** * 打开软键盘 - * @param mEditText - * @param vHandler + * @param editText + * @param handler * @param delayMillis */ - public static void openKeyboard(final EditText mEditText, Handler vHandler, int delayMillis){ - if (vHandler != null && mEditText != null){ + public static void openKeyboard(final EditText editText, Handler handler, int delayMillis) { + if (handler != null && editText != null) { // 延迟打开 - vHandler.postDelayed(new Runnable() { + handler.postDelayed(new Runnable() { @Override public void run() { try { - mEditText.requestFocus(); - mEditText.setSelection(mEditText.getText().toString().length()); - } catch (Exception e){ + editText.requestFocus(); + editText.setSelection(editText.getText().toString().length()); + } catch (Exception e) { } - openKeyboard(mEditText); + openKeyboard(editText); } }, delayMillis); } @@ -121,21 +119,21 @@ public static void openKeyboard() { /** * 打开软键盘 - * @param vHandler + * @param handler */ - public static void openKeyboard(Handler vHandler){ - openKeyboard(vHandler, DELAY_MILLIS); + public static void openKeyboard(Handler handler) { + openKeyboard(handler, DELAY_MILLIS); } /** * 打开软键盘 - * @param vHandler + * @param handler * @param delayMillis */ - public static void openKeyboard(Handler vHandler, int delayMillis){ - if (vHandler != null && DevUtils.getContext() != null){ + public static void openKeyboard(Handler handler, int delayMillis) { + if (handler != null && DevUtils.getContext() != null) { // 延迟打开 - vHandler.postDelayed(new Runnable() { + handler.postDelayed(new Runnable() { @Override public void run() { openKeyboard(); @@ -150,13 +148,13 @@ public void run() { /** * 关闭软键盘 - * @param mEditText 输入框 + * @param editText 输入框 */ - public static void closeKeyboard(EditText mEditText) { - if (mEditText != null) { + public static void closeKeyboard(EditText editText) { + if (editText != null) { try { - InputMethodManager imm = (InputMethodManager) mEditText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); + InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "closeKeyboard"); } @@ -179,14 +177,14 @@ public static void closeKeyboard() { /** * 关闭软键盘 - * @param mActivity 当前页面 + * @param activity 当前页面 */ - public static void closeKeyboard(Activity mActivity) { - if (mActivity != null) { + public static void closeKeyboard(Activity activity) { + if (activity != null) { try { - InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mActivity.getWindow().peekDecorView().getWindowToken(), 0); - //imm.hideSoftInputFromWindow(mActivity.getCurrentFocus().getWindowToken(), 0); + InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(activity.getWindow().peekDecorView().getWindowToken(), 0); + //imm.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0); } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "closeKeyboard"); } @@ -195,14 +193,14 @@ public static void closeKeyboard(Activity mActivity) { /** * 关闭dialog中打开的键盘 - * @param mDialog + * @param dialog */ - public static void closeKeyboard(Dialog mDialog) { - if (mDialog != null) { + public static void closeKeyboard(Dialog dialog) { + if (dialog != 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); + InputMethodManager imm = (InputMethodManager) dialog.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(dialog.getWindow().peekDecorView().getWindowToken(), 0); + //imm.hideSoftInputFromWindow(dialog.getCurrentFocus().getWindowToken(), 0); } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "closeKeyboard"); } @@ -213,40 +211,46 @@ public static void closeKeyboard(Dialog mDialog) { /** * 关闭软键盘 - 特殊处理 - * @param mEditText - * @param mDialog + * @param editText + * @param dialog */ - public static void closeKeyBoardSpecial(EditText mEditText, Dialog mDialog){ + public static void closeKeyBoardSpecial(EditText editText, Dialog dialog) { try { // 关闭输入法 closeKeyboard(); // 关闭输入法 - closeKeyboard(mEditText); + closeKeyboard(editText); // 关闭输入法 - closeKeyboard(mDialog); - } catch (Exception e){ + closeKeyboard(dialog); + } 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 editText + * @param dialog + * @param handler + */ + public static void closeKeyBoardSpecial(final EditText editText, final Dialog dialog, Handler handler) { + closeKeyBoardSpecial(editText, dialog, handler, DELAY_MILLIS); } /** * 关闭软键盘 - 特殊处理(两个都关闭) - * @param mEditText - * @param mDialog - * @param vHandler + * @param editText + * @param dialog + * @param handler * @param delayMillis */ - public static void closeKeyBoardSpecial(final EditText mEditText, final Dialog mDialog, Handler vHandler, int delayMillis){ - if (vHandler != null){ + public static void closeKeyBoardSpecial(final EditText editText, final Dialog dialog, Handler handler, int delayMillis) { + if (handler != null) { // 延迟打开 - vHandler.postDelayed(new Runnable() { + handler.postDelayed(new Runnable() { @Override public void run() { - closeKeyBoardSpecial(mEditText, mDialog); + closeKeyBoardSpecial(editText, dialog); } }, delayMillis); } @@ -254,41 +258,50 @@ public void run() { // - - public static void closeKeyboard(final EditText mEditText, Handler vHandler){ - closeKeyboard(mEditText, vHandler, DELAY_MILLIS); + /** + * 关闭软键盘 + * @param editText + * @param handler + */ + public static void closeKeyboard(final EditText editText, Handler handler) { + closeKeyboard(editText, handler, DELAY_MILLIS); } /** * 关闭软键盘 - * @param mEditText - * @param vHandler + * @param editText + * @param handler * @param delayMillis */ - public static void closeKeyboard(final EditText mEditText, Handler vHandler, int delayMillis){ - if (vHandler != null && mEditText != null){ + public static void closeKeyboard(final EditText editText, Handler handler, int delayMillis) { + if (handler != null && editText != null) { // 延迟打开 - vHandler.postDelayed(new Runnable() { + handler.postDelayed(new Runnable() { @Override public void run() { - closeKeyboard(mEditText); + closeKeyboard(editText); } }, delayMillis); } } - public static void closeKeyboard(Handler vHandler){ - closeKeyboard(vHandler, DELAY_MILLIS); + /** + * 关闭软键盘 + * @param handler + */ + public static void closeKeyboard(Handler handler) { + closeKeyboard(handler, DELAY_MILLIS); } /** * 关闭软键盘 - * @param vHandler + * @param handler * @param delayMillis */ - public static void closeKeyboard(Handler vHandler, int delayMillis){ - if (vHandler != null && DevUtils.getContext() != null){ + public static void closeKeyboard(Handler handler, int delayMillis) { + if (handler != null && DevUtils.getContext() != null) { // 延迟打开 - vHandler.postDelayed(new Runnable() { + handler.postDelayed(new Runnable() { @Override public void run() { closeKeyboard(); @@ -297,45 +310,55 @@ public void run() { } } - public static void closeKeyboard(final Activity mActivity, Handler vHandler){ - closeKeyboard(mActivity, vHandler, DELAY_MILLIS); + /** + * 关闭软键盘 + * @param activity + * @param handler + */ + public static void closeKeyboard(final Activity activity, Handler handler) { + closeKeyboard(activity, handler, DELAY_MILLIS); } /** * 关闭软键盘 - * @param mActivity - * @param vHandler + * @param activity + * @param handler * @param delayMillis */ - public static void closeKeyboard(final Activity mActivity, Handler vHandler, int delayMillis){ - if (vHandler != null && mActivity != null){ + public static void closeKeyboard(final Activity activity, Handler handler, int delayMillis) { + if (handler != null && activity != null) { // 延迟打开 - vHandler.postDelayed(new Runnable() { + handler.postDelayed(new Runnable() { @Override public void run() { - closeKeyboard(mActivity); + closeKeyboard(activity); } }, delayMillis); } } - public static void closeKeyboard(final Dialog mDialog, Handler vHandler){ - closeKeyboard(mDialog, vHandler, DELAY_MILLIS); + /** + * 关闭软键盘 + * @param dialog + * @param handler + */ + public static void closeKeyboard(final Dialog dialog, Handler handler) { + closeKeyboard(dialog, handler, DELAY_MILLIS); } /** * 关闭软键盘 - * @param mDialog - * @param vHandler + * @param dialog + * @param handler * @param delayMillis */ - public static void closeKeyboard(final Dialog mDialog, Handler vHandler, int delayMillis){ - if (vHandler != null && mDialog != null){ + public static void closeKeyboard(final Dialog dialog, Handler handler, int delayMillis) { + if (handler != null && dialog != null) { // 延迟打开 - vHandler.postDelayed(new Runnable() { + handler.postDelayed(new Runnable() { @Override public void run() { - closeKeyboard(mDialog); + closeKeyboard(dialog); } }, delayMillis); } @@ -417,7 +440,7 @@ private static int getContentViewInvisibleHeight(final Activity activity) { Rect rect = new Rect(); contentView.getWindowVisibleDisplayFrame(rect); return contentView.getRootView().getHeight() - rect.height(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getContentViewInvisibleHeight"); return 0; } @@ -444,7 +467,7 @@ public void onGlobalLayout() { } } }); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "registerSoftInputChangedListener"); } } @@ -459,21 +482,21 @@ public static void registerSoftInputChangedListener2(final Activity activity, fi decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { - if (listener != null){ + 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "registerSoftInputChangedListener2"); } } @@ -484,6 +507,11 @@ public void onGlobalLayout() { /** 输入法弹出、隐藏改变事件 */ public interface OnSoftInputChangedListener { + /** + * 输入法弹出、隐藏改变通知 + * @param visible + * @param height + */ void onSoftInputChanged(boolean visible, int height); } @@ -497,7 +525,7 @@ 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"}; + String[] strArr = new String[]{"mCurRootView", "mServedView", "mNextServedView", "mLastSrvView"}; for (int i = 0; i < 3; i++) { try { Field declaredField = imm.getClass().getDeclaredField(strArr[i]); @@ -516,7 +544,7 @@ public static void fixSoftInputLeaks(final Context context) { } catch (Throwable th) { } } - } catch (Exception e){ + } 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 index 4caab7a7e0..dc2472cf5e 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/KeyguardUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/KeyguardUtils.java @@ -1,9 +1,11 @@ package dev.utils.app; +import android.Manifest; 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; @@ -20,16 +22,16 @@ public final class KeyguardUtils { /** 获取 KeyguardUtils 实例 ,单例模式 */ public static KeyguardUtils getInstance() { - if (INSTANCE == null){ + if (INSTANCE == null) { INSTANCE = new KeyguardUtils(); } return INSTANCE; } // 锁屏管理类 - KeyguardManager keyguardManager; + private KeyguardManager keyguardManager; // android-26 开始过时 - KeyguardManager.KeyguardLock keyguardLock; + private KeyguardManager.KeyguardLock keyguardLock; /** 构造函数 */ private KeyguardUtils() { @@ -71,10 +73,18 @@ public boolean inKeyguardRestrictedInputMode() { return keyguardManager.inKeyguardRestrictedInputMode(); } + /** + * 获取 KeyguardManager + * @return + */ public KeyguardManager getKeyguardManager() { return keyguardManager; } + /** + * 设置 KeyguardManager + * @param keyguardManager + */ public void setKeyguardManager(KeyguardManager keyguardManager) { this.keyguardManager = keyguardManager; } @@ -84,21 +94,27 @@ public void setKeyguardManager(KeyguardManager keyguardManager) { /** * 屏蔽系统的屏保 * 利用 disableKeyguard 解锁, 解锁并不是真正的解锁, 只是把锁屏的界面隐藏掉而已 + * */ + @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD) public void disableKeyguard() { keyguardLock.disableKeyguard(); } /** * 使能显示锁屏界面,如果你之前调用了disableKeyguard()方法取消锁屏界面,那么会马上显示锁屏界面。 + * */ + @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD) public void reenableKeyguard() { keyguardLock.reenableKeyguard(); } /** * 释放资源 + * */ + @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD) public void release() { if (keyguardLock != null) { // 禁用显示键盘锁定 @@ -106,14 +122,26 @@ public void release() { } } - public void newKeyguardLock (String tag){ + /** + * 生成一个新的 KeyguardLock, 并且设置 Tag + * @param tag + */ + public void newKeyguardLock (String tag) { keyguardLock = keyguardManager.newKeyguardLock(tag); } + /** + * 获取 KeyguardManager.KeyguardLock + * @return + */ public KeyguardManager.KeyguardLock getKeyguardLock() { return keyguardLock; } + /** + * 设置 KeyguardManager.KeyguardLock + * @param 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 index 18c514f20a..8812bf64ac 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ListenerUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ListenerUtils.java @@ -25,7 +25,7 @@ private ListenerUtils() { * @param view * @return */ - public static View.OnTouchListener getTouchListener(View view){ + public static View.OnTouchListener getTouchListener(View view) { return (View.OnTouchListener) getListenerInfoListener(view, "mOnTouchListener"); } @@ -34,13 +34,13 @@ public static View.OnTouchListener getTouchListener(View view){ * @param view * @return */ - public static Object getListenerInfo(View view){ + public static Object getListenerInfo(View view) { try { // 获取 ListenerInfo 对象 Field infoField = View.class.getDeclaredField("mListenerInfo"); infoField.setAccessible(true); return infoField.get(view); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getListenerInfo"); } return null; @@ -52,7 +52,7 @@ public static Object getListenerInfo(View view){ * @param listener * @return */ - public static Object getListenerInfoListener(View view, String listener){ + public static Object getListenerInfoListener(View view, String listener) { try { // 获取 ListenerInfo 对象 Object listenerInfo = getListenerInfo(view); @@ -62,7 +62,7 @@ public static Object getListenerInfoListener(View view, String listener){ listenerField.setAccessible(true); // 进行获取返回 return listenerField.get(listenerInfo); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getListenerInfoListener"); } return null; @@ -76,9 +76,9 @@ public static Object getListenerInfoListener(View view, String listener){ * @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++){ + 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); } @@ -91,9 +91,9 @@ public static void setOnClicks(final View view, final View.OnClickListener onCli * @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++){ + 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); } @@ -105,9 +105,9 @@ public static void setOnClicks(final Activity activity, final View.OnClickListen * @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++){ + 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); } @@ -135,7 +135,7 @@ private static T findViewById(View view, int id) { * @param * @return */ - private static T findViewById(Window window, int id){ + private static T findViewById(Window window, int id) { return window.findViewById(id); } diff --git a/DevLibUtils/src/main/java/dev/utils/app/LocationUtils.java b/DevLibUtils/src/main/java/dev/utils/app/LocationUtils.java index b7e869e7fa..065f2b9059 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/LocationUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/LocationUtils.java @@ -1,7 +1,6 @@ package dev.utils.app; import android.annotation.SuppressLint; -import android.content.Context; import android.content.Intent; import android.location.Address; import android.location.Criteria; @@ -33,7 +32,6 @@ private LocationUtils() { // 日志TAG private static final String TAG = LocationUtils.class.getSimpleName(); - /** 时间常量 = 2分钟 */ private static final int MINUTES_TWO = 1000 * 60 * 2; /** 定位改变通知事件 */ @@ -46,17 +44,16 @@ private LocationUtils() { /** * 获取位置, 需要先判断是否开启了定位 * - * @param context * @param listener * @param time 间隔时间 * @param distance 间隔距离 - * @return {@code Location} + * @return */ @SuppressLint("MissingPermission") - public static Location getLocation(Context context, LocationListener listener, long time, float distance) { + public static Location getLocation(LocationListener listener, long time, float distance) { Location location = null; try { - mLocationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE); + mLocationManager = (LocationManager) DevUtils.getContext().getSystemService(LOCATION_SERVICE); if (isLocationEnabled()) { mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, time, distance, listener); if (mLocationManager != null) { @@ -114,14 +111,14 @@ public static void openGpsSettings() { /** * 注册 - 使用完记得调用{@link #unregister()} - * 需添加权限 {@code } - * 需添加权限 {@code } - * 需添加权限 {@code } - * 如果{@code minDistance}为0,则通过{@code minTime}来定时更新; - * {@code minDistance}不为0,则以{@code minDistance}为准; + * + * + * + * 如果 minDistance 为 0,则通过 minTime 来定时更新; + * minDistance 不为 0,则以 minDistance 为准; * 两者都为0,则随时刷新。 - * @param minTime 位置信息更新周期(单位:毫秒) - * @param minDistance 位置变化最小距离:当位置距离变化超过此值时,将更新位置信息(单位:米) + * @param minTime 位置信息更新周期(单位:毫秒) + * @param minDistance 位置变化最小距离:当位置距离变化超过此值时,将更新位置信息(单位:米) * @param listener 位置刷新的回调接口 * @return true : 初始化成功, false : 初始化失败 */ @@ -143,7 +140,7 @@ public static boolean register(long minTime, long minDistance, OnLocationChangeL } /** - * 注销 + * 注销监听 */ @SuppressLint("MissingPermission") public static void unregister() { diff --git a/DevLibUtils/src/main/java/dev/utils/app/ManifestUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ManifestUtils.java index 58b5e495aa..017c6227d5 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ManifestUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ManifestUtils.java @@ -1,8 +1,12 @@ 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; @@ -13,20 +17,30 @@ */ public final class ManifestUtils { - private ManifestUtils(){ + private ManifestUtils() { } // 日志TAG private static final String TAG = ManifestUtils.class.getSimpleName(); /** - * 获取 Manifest Meta Data + * 获取 Application Meta Data * @param metaKey * @return */ public static String getMetaData(String metaKey) { + return getMetaData(DevUtils.getContext().getPackageName(), metaKey); + } + + /** + * 获取 Application Meta Data + * @param packageName + * @param metaKey + * @return + */ + public static String getMetaData(String packageName, String metaKey) { try { - ApplicationInfo appInfo = DevUtils.getContext().getPackageManager().getApplicationInfo(DevUtils.getContext().getPackageName(), PackageManager.GET_META_DATA); + ApplicationInfo appInfo = DevUtils.getContext().getPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA); String data = appInfo.metaData.getString(metaKey); return data; } catch (Exception e) { @@ -35,10 +49,174 @@ public static String getMetaData(String metaKey) { return null; } + // - + + /** + * 获取 Activity Meta Data + * @param cla + * @param metaKey + * @return + */ + public static String getMetaDataInActivity(Class cla, String metaKey) { + return getMetaDataInActivity(DevUtils.getContext().getPackageName(), cla.getCanonicalName(), metaKey); + } + + /** + * 获取 Activity Meta Data + * @param name 完整路径名 package.name => class.getCanonicalName() + * @param metaKey + * @return + */ + public static String getMetaDataInActivity(String name, String metaKey) { + return getMetaDataInActivity(DevUtils.getContext().getPackageName(), name, metaKey); + } + + /** + * 获取 Activity Meta Data + * @param packageName + * @param name 完整路径名 package.name => class.getCanonicalName() + * @param metaKey + * @return + */ + public static String getMetaDataInActivity(String packageName, String name, String metaKey) { + try { + ComponentName componentName = new ComponentName(packageName, name); + ActivityInfo activityInfo = DevUtils.getContext().getPackageManager().getActivityInfo(componentName, PackageManager.GET_META_DATA); + String data = activityInfo.metaData.getString(metaKey); + return data; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInActivity"); + } + return null; + } + + // - + + /** + * 获取 Service Meta Data + * @param cla + * @param metaKey + * @return + */ + public static String getMetaDataInService(Class cla, String metaKey) { + return getMetaDataInService(DevUtils.getContext().getPackageName(), cla.getCanonicalName(), metaKey); + } + + /** + * 获取 Service Meta Data + * @param name 完整路径名 package.name => class.getCanonicalName() + * @param metaKey + * @return + */ + public static String getMetaDataInService(String name, String metaKey) { + return getMetaDataInService(DevUtils.getContext().getPackageName(), name, metaKey); + } + + /** + * 获取 Service Meta Data + * @param packageName + * @param name 完整路径名 package.name => class.getCanonicalName() + * @param metaKey + * @return + */ + public static String getMetaDataInService(String packageName, String name, String metaKey) { + try { + ComponentName componentName = new ComponentName(packageName, name); + ServiceInfo serviceInfo = DevUtils.getContext().getPackageManager().getServiceInfo(componentName, PackageManager.GET_META_DATA); + String data = serviceInfo.metaData.getString(metaKey); + return data; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInService"); + } + return null; + } + + // - + + /** + * 获取 Receiver Meta Data + * @param cla + * @param metaKey + * @return + */ + public static String getMetaDataInReceiver(Class cla, String metaKey) { + return getMetaDataInReceiver(DevUtils.getContext().getPackageName(), cla.getCanonicalName(), metaKey); + } + + /** + * 获取 Receiver Meta Data + * @param name 完整路径名 package.name => class.getCanonicalName() + * @param metaKey + * @return + */ + public static String getMetaDataInReceiver(String name, String metaKey) { + return getMetaDataInReceiver(DevUtils.getContext().getPackageName(), name, metaKey); + } + + /** + * 获取 Receiver Meta Data + * @param packageName + * @param name 完整路径名 package.name => class.getCanonicalName() + * @param metaKey + * @return + */ + public static String getMetaDataInReceiver(String packageName, String name, String metaKey) { + try { + ComponentName componentName = new ComponentName(packageName, name); + ActivityInfo receiverInfo = DevUtils.getContext().getPackageManager().getReceiverInfo(componentName, PackageManager.GET_META_DATA); + String data = receiverInfo.metaData.getString(metaKey); + return data; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInReceiver"); + } + return null; + } + + // - + + /** + * 获取 ContentProvider Meta Data + * @param cla + * @param metaKey + * @return + */ + public static String getMetaDataInProvider(Class cla, String metaKey) { + return getMetaDataInProvider(DevUtils.getContext().getPackageName(), cla.getCanonicalName(), metaKey); + } + + /** + * 获取 ContentProvider Meta Data + * @param name 完整路径名 package.name => class.getCanonicalName() + * @param metaKey + * @return + */ + public static String getMetaDataInProvider(String name, String metaKey) { + return getMetaDataInProvider(DevUtils.getContext().getPackageName(), name, metaKey); + } + + /** + * 获取 ContentProvider Meta Data + * @param packageName + * @param name 完整路径名 package.name => class.getCanonicalName() + * @param metaKey + * @return + */ + public static String getMetaDataInProvider(String packageName, String name, String metaKey) { + try { + ComponentName componentName = new ComponentName(packageName, name); + ProviderInfo providerInfo = DevUtils.getContext().getPackageManager().getProviderInfo(componentName, PackageManager.GET_META_DATA); + String data = providerInfo.metaData.getString(metaKey); + return data; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMetaDataInProvider"); + } + return null; + } + // == /** - * 获取app版本信息 + * 获取 App 版本信息 * @return 0 = versionName , 1 = versionCode */ public static String[] getAppVersion() { @@ -58,7 +236,7 @@ public static String[] getAppVersion() { } /** - * 获取app版本号 + * 获取 App 版本号 * @return 当前版本Code */ public static int getAppVersionCode() { @@ -75,7 +253,7 @@ public static int getAppVersionCode() { } /** - * 获取app版本信息 + * 获取 App 版本信息 * @return 当前版本信息 */ public static String getAppVersionName() { @@ -94,7 +272,7 @@ public static String getAppVersionName() { // = /** - * 获取app版本号 - 内部判断 + * 获取 App 版本号 - 内部判断 * @param packageName * @return */ @@ -112,7 +290,7 @@ public static int getAppVersionCode(final String packageName) { /** - * 获取app版本名 - 对外显示 + * 获取 App 版本名 - 对外显示 * @param packageName * @return */ diff --git a/DevLibUtils/src/main/java/dev/utils/app/MemoryUtils.java b/DevLibUtils/src/main/java/dev/utils/app/MemoryUtils.java index cd40be9c65..3708001cb1 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/MemoryUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/MemoryUtils.java @@ -1,9 +1,9 @@ package dev.utils.app; -import android.annotation.TargetApi; 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; @@ -18,13 +18,14 @@ */ public final class MemoryUtils { - private MemoryUtils(){ + private MemoryUtils() { } // 日志TAG private static final String TAG = MemoryUtils.class.getSimpleName(); /** + * 打印内存信息 * Print memory info. such as: * MemTotal: 1864292 kB * MemFree: 779064 kB @@ -86,7 +87,7 @@ public static String printMemInfo() { * 获取内存信息 * @return */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) + @RequiresApi(Build.VERSION_CODES.CUPCAKE) public static ActivityManager.MemoryInfo getMemoryInfo() { ActivityManager am = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); @@ -95,10 +96,11 @@ public static ActivityManager.MemoryInfo getMemoryInfo() { } /** - * Print Memory info. + * 打印内存信息 + * @return */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) - public static ActivityManager.MemoryInfo printMemoryInfo(Context context) { + @RequiresApi(Build.VERSION_CODES.CUPCAKE) + public static ActivityManager.MemoryInfo printMemoryInfo() { ActivityManager.MemoryInfo mi = getMemoryInfo(); StringBuilder sBuilder = new StringBuilder(); sBuilder.append("_______ Memory : "); @@ -112,9 +114,11 @@ public static ActivityManager.MemoryInfo printMemoryInfo(Context context) { } /** + * 获取可用内存信息 * Get available memory info. + * @return */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) + @RequiresApi(Build.VERSION_CODES.CUPCAKE) public static String getAvailMemory() {// 获取android 当前可用内存大小 ActivityManager am = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); @@ -123,7 +127,6 @@ public static String getAvailMemory() {// 获取android 当前可用内存大小 return Formatter.formatFileSize(DevUtils.getContext(), mi.availMem); } - // 内存信息文件地址 private static final String MEM_INFO_PATH = "/proc/meminfo"; // 获取内存总大小 @@ -149,7 +152,7 @@ public static String getMemoryAvailable() { } /** - * 获取 type info + * 通过不同 type 获取对应的内存信息 * @param type * @return */ @@ -167,7 +170,7 @@ public static String getMemInfoIype(String type) { /* \\s表示 空格,回车,换行等空白符, +号表示一个或多个的意思 */ String[] array = str.split("\\s+"); - // 获得系统总内存,单位是KB,乘以1024转换为Byte + // 获取系统总内存,单位是KB,乘以1024转换为Byte long length = Long.valueOf(array[1]).longValue() * 1024; return android.text.format.Formatter.formatFileSize(DevUtils.getContext(), length); } catch (Exception e) { diff --git a/DevLibUtils/src/main/java/dev/utils/app/NetWorkUtils.java b/DevLibUtils/src/main/java/dev/utils/app/NetWorkUtils.java index ffc4636fa2..d00cc7ecf8 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/NetWorkUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/NetWorkUtils.java @@ -9,13 +9,16 @@ import android.os.Build; 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; @@ -24,6 +27,9 @@ import dev.DevUtils; import dev.utils.LogPrintUtils; +import static android.Manifest.permission.ACCESS_WIFI_STATE; +import static android.Manifest.permission.MODIFY_PHONE_STATE; + /** * detail: 网络管理工具类 * Created by Ttt @@ -69,12 +75,11 @@ public static boolean getMobileDataEnabled() { /** * 设置移动网络开关(无判断是否已开启移动网络) - 实际无效果, 非系统应用无权限 + * * @param isOpen 是否打开移动网络 * @return 是否执行正常 - * hint: - * 需系统应用、添加权限 - * */ + @RequiresPermission(MODIFY_PHONE_STATE) public static boolean setMobileDataEnabled(boolean isOpen) { try { // 属于5.0以下的使用 @@ -85,7 +90,7 @@ public static boolean setMobileDataEnabled(boolean isOpen) { Method mMethod = ConnectivityManager.class.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE); // 设置移动网络 mMethod.invoke(cManager, isOpen); - } else { // 需要 android.Manifest.permission.MODIFY_PHONE_STATE 权限, 普通APP无法获取 + } 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); @@ -101,10 +106,11 @@ public static boolean setMobileDataEnabled(boolean isOpen) { /** * 判断是否连接了网络 + * */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static boolean isConnect() { - // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) + // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) try { ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); if (cManager != null) { @@ -125,11 +131,12 @@ public static boolean isConnect() { /** * 获取连接的网络类型 + * * @return -1 = 等于未知 , 1 = Wifi, 2 = 移动网络 */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static int getConnectType() { - // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) + // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) try { // 获取网络连接状态 ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); @@ -154,6 +161,7 @@ public static int getConnectType() { /** * 判断是否连接Wifi(连接上、连接中) + * * @return */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @@ -163,6 +171,7 @@ public static boolean isConnWifi() { /** * 判断是否连接移动网络(连接上、连接中) + * * @return */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @@ -184,6 +193,7 @@ public enum NetworkType { /** * 判断网络是否可用 + * * @return true: 可用 false: 不可用 */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @@ -194,11 +204,12 @@ public static boolean isAvailable() { /** * 获取活动网络信息 - * @return NetworkInfo + * + * @return */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) private static NetworkInfo getActiveNetworkInfo() { - if (DevUtils.getContext() != null){ + if (DevUtils.getContext() != null) { try { return ((ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo(); } catch (Exception e) { @@ -212,10 +223,11 @@ private static NetworkInfo getActiveNetworkInfo() { /** * 判断是否4G网络 + * * @return */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public static boolean is4G(){ + public static boolean is4G() { return getNetworkType() == NetworkType.NETWORK_4G; } @@ -228,7 +240,7 @@ public static boolean getWifiEnabled() { @SuppressLint("WifiManagerLeak") WifiManager wifiManager = (WifiManager) DevUtils.getContext().getSystemService(Context.WIFI_SERVICE); return wifiManager.isWifiEnabled(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getWifiEnabled"); } return false; @@ -236,6 +248,7 @@ public static boolean getWifiEnabled() { /** * 判断wifi数据是否可用 + * * @return */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @@ -251,7 +264,7 @@ public static String getNetworkOperatorName() { try { TelephonyManager tm = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); return tm != null ? tm.getNetworkOperatorName() : null; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getNetworkOperatorName"); } return null; @@ -261,8 +274,8 @@ public static String getNetworkOperatorName() { /** * 获取当前网络类型 - * @return DevUtils.getContext() - * @return 网络类型 + * + * @return */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static NetworkType getNetworkType() { @@ -275,7 +288,7 @@ public static NetworkType getNetworkType() { // 属于可用则修改为未知 netType = NetworkType.NETWORK_UNKNOWN; // 获取类型 - switch (networkInfo.getType()){ + switch (networkInfo.getType()) { case ConnectivityManager.TYPE_WIFI: // 属于Wifi netType = NetworkType.NETWORK_WIFI; break; @@ -334,6 +347,31 @@ public static NetworkType getNetworkType() { return netType; } + /** + * 获取广播 ip 地址 + * @return + */ + 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 @@ -395,4 +433,72 @@ public static String getIPAddress(boolean useIPv4) { } return null; } + + /** + * 根据 WiFi 获取网络 IP 地址 + * + * @return + */ + @RequiresPermission(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 + */ + @RequiresPermission(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 + */ + @RequiresPermission(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 + */ + @RequiresPermission(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; + } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/NotificationUtils.java b/DevLibUtils/src/main/java/dev/utils/app/NotificationUtils.java index 43b22ed0fa..5b68bcb69a 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/NotificationUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/NotificationUtils.java @@ -19,7 +19,7 @@ */ public final class NotificationUtils { - private NotificationUtils(){ + private NotificationUtils() { } // 通知栏管理类 @@ -30,7 +30,7 @@ private NotificationUtils(){ * @return */ public static NotificationManager getNotificationManager() { - if (mNotificationManager == null){ + if (mNotificationManager == null) { mNotificationManager = (NotificationManager) DevUtils.getContext().getSystemService(Context.NOTIFICATION_SERVICE); } return mNotificationManager; @@ -39,8 +39,8 @@ public static NotificationManager getNotificationManager() { /** * 移除通知 - 移除所有通知(只是针对当前Context下的Notification) */ - public static void cancelAll(){ - if (getNotificationManager() != null){ + public static void cancelAll() { + if (getNotificationManager() != null) { mNotificationManager.cancelAll(); } } @@ -49,9 +49,9 @@ public static void cancelAll(){ * 移除通知 - 移除标记为id的通知 (只是针对当前Context下的所有Notification) * @param args */ - public static void cancel(final int... args){ - if (getNotificationManager() != null && args != null){ - for (int id : args){ + public static void cancel(final int... args) { + if (getNotificationManager() != null && args != null) { + for (int id : args) { mNotificationManager.cancel(id); } } @@ -62,8 +62,8 @@ public static void cancel(final int... args){ * @param tag * @param id */ - public static void cancel(final String tag, final int id){ - if (getNotificationManager() != null && tag != null){ + public static void cancel(final String tag, final int id) { + if (getNotificationManager() != null && tag != null) { mNotificationManager.cancel(tag, id); } } @@ -74,8 +74,8 @@ public static void cancel(final String tag, final int id){ * @param notification * @return */ - public static boolean notify(final int id, final Notification notification){ - if (getNotificationManager() != null && notification != null){ + public static boolean notify(final int id, final Notification notification) { + if (getNotificationManager() != null && notification != null) { mNotificationManager.notify(id, notification); return true; } @@ -89,8 +89,8 @@ public static boolean notify(final int id, final Notification notification){ * @param notification * @return */ - public static boolean notify(final String tag, final int id, final Notification notification){ - if (getNotificationManager() != null && tag != null && notification != null){ + 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; } @@ -110,7 +110,7 @@ public static boolean notify(final String tag, final int id, final Notification * @param id * @return */ - public static PendingIntent crePendingIntent(Intent intent, int id){ + public static PendingIntent createPendingIntent(Intent intent, int id) { /* 跳转Intent */ PendingIntent pIntent = PendingIntent.getActivity(DevUtils.getContext(), id, intent, PendingIntent.FLAG_UPDATE_CURRENT); return pIntent; @@ -123,8 +123,8 @@ public static PendingIntent crePendingIntent(Intent intent, int id){ * @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)); + public static Notification createNotification(int icon, String title, String msg) { + return createNotification(null, icon, title, title, msg, true, VibratePattern.obtain(0, 100, 300), LightPattern.obtain(Color.WHITE, 1000, 1000)); } /** @@ -136,8 +136,8 @@ public static Notification creNotification(int icon, String title, String msg) { * @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); + public static Notification createNotification(int icon, String title, String msg, VibratePattern vibratePattern, LightPattern lightPattern) { + return createNotification(null, icon, title, title, msg, true, vibratePattern, lightPattern); } /** @@ -152,7 +152,7 @@ public static Notification creNotification(int icon, String title, String msg, V * @param lightPattern * @return */ - public static Notification creNotification(PendingIntent pendingIntent, int icon, String ticker, String title, String msg, boolean isAutoCancel, VibratePattern vibratePattern, LightPattern lightPattern) { + public static Notification createNotification(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 @@ -174,11 +174,11 @@ public static Notification creNotification(PendingIntent pendingIntent, int icon // 设置时间 builder.setWhen(System.currentTimeMillis()); // 设置震动参数 - if (vibratePattern != null && !vibratePattern.isEmpty()){ + if (vibratePattern != null && !vibratePattern.isEmpty()) { builder.setVibrate(vibratePattern.vibrates); } // 设置 led 灯参数 - if (lightPattern != null){ + if (lightPattern != null) { builder.setLights(lightPattern.argb, lightPattern.durationMS, lightPattern.startOffMS); } // = 初始化 Notification 对象 = @@ -208,17 +208,17 @@ public static Notification creNotification(PendingIntent pendingIntent, int icon // notification.flags |= Notification.FLAG_AUTO_CANCEL; // } // // 设置震动参数 -// if (vibratePattern != null && !vibratePattern.isEmpty()){ +// if (vibratePattern != null && !vibratePattern.isEmpty()) { // notification.vibrate = vibratePattern.vibrates; // } // // 设置 led 灯参数 -// if (lightPattern != null){ +// 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){ +// } catch (Exception e) { // } // } // // 设置标题内容等 - 已经移除, 现在都是支持 4.0以上, 不需要兼容处理 @@ -264,8 +264,8 @@ public static LightPattern obtain(int argb, int startOffMS, int durationMS) { */ public static class VibratePattern { /** - * vibrate 属性是一个长整型的数组,用于设置手机静止和振动的时长,以毫秒为单位。 - * 参数中下标为0的值表示手机静止的时长,下标为1的值表示手机振动的时长, 下标为2的值又表示手机静止的时长,以此类推。 + * vibrate 属性是一个长整型的数组,用于设置手机静止和震动的时长,以毫秒为单位。 + * 参数中下标为0的值表示手机静止的时长,下标为1的值表示手机震动的时长, 下标为2的值又表示手机静止的时长,以此类推。 */ // long[] vibrates = { 0, 1000, 1000, 1000 }; private long[] vibrates = null; @@ -278,9 +278,9 @@ private VibratePattern(long[] vibrates) { * 判断是否为null * @return */ - public boolean isEmpty(){ - if (vibrates != null){ - if (vibrates.length != 0){ + public boolean isEmpty() { + if (vibrates != null) { + if (vibrates.length != 0) { return false; } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/OSUtils.java b/DevLibUtils/src/main/java/dev/utils/app/OSUtils.java index c37c46371e..515cd01a1d 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/OSUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/OSUtils.java @@ -14,8 +14,8 @@ /** * detail: 工具类: OS 系统相关 * Created by Ttt - * 简介 : 由于国内定制系统的泛滥, 不同定制系统的一些功能或实现方法会有所不同, 如果需要做到足够好的适配工作, 需要 对不同的定制系统做一些专门的适配. - * API : 获取 ROM 类型等 + * 简介 : 由于国内定制系统的泛滥, 不同定制系统的一些功能或实现方法会有所不同, 如果需要做到足够好的适配工作, 需要 对不同的定制系统做一些专门的适配. + * API : 获取 ROM 类型等 */ public final class OSUtils { @@ -342,18 +342,34 @@ public enum ROM { private int baseVersion = -1; private String version; + /** + * 设置 Rom 具体版本号 + * @param version + */ void setVersion(String version) { this.version = version; } + /** + * 设置 Rom 版本 + * @param baseVersion + */ void setBaseVersion(int baseVersion) { this.baseVersion = baseVersion; } + /** + * 获取 Rom 版本, 如 MIUI 9 获取的是 9 + * @return + */ public int getBaseVersion() { return baseVersion; } + /** + * 获取 Rom 具体版本号, 如 MIUI 9 8.4.26 获取的是 8.4.26 + * @return + */ public String getVersion() { return version; } diff --git a/DevLibUtils/src/main/java/dev/utils/app/PathUtils.java b/DevLibUtils/src/main/java/dev/utils/app/PathUtils.java index 4e7c2182c1..ed4aad1ee8 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/PathUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/PathUtils.java @@ -1,21 +1,15 @@ 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 java.io.File; import dev.DevUtils; /** * detail: 路径相关工具类 - * @author: Blankj + * Created by Ttt */ public final class PathUtils { @@ -23,338 +17,341 @@ private PathUtils() { } /** - * 获取 Android 系统根目录 - path: /system - * @return 系统根目录 + * 获取 Android 系统根目录 - path /system + * @return /system */ public static String getRootPath() { - return Environment.getRootDirectory().getAbsolutePath(); + return getAbsolutePath(Environment.getRootDirectory()); } /** - * 获取 data 目录 - path: /data - * @return data 目录 + * 获取 data 目录 - path /data + * @return /data */ public static String getDataPath() { - return Environment.getDataDirectory().getAbsolutePath(); + return getAbsolutePath(Environment.getDataDirectory()); } /** - * 获取缓存目录 - path: data/cache - * @return 缓存目录 + * 获取下载缓存目录 - path data/cache + * @return data/cache */ - public static String getIntDownloadCachePath() { - return Environment.getDownloadCacheDirectory().getAbsolutePath(); + public static String getDownloadCachePath() { + return getAbsolutePath(Environment.getDownloadCacheDirectory()); } /** - * 获取此应用的缓存目录 - path: /data/data/package/cache - * @return 此应用的缓存目录 + * 获取内存应用数据路径 - path /data/data/package + * @return /data/data/package */ - public static String getAppIntCachePath() { - return DevUtils.getContext().getCacheDir().getAbsolutePath(); + 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/files - * @return 此应用的文件目录 + * 获取内存应用代码缓存路径 - path /data/data/package/code_cache + * @return /data/data/package/code_cache */ - public static String getAppIntFilesPath() { - return DevUtils.getContext().getFilesDir().getAbsolutePath(); + 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/name - * @param name 数据库文件名 - * @return 数据库文件目录 + * 获取内存应用数据库路径 - path /data/data/package/databases. + * @return /data/data/package/databases. */ - public static String getAppIntDbPath(String name) { - return DevUtils.getContext().getDatabasePath(name).getAbsolutePath(); + public static String getInternalAppDbsPath() { + return DevUtils.getContext().getApplicationInfo().dataDir + "/databases"; } /** - * 获取 Android 外置储存的根目录 - path: /storage/emulated/0 - * @return 外置储存根目录 + * 获取内存应用数据库路径 - path /data/data/package/databases/name. + * @param name + * @return /data/data/package/databases/name */ - public static String getExtStoragePath() { - return Environment.getExternalStorageDirectory().getAbsolutePath(); + public static String getInternalAppDbPath(String name) { + return getAbsolutePath(DevUtils.getContext().getDatabasePath(name)); } /** - * 获取闹钟铃声目录 - path: /storage/emulated/0/Alarms - * @return 闹钟铃声目录 + * 获取内存应用文件路径 - path/data/data/package/files. + * @return /data/data/package/files */ - public static String getExtAlarmsPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS).getAbsolutePath(); + public static String getInternalAppFilesPath() { + return getAbsolutePath(DevUtils.getContext().getFilesDir()); } /** - * 获取相机拍摄的照片和视频的目录 - path: /storage/emulated/0/DCIM - * @return 照片和视频目录 + * 获取内存应用 SP 路径 - path/data/data/package/shared_prefs. + * @return /data/data/package/shared_prefs */ - public static String getExtDcimPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath(); + public static String getInternalAppSpPath() { + return DevUtils.getContext().getApplicationInfo().dataDir + "shared_prefs"; } /** - * 获取文档目录 - path: /storage/emulated/0/Documents - * @return 文档目录 + * 获取内存应用未备份文件路径 - path/data/data/package/no_backup. + * @return /data/data/package/no_backup */ - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - public static String getExtDocumentsPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath(); + 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/Download - * @return 下载目录 + * 获取外存路径 - path/storage/emulated/0. + * @return /storage/emulated/0 */ - public static String getExtDownloadsPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); + public static String getExternalStoragePath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(Environment.getExternalStorageDirectory()); } /** - * 获取视频目录 - path: /storage/emulated/0/Movies - * @return 视频目录 + * 获取外存音乐路径 - path/storage/emulated/0/Music. + * @return /storage/emulated/0/Music */ - public static String getExtMoviesPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath(); + public static String getExternalMusicPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC)); } /** - * 获取音乐目录 - path: /storage/emulated/0/Music - * @return 音乐目录 + * 获取外存播客路径 - path/storage/emulated/0/Podcasts. + * @return /storage/emulated/0/Podcasts */ - public static String getExtMusicPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath(); + public static String getExternalPodcastsPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS)); } /** - * 获取提示音目录 - path: /storage/emulated/0/Notifications - * @return 提示音目录 + * 获取外存铃声路径 - path/storage/emulated/0/Ringtones. + * @return /storage/emulated/0/Ringtones */ - public static String getExtNotificationsPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS).getAbsolutePath(); + public static String getExternalRingtonesPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES)); } /** - * 获取图片目录 - path: /storage/emulated/0/Pictures - * @return 图片目录 + * 获取外存闹铃路径 - path/storage/emulated/0/Alarms. + * @return /storage/emulated/0/Alarms */ - public static String getExtPicturesPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath(); + public static String getExternalAlarmsPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS)); } /** - * 获取 Podcasts 目录 - path: /storage/emulated/0/Podcasts - * @return Podcasts 目录 + * 获取外存通知路径 - path/storage/emulated/0/Notifications. + * @return /storage/emulated/0/Notifications */ - public static String getExtPodcastsPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS).getAbsolutePath(); + public static String getExternalNotificationsPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS)); } /** - * 获取铃声目录 - path: /storage/emulated/0/Ringtones - * @return 下载目录 + * 获取外存图片路径 - path/storage/emulated/0/Pictures. + * @return /storage/emulated/0/Pictures */ - public static String getExtRingtonesPath() { - return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES).getAbsolutePath(); + public static String getExternalPicturesPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)); } /** - * 获取此应用在外置储存中的缓存目录 - path: /storage/emulated/0/Android/data/package/cache - * @return 此应用在外置储存中的缓存目录 + * 获取外存影片路径 - path/storage/emulated/0/Movies. + * @return /storage/emulated/0/Movies */ - public static String getAppExtCachePath() { - return DevUtils.getContext().getExternalCacheDir().getAbsolutePath(); + public static String getExternalMoviesPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)); } /** - * 获取此应用在外置储存中的文件目录 - path: /storage/emulated/0/Android/data/package/files - * @return 此应用在外置储存中的文件目录 + * 获取外存下载路径 - path/storage/emulated/0/Download. + * @return /storage/emulated/0/Download */ - public static String getAppExtFilePath() { - return DevUtils.getContext().getExternalFilesDir(null).getAbsolutePath(); + public static String getExternalDownloadsPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)); } /** - * 获取此应用在外置储存中的闹钟铃声目录 - path: /storage/emulated/0/Android/data/package/files/Alarms - * @return 此应用在外置储存中的闹钟铃声目录 + * 获取外存数码相机图片路径 - path/storage/emulated/0/DCIM. + * @return /storage/emulated/0/DCIM */ - public static String getAppExtAlarmsPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_ALARMS).getAbsolutePath(); + public static String getExternalDcimPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)); } /** - * 获取此应用在外置储存中的相机目录 - path: /storage/emulated/0/Android/data/package/files/DCIM - * @return 此应用在外置储存中的相机目录 + * 获取外存文档路径 - path/storage/emulated/0/Documents. + * @return /storage/emulated/0/Documents */ - public static String getAppExtDcimPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_DCIM).getAbsolutePath(); + public static String getExternalDocumentsPath() { + if (isExternalStorageDisable()) 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/files/Documents - * @return 此应用在外置储存中的文档目录 + * 获取外存应用数据路径 - path/storage/emulated/0/Android/data/package. + * @return /storage/emulated/0/Android/data/package */ - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - public static String getAppExtDocumentsPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath(); + public static String getExternalAppDataPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalCacheDir().getParentFile()); } /** - * 获取此应用在外置储存中的闹钟目录 - path: /storage/emulated/0/Android/data/package/files/Download - * @return 此应用在外置储存中的闹钟目录 + * 获取外存应用缓存路径 - path/storage/emulated/0/Android/data/package/cache. + * @return /storage/emulated/0/Android/data/package/cache */ - public static String getAppExtDownloadPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); + public static String getExternalAppCachePath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalCacheDir()); } /** - * 获取此应用在外置储存中的视频目录 - path: /storage/emulated/0/Android/data/package/files/Movies - * @return 此应用在外置储存中的视频目录 + * 获取外存应用文件路径 - path/storage/emulated/0/Android/data/package/files. + * @return /storage/emulated/0/Android/data/package/files */ - public static String getAppExtMoviesPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_MOVIES).getAbsolutePath(); + public static String getExternalAppFilesPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(null)); } /** - * 获取此应用在外置储存中的音乐目录 - path: /storage/emulated/0/Android/data/package/files/Music - * @return 此应用在外置储存中的音乐目录 + * 获取外存应用音乐路径 - path/storage/emulated/0/Android/data/package/files/Music. + * @return /storage/emulated/0/Android/data/package/files/Music */ - public static String getAppExtMusicPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_MUSIC).getAbsolutePath(); + public static String getExternalAppMusicPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_MUSIC)); } /** - * 获取此应用在外置储存中的提示音目录 - path: /storage/emulated/0/Android/data/package/files/Notifications - * @return 此应用在外置储存中的提示音目录 + * 获取外存应用播客路径 - path/storage/emulated/0/Android/data/package/files/Podcasts. + * @return /storage/emulated/0/Android/data/package/files/Podcasts */ - public static String getAppExtNotificationsPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_NOTIFICATIONS).getAbsolutePath(); + public static String getExternalAppPodcastsPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_PODCASTS)); } /** - * 获取此应用在外置储存中的图片目录 - path: /storage/emulated/0/Android/data/package/files/Pictures - * @return 此应用在外置储存中的图片目录 + * 获取外存应用铃声路径 - path/storage/emulated/0/Android/data/package/files/Ringtones. + * @return /storage/emulated/0/Android/data/package/files/Ringtones */ - public static String getAppExtPicturesPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath(); + public static String getExternalAppRingtonesPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_RINGTONES)); } /** - * 获取此应用在外置储存中的 Podcasts 目录 - path: /storage/emulated/0/Android/data/package/files/Podcasts - * @return 此应用在外置储存中的 Podcasts 目录 + * 获取外存应用闹铃路径 - path/storage/emulated/0/Android/data/package/files/Alarms. + * @return /storage/emulated/0/Android/data/package/files/Alarms */ - public static String getAppExtPodcastsPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_PODCASTS).getAbsolutePath(); + public static String getExternalAppAlarmsPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_ALARMS)); } /** - * 获取此应用在外置储存中的铃声目录 - path: /storage/emulated/0/Android/data/package/files/Ringtones - * @return 此应用在外置储存中的铃声目录 + * 获取外存应用通知路径 - path/storage/emulated/0/Android/data/package/files/Notifications. + * @return /storage/emulated/0/Android/data/package/files/Notifications */ - public static String getAppExtRingtonesPath() { - return DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_RINGTONES).getAbsolutePath(); + public static String getExternalAppNotificationsPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_NOTIFICATIONS)); } /** - * 获取此应用的 Obb 目录 - path: /storage/emulated/0/Android/obb/package - * 一般用来存放游戏数据包 - * @return 此应用的 Obb 目录 + * 获取外存应用图片路径 - path/storage/emulated/0/Android/data/package/files/Pictures. + * @return path /storage/emulated/0/Android/data/package/files/Pictures */ - public static String getObbPath() { - return DevUtils.getContext().getObbDir().getAbsolutePath(); + public static String getExternalAppPicturesPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)); } - 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(); + /** + * 获取外存应用影片路径 - path/storage/emulated/0/Android/data/package/files/Movies. + * @return /storage/emulated/0/Android/data/package/files/Movies + */ + public static String getExternalAppMoviesPath() { + if (isExternalStorageDisable()) 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 (isExternalStorageDisable()) 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 (isExternalStorageDisable()) 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 (isExternalStorageDisable()) return ""; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(null)) + "/Documents"; } - return null; + return getAbsolutePath(DevUtils.getContext().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)); } - private static boolean isExternalStorageDocument(Uri uri) { - return "com.android.externalstorage.documents".equals(uri.getAuthority()); + /** + * 获取外存应用 OBB 路径 - path/storage/emulated/0/Android/obb/package. + * @return /storage/emulated/0/Android/obb/package + */ + public static String getExternalAppObbPath() { + if (isExternalStorageDisable()) return ""; + return getAbsolutePath(DevUtils.getContext().getObbDir()); } - private static boolean isDownloadsDocument(Uri uri) { - return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + /** + * 判断SDCard是否正常挂载 + * @return + */ + private static boolean isExternalStorageDisable() { + return !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } - private static boolean isMediaDocument(Uri uri) { - return "com.android.providers.media.documents".equals(uri.getAuthority()); + /** + * 获取 getAbsolutePath 路径, 防止 File 为 null + * @param file + * @return + */ + private static String getAbsolutePath(File file){ + if (file == null) return null; + return file.getAbsolutePath(); } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/PermissionUtils.java b/DevLibUtils/src/main/java/dev/utils/app/PermissionUtils.java index 5950762201..9360ca287c 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/PermissionUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/PermissionUtils.java @@ -59,9 +59,9 @@ public final class PermissionUtils { /** Permission 请求Code */ public static final int P_REQUEST_CODE = 100; /** 全部权限 */ - private final static Set mAllPermissions = new HashSet<>(1); + private static final Set mAllPermissions = new HashSet<>(1); // 判断是否请求过 - private boolean isRequest = false; + private boolean request = false; /** 申请的权限 */ private List mPermissions = new ArrayList<>(); /** 准备请求的权限 */ @@ -103,12 +103,12 @@ private static synchronized void initializePermissionsMap() { * 构造函数 * @param permissions */ - private PermissionUtils(final String... permissions){ + private PermissionUtils(final String... permissions) { mPermissions.clear(); // 防止数据为null - if (permissions != null && permissions.length != 0){ + if (permissions != null && permissions.length != 0) { // 遍历全部需要申请的权限 - for (String permission : permissions){ + for (String permission : permissions) { mPermissions.add(permission); } } @@ -151,7 +151,7 @@ private static boolean isGranted(final Context context, final String permission) * @param permission * @return */ - public static boolean shouldShowRequestPermissionRationale(Activity activity, final String permission){ + public static boolean shouldShowRequestPermissionRationale(Activity activity, final String permission) { return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission); } @@ -162,7 +162,7 @@ public static boolean shouldShowRequestPermissionRationale(Activity activity, fi * @param permissions * @return */ - public static PermissionUtils permission(final String... permissions){ + public static PermissionUtils permission(final String... permissions) { return new PermissionUtils(permissions); } @@ -170,8 +170,8 @@ public static PermissionUtils permission(final String... permissions){ * 设置回调方法 * @param callBack */ - public PermissionUtils callBack(PermissionCallBack callBack){ - if (isRequest){ + public PermissionUtils callBack(PermissionCallBack callBack) { + if (request) { return this; } this.mCallBack = callBack; @@ -182,11 +182,11 @@ public PermissionUtils callBack(PermissionCallBack callBack){ * 权限判断处理 * @return -1 已经请求过, 0 = 不处理, 1 = 需要请求 */ - private int checkPermissions(){ - if (isRequest){ + private int checkPermissions() { + if (request) { return -1; // 已经申请过 } - isRequest = true; + request = true; // 如果 SDK 版本小于 23 则直接通过 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 表示全部权限都通过 @@ -194,11 +194,11 @@ private int checkPermissions(){ // 处理请求回调 requestCallback(); } else { - for (String permission : mPermissions){ + for (String permission : mPermissions) { // 首先判断是否存在 - if (mAllPermissions.contains(permission)){ + if (mAllPermissions.contains(permission)) { // 判断是否通过请求 - if (isGranted(DevUtils.getContext(), permission)){ + if (isGranted(DevUtils.getContext(), permission)) { mPermissionsGranted.add(permission); // 权限允许通过 } else { mPermissionsRequest.add(permission); // 准备请求权限 @@ -209,7 +209,7 @@ private int checkPermissions(){ } } // 判断是否存在等待请求的权限 - if (mPermissionsRequest.isEmpty()){ + if (mPermissionsRequest.isEmpty()) { // 处理请求回调 requestCallback(); } else { // 表示需要申请 @@ -227,8 +227,8 @@ private int checkPermissions(){ * 无需调用以下代码判断 * boolean isGranted = PermissionUtils.isGranted(Manifest.permission.xx); */ - public void request(){ - if (checkPermissions() == 1){ + public void request() { + if (checkPermissions() == 1) { // 如果 SDK 版本大于 23 才请求 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { sInstance = this; @@ -242,7 +242,7 @@ public void request(){ * 请求权限 * @param activity {@link Fragment#getActivity()} */ - public void request(Activity activity){ + public void request(Activity activity) { request(activity, P_REQUEST_CODE); } @@ -251,8 +251,8 @@ public void request(Activity activity){ * @param activity {@link Fragment#getActivity()} * @param requestCode */ - public void request(Activity activity, int requestCode){ - if (checkPermissions() == 1 && activity != null){ + 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; @@ -290,12 +290,20 @@ public interface PermissionCallBack { @RequiresApi(api = Build.VERSION_CODES.M) public static class PermissionActivity extends Activity { - public static void start(final Context context) { + /** + * 跳转 PermissionActivity 请求权限 内部方法 + * @param 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); @@ -304,6 +312,12 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { requestPermissions(sInstance.mPermissionsRequest.toArray(new String[size]), 1); } + /** + * 请求权限回调 + * @param requestCode + * @param permissions + * @param grantResults + */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { sInstance.onRequestPermissionsResultCommon(this); // 处理回调 @@ -315,11 +329,11 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis /** 内部请求回调, 统一处理方法 */ private void requestCallback() { - if (mCallBack != null){ + if (mCallBack != null) { // 判断是否允许全部权限 boolean isGrantedAll = (mPermissions.size() == mPermissionsGranted.size()); // 允许则触发回调 - if (isGrantedAll){ + if (isGrantedAll) { new Handler(mLooper).post(new Runnable() { @Override public void run() { @@ -355,7 +369,7 @@ private void onRequestPermissionsResultCommon(final Activity activity) { private void getPermissionsStatus(final Activity activity) { for (String permission : mPermissionsRequest) { // 判断是否通过请求 - if (isGranted(activity, permission)){ + if (isGranted(activity, permission)) { mPermissionsGranted.add(permission); } else { // 未授权 @@ -374,8 +388,8 @@ private void getPermissionsStatus(final Activity activity) { * 请求权限回调 - 需要在 onRequestPermissionsResult 回调里面调用 * @param activity */ - public static void onRequestPermissionsResult(Activity activity){ - if (activity != null && sInstance != null){ + public static void onRequestPermissionsResult(Activity activity) { + if (activity != null && sInstance != null) { // 触发回调 sInstance.onRequestPermissionsResultCommon(activity); } diff --git a/DevLibUtils/src/main/java/dev/utils/app/PhoneUtils.java b/DevLibUtils/src/main/java/dev/utils/app/PhoneUtils.java index ca38436017..38b26d2d62 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/PhoneUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/PhoneUtils.java @@ -1,16 +1,20 @@ package dev.utils.app; +import android.Manifest; 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.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; @@ -26,11 +30,20 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.UUID; import dev.DevUtils; import dev.utils.LogPrintUtils; +import static android.Manifest.permission.CALL_PHONE; +import static android.Manifest.permission.READ_CONTACTS; +import static android.Manifest.permission.READ_EXTERNAL_STORAGE; +import static android.Manifest.permission.READ_PHONE_STATE; +import static android.Manifest.permission.READ_SMS; +import static android.Manifest.permission.SEND_SMS; +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; + /** * detail: 手机相关工具类 * Created by Ttt @@ -88,7 +101,7 @@ public static String getUserCountry() { /** * 判断地区,是否属于国内 - * @return 状态码 1 属于国内(中国),2 属于 国外,3 属于无SIM卡 + * @return 状态码 1 属于国内(中国),2 属于 国外,3 属于无SIM卡 */ public static int judgeArea() { // 默认属于无sim卡 @@ -129,18 +142,55 @@ 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isPhone"); } return false; } + /** + * 获取 MEID 移动设备识别码 + * + * @return + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public static String getMEID() { + try { + TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return telephonyManager.getMeid(); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMEID"); + } + return null; + } + + /** + * 获取 MEID 移动设备识别码 + * + * @param slotId + * @return + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public static String getMEID(int slotId) { + try { + TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return telephonyManager.getMeid(slotId); + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getMEID"); + } + return null; + } + /** * 获取 IMEI 码 * * @return IMEI 码 * ========== - * IMEI是International Mobile Equipment Identity (国际移动设备标识)的简称 + * IMEI是International Mobile Equipment Identity (国际移动设备标识)的简称 * IMEI由15位数字组成的”电子串号”,它与每台手机一一对应,而且该码是全世界唯一的 * 其组成为: * 1. 前6位数(TAC)是”型号核准号码”,一般代表机型 @@ -148,17 +198,61 @@ public static boolean isPhone() { * 3. 之后的6位数(SNR)是”串号”,一般代表生产顺序号 * 4. 最后1位数(SP)通常是”0″,为检验码,目前暂备用 */ - @SuppressLint({"HardwareIds", "MissingPermission"}) + @SuppressLint({"HardwareIds"}) + @RequiresPermission(READ_PHONE_STATE) public static String getIMEI() { try { TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); - return telephonyManager != null ? telephonyManager.getDeviceId() : null; - } catch (Exception e){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return telephonyManager.getImei(); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + try { + Class clazz = telephonyManager.getClass(); + Method getImeiMethod = clazz.getDeclaredMethod("getImei"); + getImeiMethod.setAccessible(true); + String imei = (String) getImeiMethod.invoke(telephonyManager); + if (imei != null) return imei; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIMEI"); + } + } + return telephonyManager.getDeviceId(); + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getIMEI"); } return null; } + /** + * 获取 IMEI 码 + * + * @param slotId + * @return IMEI 码 + */ + @SuppressLint({"HardwareIds"}) + @RequiresPermission(READ_PHONE_STATE) + public static String getIMEI(int slotId) { + try { + TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return telephonyManager.getImei(slotId); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + try { + Class clazz = telephonyManager.getClass(); + Method getImeiMethod = clazz.getDeclaredMethod("getImei"); + getImeiMethod.setAccessible(true); + String imei = (String) getImeiMethod.invoke(telephonyManager, slotId); + if (imei != null) return imei; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIMEI"); + } + } + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getIMEI"); + } + return getIMEI(); + } + /** * 获取 IMSI 码 * @@ -170,18 +264,19 @@ public static String getIMEI() { * MCC:Mobile Country Code,移动国家码,共3位,中国为460; * MNC:Mobile NetworkCode,移动网络码,共2位 * 在中国,移动的代码为电00和02,联通的代码为01,电信的代码为03 - * 合起来就是(也是Android手机中APN配置文件中的代码): + * 合起来就是(也是Android手机中APN配置文件中的代码): * 中国移动:46000 46002 * 中国联通:46001 * 中国电信:46003 * 举例,一个典型的IMSI号码为460030912121001 */ - @SuppressLint({"HardwareIds", "MissingPermission"}) + @SuppressLint({"HardwareIds"}) + @RequiresPermission(READ_PHONE_STATE) public static String getIMSI() { try { TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); return telephonyManager != null ? telephonyManager.getSubscriberId() : null; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getIMSI"); } return null; @@ -192,7 +287,7 @@ public static String getIMSI() { * @param IMSI * @return */ - public static String getIMSIIDName(String IMSI){ + public static String getIMSIIDName(String IMSI) { if (IMSI != null) { if (IMSI.startsWith("46000") || IMSI.startsWith("46002")) { return "中国移动"; @@ -217,10 +312,10 @@ public static int getPhoneType() { try { TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); return telephonyManager != null ? telephonyManager.getPhoneType() : -1; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getPhoneType"); } - return -1; + return 0; } /** @@ -231,7 +326,7 @@ 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isSimCardReady"); } return false; @@ -245,7 +340,7 @@ public static String getSimOperatorName() { try { TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); return telephonyManager != null ? telephonyManager.getSimOperatorName() : null; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getSimOperatorName"); } return null; @@ -272,7 +367,7 @@ public static String getSimOperatorByMnc() { default: return operator; } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getSimOperatorByMnc"); } return null; @@ -283,13 +378,14 @@ public static String getSimOperatorByMnc() { * * @return */ - @SuppressLint({"HardwareIds", "MissingPermission"}) - public static String getDeviceId(){ + @SuppressLint({"HardwareIds"}) + @RequiresPermission(READ_PHONE_STATE) + public static String getDeviceId() { try { TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); if (telephonyManager == null) return ""; return telephonyManager.getDeviceId(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getDeviceId"); } return null; @@ -297,10 +393,17 @@ public static String getDeviceId(){ /** * 返回设备序列化 + * * @return */ - public static String getSerialNumber(){ - return android.os.Build.SERIAL; + @RequiresPermission(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; } /** @@ -320,9 +423,11 @@ public static String getAndroidId() { /** * 获取设备唯一id + * * @return */ - public static String getUUID(){ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public static String getUUID() { String androidId = getAndroidId() + ""; String deviceId = getDeviceId() + ""; String serialNumber = getSerialNumber() + ""; @@ -351,7 +456,8 @@ public static String getUUID(){ * SubscriberId(IMSI) = 460030419724900
* VoiceMailNumber = *86
*/ - @SuppressLint({"HardwareIds", "MissingPermission"}) + @SuppressLint({"HardwareIds"}) + @RequiresPermission(READ_PHONE_STATE) public static String getPhoneStatus() { try { TelephonyManager telephonyManager = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); @@ -373,7 +479,7 @@ public static String getPhoneStatus() { stringBuffer.append("SubscriberId(IMSI) = " + telephonyManager.getSubscriberId() + "(" + getIMSIIDName(telephonyManager.getSubscriberId()) + ")"+ "\n"); stringBuffer.append("VoiceMailNumber = " + telephonyManager.getVoiceMailNumber() + "\n"); return stringBuffer.toString(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getPhoneStatus"); } return ""; @@ -382,27 +488,48 @@ public static String getPhoneStatus() { /** * 跳至拨号界面 * @param phoneNumber 电话号码 + * @return */ - public static void dial(final String phoneNumber) { - DevUtils.getContext().startActivity(IntentUtils.getDialIntent(phoneNumber, true)); + 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 */ - public static void call(final String phoneNumber) { - DevUtils.getContext().startActivity(IntentUtils.getCallIntent(phoneNumber, true)); + @RequiresPermission(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 */ - public static void sendSms(final String phoneNumber, final String content) { - DevUtils.getContext().startActivity(IntentUtils.getSendSmsIntent(phoneNumber, content, true)); + 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; } /** @@ -411,9 +538,10 @@ public static void sendSms(final String phoneNumber, final String content) { * @param phoneNumber 接收号码 * @param content 短信内容 */ + @RequiresPermission(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(), 0); + PendingIntent sentIntent = PendingIntent.getBroadcast(DevUtils.getContext(), 0, new Intent("send"), 0); SmsManager smsManager = SmsManager.getDefault(); if (content.length() >= 70) { List ms = smsManager.divideMessage(content); @@ -429,10 +557,11 @@ public static void sendSmsSilent(final String phoneNumber, final String content) * 获取手机联系人 * * - * return + * @return */ - public static ArrayList> getAllContactInfo() { - ArrayList> list = new ArrayList<>(); + @RequiresPermission(allOf = { READ_EXTERNAL_STORAGE, READ_CONTACTS}) + public static List> getAllContactInfo() { + List> list = new ArrayList<>(); // 1.获取内容解析者 ContentResolver resolver = DevUtils.getContext().getContentResolver(); // 2.获取内容提供者的地址:com.android.contacts @@ -460,7 +589,7 @@ public static ArrayList> getAllContactInfo() { // 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(); + Map map = new HashMap<>(); // 8.解析 c if (c != null) { while (c.moveToNext()) { @@ -484,7 +613,7 @@ public static ArrayList> getAllContactInfo() { } } } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getAllContactInfo"); } finally { // 12.关闭 cursor @@ -499,12 +628,12 @@ public static ArrayList> getAllContactInfo() { * 获取手机联系人 * @return */ - public static ArrayList> getAllContactInfo2() { - ArrayList> list = new ArrayList<>(); + 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()) { - HashMap map = new HashMap<>(); + Map map = new HashMap<>(); // 电话号码 String phoneNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)).trim().replaceAll(" ", ""); // 手机联系人名字 @@ -525,15 +654,18 @@ public static ArrayList> getAllContactInfo2() { /** * 打开手机联系人界面点击联系人后便获取该号码 + * @param activity */ - 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); + public static void getContactNum(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); + } // @Override -// protected void onActivityResult ( int requestCode, int resultCode, Intent data){ +// protected void onActivityResult (int requestCode, int resultCode, Intent data) { // super.onActivityResult(requestCode, resultCode, data); // if (data != null) { // Uri uri = data.getData(); @@ -556,6 +688,7 @@ public static void getContactNum() { * * */ + @RequiresPermission(allOf = {WRITE_EXTERNAL_STORAGE, READ_SMS}) public static void getAllSMS() { // 1.获取短信 // 1.1获取内容解析者 @@ -642,8 +775,11 @@ public static class TeleInfo { public int phoneType_1; public int phoneType_2; - @Override - public String toString() { + /** + * 打印 TeleInfo 信息 + * @return + */ + public String printInfo() { return "TeleInfo{" + "imsi_1='" + imsi_1 + '\'' + ", imsi_2='" + imsi_2 + '\'' + @@ -656,10 +792,10 @@ public String toString() { } /** - * MTK Phone. * 获取 MTK 神机的双卡 IMSI、IMSI 信息 + * @return */ - public static TeleInfo getMtkTeleInfo(Context context) { + public static TeleInfo getMtkTeleInfo() { TeleInfo teleInfo = new TeleInfo(); try { Class phone = Class.forName("com.android.internal.telephony.Phone"); @@ -672,7 +808,7 @@ public static TeleInfo getMtkTeleInfo(Context context) { fields2.setAccessible(true); int simId_2 = (Integer) fields2.get(null); - TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + TelephonyManager tm = (TelephonyManager) DevUtils.getContext().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); @@ -698,13 +834,15 @@ public static TeleInfo getMtkTeleInfo(Context context) { } /** - * MTK Phone. * 获取 MTK 神机的双卡 IMSI、IMSI 信息 + * + * @return */ - public static TeleInfo getMtkTeleInfo2(Context context) { + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public static TeleInfo getMtkTeleInfo2() { TeleInfo teleInfo = new TeleInfo(); try { - TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + TelephonyManager tm = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); Class phone = Class.forName("com.android.internal.telephony.Phone"); Field fields1 = phone.getField("GEMINI_SIM_1"); fields1.setAccessible(true); @@ -738,15 +876,15 @@ public static TeleInfo getMtkTeleInfo2(Context context) { } /** - * Qualcomm Phone. * 获取 高通 神机的双卡 IMSI、IMSI 信息 + * @return */ - public static TeleInfo getQualcommTeleInfo(Context context) { + public static TeleInfo getQualcommTeleInfo() { TeleInfo teleInfo = new TeleInfo(); try { - TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + TelephonyManager tm = (TelephonyManager) DevUtils.getContext().getSystemService(Context.TELEPHONY_SERVICE); Class simTMclass = Class.forName("android.telephony.MSimTelephonyManager"); - Object sim = context.getSystemService("phone_msim"); + Object sim = DevUtils.getContext().getSystemService("phone_msim"); int simId_1 = 0; int simId_2 = 1; @@ -774,9 +912,11 @@ public static TeleInfo getQualcommTeleInfo(Context context) { } /** - * Spreadtrum Phone. * 获取 展讯 神机的双卡 IMSI、IMSI 信息 + * + * @return */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) public static TeleInfo getSpreadtrumTeleInfo() { TeleInfo teleInfo = new TeleInfo(); try { @@ -805,4 +945,13 @@ public static TeleInfo getSpreadtrumTeleInfo() { } return teleInfo; } + + /** + * 判断 Intent 是否可用 + * @param intent + * @return + */ + private static boolean isIntentAvailable(final Intent intent) { + return DevUtils.getContext().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; + } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/PollingUtils.java b/DevLibUtils/src/main/java/dev/utils/app/PollingUtils.java index 66fc5cf046..2059437e6b 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/PollingUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/PollingUtils.java @@ -1,26 +1,26 @@ 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; +import android.support.annotation.RequiresApi; /** * detail: 轮询工具类 - * @author MaTianyu + * Created by Ttt */ public final class PollingUtils { - private PollingUtils(){ + private PollingUtils() { } /** * 开启轮询 */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) + @RequiresApi(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); @@ -29,7 +29,7 @@ public static void startPolling(Context context, int mills, PendingIntent pendin /** * 停止轮询 */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) + @RequiresApi(Build.VERSION_CODES.CUPCAKE) public static void stopPolling(Context context, PendingIntent pendingIntent) { AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); manager.cancel(pendingIntent); @@ -38,7 +38,7 @@ public static void stopPolling(Context context, PendingIntent pendingIntent) { /** * 开启轮询服务 */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) + @RequiresApi(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); @@ -47,9 +47,9 @@ public static void startPollingService(Context context, int mills, Class cls, } /** - * 停止启轮询服务 + * 停止轮询服务 */ - @TargetApi(Build.VERSION_CODES.CUPCAKE) + @RequiresApi(Build.VERSION_CODES.CUPCAKE) public static void stopPollingService(Context context, Class cls, String action) { Intent intent = new Intent(context, cls); intent.setAction(action); diff --git a/DevLibUtils/src/main/java/dev/utils/app/PowerManagerUtils.java b/DevLibUtils/src/main/java/dev/utils/app/PowerManagerUtils.java index 6fc0249400..18f837c9da 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/PowerManagerUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/PowerManagerUtils.java @@ -21,7 +21,7 @@ public final class PowerManagerUtils { /** 获取 PowerManagerUtils 实例 ,单例模式 */ public static PowerManagerUtils getInstance() { - if (INSTANCE == null){ + if (INSTANCE == null) { INSTANCE = new PowerManagerUtils(); } return INSTANCE; @@ -36,8 +36,11 @@ public static PowerManagerUtils getInstance() { private PowerManagerUtils() { // 获取系统服务 powerManager = (PowerManager) DevUtils.getContext().getSystemService(Context.POWER_SERVICE); - // 电源管理锁 - wakeLock = powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.FULL_WAKE_LOCK, "PowerManagerUtils"); + try { + // 电源管理锁 + wakeLock = powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.FULL_WAKE_LOCK, "PowerManagerUtils"); + } catch (Exception e){ + } } /** @@ -73,18 +76,34 @@ public void turnScreenOff() { } } + /** + * 获取 PowerManager.WakeLock + * @return + */ public PowerManager.WakeLock getWakeLock() { return wakeLock; } + /** + * 设置 PowerManager.WakeLock + * @param wakeLock + */ public void setWakeLock(PowerManager.WakeLock wakeLock) { this.wakeLock = wakeLock; } + /** + * 获取 PowerManager + * @return + */ public PowerManager getPowerManager() { return powerManager; } + /** + * 设置 PowerManager + * @param powerManager + */ public void setPowerManager(PowerManager powerManager) { this.powerManager = powerManager; } @@ -93,8 +112,8 @@ public void setPowerManager(PowerManager powerManager) { * 设置屏幕常亮 * @param activity */ - public static void setBright(Activity activity){ - if (activity != null){ + public static void setBright(Activity activity) { + if (activity != null) { setBright(activity.getWindow()); } } @@ -103,7 +122,7 @@ public static void setBright(Activity activity){ * 设置屏幕常亮 * @param window {@link Activity#getWindow()} */ - public static void setBright(Window window){ + public static void setBright(Window window) { if (window != null) { window.setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } @@ -114,15 +133,19 @@ public static void setBright(Window window){ * @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(); // 常量, 持有不黑屏 + public static PowerManager.WakeLock setWakeLockToBright() { + try { + // onResume() + PowerManager.WakeLock mWakeLock = PowerManagerUtils.getInstance().getPowerManager().newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "setWakeLockToBright"); + mWakeLock.acquire(); // 常量, 持有不黑屏 // // onPause() -// if (mWakeLock != null){ +// if (mWakeLock != null) { // mWakeLock.release(); // 释放资源, 到休眠时间自动黑屏 // } - return mWakeLock; + return mWakeLock; + } catch (Exception e){ + } + return null; } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/ProcessUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ProcessUtils.java index d41f53bf66..690c700124 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ProcessUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ProcessUtils.java @@ -1,6 +1,5 @@ package dev.utils.app; -import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; @@ -10,6 +9,7 @@ import android.content.pm.ResolveInfo; import android.provider.Settings; import android.support.annotation.NonNull; +import android.support.annotation.RequiresPermission; import android.text.TextUtils; import java.io.BufferedReader; @@ -24,6 +24,9 @@ import dev.DevUtils; import dev.utils.LogPrintUtils; +import static android.Manifest.permission.KILL_BACKGROUND_PROCESSES; +import static android.Manifest.permission.PACKAGE_USAGE_STATS; + /** * detail: 进程相关工具类 * Created by Ttt @@ -36,6 +39,47 @@ private ProcessUtils() { // 日志TAG private static final String TAG = ProcessUtils.class.getSimpleName(); + /** + * 销毁自身进程 + */ + public static void kill() { + kill(android.os.Process.myPid()); + } + + /** + * 销毁进程 + * @param pid + */ + public static void kill(int pid) { + //从操作系统中结束掉当前程序的进程 + android.os.Process.killProcess(pid); + } + + /** + * 判断是否当前进程 + * @return + */ + public static boolean isCurProcess() { + return DevUtils.getContext().getPackageName().equals(getCurProcessName()); + } + + /** + * 获取当前进程的名字 + * 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); + List listInfos = activityManager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo appProcess : listInfos) { + if (appProcess.pid == pid) { + return appProcess.processName; + } + } + return null; + } + /** * 获取进程号对应的进程名 * @param pid 进程号 => android.os.Process.myPid() @@ -64,28 +108,67 @@ public static String getProcessName(int pid) { } /** - * 获得当前进程的名字 - * hit: 获取当前进程 DevUtils.getContext().getApplicationInfo().packageName - * @return 进程号 + * 根据包名获取进程id + * @param packageName + * @return */ - public static String getCurProcessName() { - int pid = android.os.Process.myPid(); + public static int getPid(String packageName) { + if (TextUtils.isEmpty(packageName)) { + return 0; + } + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + List listInfos = activityManager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo appProcess : listInfos) { + if (appProcess.processName.equals(packageName)) { + return appProcess.pid; + } + } + return 0; + } + + /** + * 根据 pid 获取进程信息 + * @param pid + * @return + */ + public static ActivityManager.RunningAppProcessInfo getRunningAppProcessInfo(int pid) { ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); - for (ActivityManager.RunningAppProcessInfo appProcess : activityManager.getRunningAppProcesses()) { + List listInfos = activityManager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo appProcess : listInfos) { if (appProcess.pid == pid) { - return appProcess.processName; + return appProcess; } } return null; } + /** + * 根据包名获取进程信息 + * @param packageName + * @return + */ + public static ActivityManager.RunningAppProcessInfo getRunningAppProcessInfo(String packageName) { + if (TextUtils.isEmpty(packageName)) return null; + ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); + List listInfos = activityManager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo appProcess : listInfos) { + if (appProcess.processName.equals(packageName)) { + return appProcess; + } + } + return null; + } + + // = + /** * 获取前台线程包名 - * => 属于系统权限 + * * @return 前台应用包名 */ + @RequiresPermission(PACKAGE_USAGE_STATS) public static String getForegroundProcessName() { - if (DevUtils.getContext() == null){ + if (DevUtils.getContext() == null) { return null; } ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); @@ -135,8 +218,9 @@ public static String getForegroundProcessName() { * * @return 后台服务进程 */ + @RequiresPermission(KILL_BACKGROUND_PROCESSES) public static Set getAllBackgroundProcesses() { - if (DevUtils.getContext() == null){ + if (DevUtils.getContext() == null) { return Collections.emptySet(); } ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); @@ -156,9 +240,9 @@ public static Set getAllBackgroundProcesses() { * * @return 被暂时杀死的服务集合 */ - @SuppressLint("MissingPermission") + @RequiresPermission(KILL_BACKGROUND_PROCESSES) public static Set killAllBackgroundProcesses() { - if (DevUtils.getContext() == null){ + if (DevUtils.getContext() == null) { return null; } ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); @@ -166,15 +250,15 @@ public static Set killAllBackgroundProcesses() { List listInfos = activityManager.getRunningAppProcesses(); Set set = new HashSet<>(); for (ActivityManager.RunningAppProcessInfo apInfo : listInfos) { - for (String pkg : apInfo.pkgList) { - activityManager.killBackgroundProcesses(pkg); - set.add(pkg); + for (String packageName : apInfo.pkgList) { + activityManager.killBackgroundProcesses(packageName); + set.add(packageName); } } listInfos = activityManager.getRunningAppProcesses(); for (ActivityManager.RunningAppProcessInfo aInfo : listInfos) { - for (String pkg : aInfo.pkgList) { - set.remove(pkg); + for (String packageName : aInfo.pkgList) { + set.remove(packageName); } } return set; @@ -183,10 +267,10 @@ public static Set killAllBackgroundProcesses() { /** * 杀死后台服务进程 * - * @param packageName The name of the package. + * @param packageName * @return true : 杀死成功, false : 杀死失败 */ - @SuppressLint("MissingPermission") + @RequiresPermission(KILL_BACKGROUND_PROCESSES) public static boolean killBackgroundProcesses(@NonNull final String packageName) { ActivityManager activityManager = (ActivityManager) DevUtils.getContext().getSystemService(Context.ACTIVITY_SERVICE); if (activityManager == null) return false; diff --git a/DevLibUtils/src/main/java/dev/utils/app/ResourceUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ResourceUtils.java index 7a2ba5ca79..923b109432 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ResourceUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ResourceUtils.java @@ -48,21 +48,21 @@ private ResourceUtils() { 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getString"); } return ""; } /** - * 获得颜色 + * 获取 Color * @param colorId 颜色id * @return 颜色 */ @@ -76,7 +76,7 @@ public static int getColor(int colorId) { } /** - * 获得Drawable + * 获取 Drawable * @param drawableId Drawable的id * @return Drawable */ @@ -90,7 +90,7 @@ public static Drawable getDrawable(int drawableId) { } /** - * 根据资源名获得资源id + * 根据资源名获取资源id * @param name 资源名 * @param type 资源类型 * @return 资源id,找不到返回0 @@ -107,11 +107,11 @@ public static int getResourceId(String name, String type) { } /** - * 获取资源id + * 获取 drawable id * @param imageName * @return */ - public static int getDrawableId2(String imageName){ + public static int getDrawableId2(String imageName) { Class mipmap = R.drawable.class; try { Field field = mipmap.getField(imageName); @@ -129,56 +129,56 @@ public static int getDrawableId2(String imageName){ // == /** - * 获取 layout 布局文件 + * 获取 layout 布局文件 id * @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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getLayoutId"); } return 0; } /** - * 获取 string 值 + * 获取 string id * @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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getStringId"); } return 0; } /** - * 获取 drawable + * 获取 drawable id * @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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getDrawableId"); } return 0; } /** - * 获取 mipmap + * 获取 mipmap id * @param resName * @return */ public static int getMipmapId(String resName) { try { return DevUtils.getContext().getResources().getIdentifier(resName, "mipmap", DevUtils.getContext().getPackageName()); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getMipmapId"); } return 0; @@ -186,28 +186,28 @@ public static int getMipmapId(String resName) { /** - * 获取 style + * 获取 style id * @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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getStyleId"); } return 0; } /** - * 获取 styleable + * 获取 styleable id * @param resName styleable 的名称 * @return styleable */ - public static Object getStyleableId(String resName){ + public static Object getStyleableId(String resName) { try { return DevUtils.getContext().getResources().getIdentifier(resName, "styleable", DevUtils.getContext().getPackageName()); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getStyleableId"); } return 0; @@ -215,14 +215,14 @@ public static Object getStyleableId(String resName){ /** - * 获取 anim + * 获取 anim id * @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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getAnimId"); } return 0; @@ -236,21 +236,21 @@ public static int getAnimId(String resName) { public static int getId(String resName) { try { return DevUtils.getContext().getResources().getIdentifier(resName, "id", DevUtils.getContext().getPackageName()); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getId"); } return 0; } /** - * color + * 获取 color id * @param resName color 名称 * @return */ public static int getColorId(String resName) { try { return DevUtils.getContext().getResources().getIdentifier(resName, "color", DevUtils.getContext().getPackageName()); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getColorId"); } return 0; @@ -296,7 +296,7 @@ public static byte[] readBytesFromAssets(String fileName) { public static String readStringFromAssets(String fileName) { try { return new String(readBytesFromAssets(fileName), "UTF-8"); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "readStringFromAssets"); } return null; @@ -340,7 +340,7 @@ public static byte[] readBytesFromRaw(int resId) { public static String readStringFromRaw(int resId) { try { return new String(readBytesFromRaw(resId), "UTF-8"); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "readStringFromRaw"); } return null; @@ -349,11 +349,11 @@ public static String readStringFromRaw(int resId) { // == /** - * 获取 Assets 资源文件数据(返回ArrayList 一行的全部内容属于一个索引) + * 获取 Assets 资源文件数据(返回List 一行的全部内容属于一个索引) * @param fileName 资源文件名,可分成,如根目录,a.txt 或者子目录 /www/a.html * @return */ - public static ArrayList geFileToListFromAssets(String fileName) { + public static List geFileToListFromAssets(String fileName) { if (DevUtils.getContext() != null && !TextUtils.isEmpty(fileName)) { InputStream iStream = null; InputStreamReader inReader = null; @@ -362,7 +362,7 @@ public static ArrayList geFileToListFromAssets(String fileName) { iStream = DevUtils.getContext().getResources().getAssets().open(fileName); inReader = new InputStreamReader(iStream); bufReader = new BufferedReader(inReader); - ArrayList fileContent = new ArrayList(); + List fileContent = new ArrayList<>(); String line; while ((line = bufReader.readLine()) != null) { fileContent.add(line); @@ -383,7 +383,7 @@ public static ArrayList geFileToListFromAssets(String fileName) { } /** - * 从res/raw 中获取内容。(返回ArrayList 一行的全部内容属于一个索引) + * 从res/raw 中获取内容。(返回List一行的全部内容属于一个索引) * @param resId 资源id * @return */ @@ -396,7 +396,7 @@ public static List geFileToListFromRaw(int resId) { iStream = DevUtils.getContext().getResources().openRawResource(resId); inReader = new InputStreamReader(iStream); bufReader = new BufferedReader(inReader); - List fileContent = new ArrayList(); + List fileContent = new ArrayList<>(); String line = null; while ((line = bufReader.readLine()) != null) { fileContent.add(line); @@ -416,8 +416,8 @@ public static List geFileToListFromRaw(int resId) { return null; } - // ======== + /** * 从Assets 资源中获取内容并保存到本地 * @param fileName 资源文件名,可分成,如根目录,a.txt 或者子目录 /www/a.html diff --git a/DevLibUtils/src/main/java/dev/utils/app/SDCardUtils.java b/DevLibUtils/src/main/java/dev/utils/app/SDCardUtils.java index ff1f9bddfb..ba843f2668 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/SDCardUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/SDCardUtils.java @@ -1,9 +1,11 @@ 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.support.annotation.RequiresApi; import android.text.format.Formatter; import java.io.File; @@ -41,7 +43,7 @@ public static boolean isSDCardEnable() { } /** - * 获取SD卡路径(File对象) + * 获取SD卡路径(File对象) * @return */ public static File getSDCardFile() { @@ -49,7 +51,7 @@ public static File getSDCardFile() { } /** - * 获取SD卡路径(无添加 -> / -> File.separator) + * 获取SD卡路径(无添加 -> / -> File.separator) * @return */ public static String getSDCardPath() { @@ -211,9 +213,10 @@ public static long[] getBlockSizeInfos(String path) { } /** - * 获得 SD 卡总大小 + * 获取 SD 卡总大小 * @return */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public static String getSDTotalSize() { try { File path = Environment.getExternalStorageDirectory(); @@ -221,16 +224,17 @@ public static String getSDTotalSize() { long blockSize = stat.getBlockSizeLong(); long totalBlocks = stat.getBlockCountLong(); return Formatter.formatFileSize(DevUtils.getContext(), blockSize * totalBlocks); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getSDTotalSize"); } return "unknown"; } /** - * 获得 SD 卡剩余容量,即可用大小 + * 获取 SD 卡剩余容量,即可用大小 * @return */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public static String getSDAvailableSize() { try { File path = Environment.getExternalStorageDirectory(); @@ -238,16 +242,17 @@ public static String getSDAvailableSize() { long blockSize = stat.getBlockSizeLong(); long availableBlocks = stat.getAvailableBlocksLong(); return Formatter.formatFileSize(DevUtils.getContext(), blockSize * availableBlocks); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getSDAvailableSize"); } return "unknown"; } /** - * 获得机身内存总大小 + * 获取机身内存总大小 * @return */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public static String getRomTotalSize() { try { File path = Environment.getDataDirectory(); @@ -262,9 +267,10 @@ public static String getRomTotalSize() { } /** - * 获得机身可用内存 + * 获取机身可用内存 * @return */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public static String getRomAvailableSize() { try { File path = Environment.getDataDirectory(); @@ -311,12 +317,12 @@ public static File getCacheFile(String fPath) { * @param fPath 文件地址 * @return */ - public static String getCachePath(String fPath){ + 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 index d711d6e1ae..0219b9ce96 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ScreenUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ScreenUtils.java @@ -1,5 +1,6 @@ package dev.utils.app; +import android.Manifest; import android.app.Activity; import android.app.KeyguardManager; import android.content.Context; @@ -12,6 +13,8 @@ import android.os.Build; import android.provider.Settings; import android.support.annotation.NonNull; +import android.support.annotation.RequiresApi; +import android.support.annotation.RequiresPermission; import android.util.DisplayMetrics; import android.view.Surface; import android.view.View; @@ -37,7 +40,7 @@ private ScreenUtils() { private static final String TAG = ScreenUtils.class.getSimpleName(); /** - * 通过上下文获取 DisplayMetrics (获取关于显示的通用信息,如显示大小,分辨率和字体) + * 通过 Context 获取 DisplayMetrics (获取关于显示的通用信息,如显示大小,分辨率和字体) * @return */ private static DisplayMetrics getDisplayMetrics() { @@ -57,8 +60,7 @@ private static DisplayMetrics getDisplayMetrics() { // == ----------------------------------------- == // /** -// * 通过上下文获取屏幕宽度 -// * @param mContext +// * 通过 Context 获取屏幕宽度 // * @return // */ // @SuppressWarnings("deprecation") @@ -77,12 +79,11 @@ private static DisplayMetrics getDisplayMetrics() { // } catch (Exception e) { // LogPrintUtils.eTag(TAG, e, "getScreenWidth"); // } -// return -1; +// return 0; // } // // /** -// * 通过上下文获取屏幕高度 -// * @param mContext +// * 通过 Context 获取屏幕高度 // * @return // */ // @SuppressWarnings("deprecation") @@ -101,12 +102,11 @@ private static DisplayMetrics getDisplayMetrics() { // } catch (Exception e) { // LogPrintUtils.eTag(TAG, e, "getScreenHeight"); // } -// return -1; +// return 0; // } // // /** -// * 通过上下文获取屏幕宽度高度 -// * @param mContext +// * 通过 Context 获取屏幕宽度高度 // * @return int[] 0 = 宽度,1 = 高度 // */ // @SuppressWarnings("deprecation") @@ -131,7 +131,7 @@ private static DisplayMetrics getDisplayMetrics() { // } /** - * 获取屏幕的宽度(单位:px) + * 获取屏幕的宽度(单位:px) * @return 屏幕宽 */ public static int getScreenWidth() { @@ -147,14 +147,14 @@ public static int getScreenWidth() { windowManager.getDefaultDisplay().getSize(point); } return point.x; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getScreenWidth"); } - return -1; + return 0; } /** - * 获取屏幕的高度(单位:px) + * 获取屏幕的高度(单位:px) * @return 屏幕高 */ public static int getScreenHeight() { @@ -170,14 +170,14 @@ public static int getScreenHeight() { windowManager.getDefaultDisplay().getSize(point); } return point.y; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getScreenHeight"); } - return -1; + return 0; } /** - * 通过上下文获取屏幕宽度高度 + * 通过 Context 获取屏幕宽度高度 * @return point.x 宽, point.y 高 */ public static Point getScreenWidthHeightToPoint() { @@ -201,7 +201,7 @@ public static Point getScreenWidthHeightToPoint() { } /** - * 通过上下文获取屏幕宽度高度 + * 通过 Context 获取屏幕宽度高度 * @return int[] 0 = 宽度,1 = 高度 */ public static int[] getScreenWidthHeight() { @@ -234,7 +234,7 @@ public static String getScreenSize() { int[] whArys = getScreenWidthHeight(); // 返回分辨率信息 return whArys[1] + "x" + whArys[0]; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getScreenSize"); } return "unknown"; @@ -244,6 +244,7 @@ public static String getScreenSize() { * 获取屏幕英寸 例5.5英寸 * @return */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) public static String getScreenSizeOfDevice() { // https://blog.csdn.net/lincyang/article/details/42679589 try { @@ -257,7 +258,7 @@ public static String getScreenSizeOfDevice() { // 转换大小 DecimalFormat df = new DecimalFormat("#.0"); return df.format(screenInches); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getScreenSizeOfDevice"); } return "unknown"; @@ -266,7 +267,7 @@ public static String getScreenSizeOfDevice() { // == /** - * 通过上下文获取屏幕密度 + * 通过 Context 获取屏幕密度 * @return */ public static float getDensity() { @@ -274,17 +275,17 @@ public static float getDensity() { // 获取屏幕信息 DisplayMetrics dMetrics = getDisplayMetrics(); if (dMetrics != null) { - // 屏幕密度(0.75 / 1.0 / 1.5 / 2.0) + // 屏幕密度(0.75 / 1.0 / 1.5 / 2.0) return dMetrics.density; } } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getDensity"); } - return -1; + return 0; } /** - * 通过上下文获取屏幕密度Dpi + * 通过 Context 获取屏幕密度Dpi * @return */ public static int getDensityDpi() { @@ -292,17 +293,17 @@ public static int getDensityDpi() { // 获取屏幕信息 DisplayMetrics dMetrics = getDisplayMetrics(); if (dMetrics != null) { - // 屏幕密度DPI(120 / 160 / 240 / 320) + // 屏幕密度DPI(120 / 160 / 240 / 320) return dMetrics.densityDpi; } } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getDensityDpi"); } - return -1; + return 0; } /** - * 通过上下文获取屏幕缩放密度 + * 通过 Context 获取屏幕缩放密度 * @return */ public static float getScaledDensity() { @@ -315,7 +316,7 @@ public static float getScaledDensity() { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getScaledDensity"); } - return -1f; + return 0f; } /** @@ -332,7 +333,7 @@ public static float getXDpi() { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getXDpi"); } - return -1f; + return 0f; } /** @@ -349,7 +350,7 @@ public static float getYDpi() { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getYDpi"); } - return -1f; + return 0f; } /** @@ -366,7 +367,7 @@ public static float getWidthDpi() { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getWidthDpi"); } - return -1f; + return 0f; } /** @@ -383,14 +384,14 @@ public static float getHeightDpi() { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getHeightDpi"); } - return -1f; + return 0f; } /** * 获取屏幕信息 * @return */ - public static String getScreenInfo(){ + public static String getScreenInfo() { StringBuilder sBuilder = new StringBuilder(); // 获取屏幕信息 DisplayMetrics dMetrics = getDisplayMetrics(); @@ -453,14 +454,14 @@ public static void setFullScreen(@NonNull final Activity activity) { * 设置 Activity 的 android:configChanges="orientation"时, * 切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次 * 设置 Activity 的 android:configChanges="orientation|keyboardHidden|screenSize" - * (4.0 以上必须带最后一个参数)时 + * (4.0 以上必须带最后一个参数)时 * 切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法 * @param activity */ public static void setLandscape(@NonNull final Activity activity) { try { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setLandscape"); } } @@ -472,7 +473,7 @@ public static void setLandscape(@NonNull final Activity activity) { public static void setPortrait(@NonNull final Activity activity) { try { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setPortrait"); } } @@ -484,7 +485,7 @@ public static void setPortrait(@NonNull final Activity activity) { public static boolean isLandscape() { try { return DevUtils.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isLandscape"); } return false; @@ -497,7 +498,7 @@ public static boolean isLandscape() { public static boolean isPortrait() { try { return DevUtils.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isPortrait"); } return false; @@ -522,7 +523,7 @@ public static int getScreenRotation(@NonNull final Activity activity) { default: return 0; } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getScreenRotation"); } return 0; @@ -536,7 +537,7 @@ public static boolean isScreenLock() { try { KeyguardManager keyguardManager = (KeyguardManager) DevUtils.getContext().getSystemService(Context.KEYGUARD_SERVICE); return keyguardManager != null && keyguardManager.inKeyguardRestrictedInputMode(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isScreenLock"); } return false; @@ -549,7 +550,7 @@ public static boolean isScreenLock() { public static boolean isTablet() { try { return (DevUtils.getContext().getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isTablet"); } return false; @@ -558,7 +559,7 @@ public static boolean isTablet() { // == ----------------------------------------- == /** - * 获得状态栏的高度(无关 android:theme 获取状态栏高度) + * 获取状态栏的高度(无关 android:theme 获取状态栏高度) * @return */ public static int getStatusHeight() { @@ -570,11 +571,11 @@ public static int getStatusHeight() { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getStatusHeight"); } - return -1; + return 0; } /** - * 获取应用区域 TitleBar 高度 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) + * 获取应用区域 TitleBar 高度 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) * @param activity * @return */ @@ -586,24 +587,26 @@ public static int getStatusBarHeight(Activity activity) { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getStatusBarHeight"); } - return -1; + return 0; } /** - * 设置进入休眠时长 - 需添加权限 + * 设置进入休眠时长 + * * @param duration 时长 */ + @RequiresPermission(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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setSleepDuration"); } } /** * 获取进入休眠时长 - * @return 进入休眠时长,报错返回-123 + * @return 进入休眠时长,报错返回 -1 */ public static int getSleepDuration() { try { @@ -621,7 +624,7 @@ public static int getSleepDuration() { // https://github.com/weizongwei5/AndroidScreenShot_SysApi /** - * 获取当前屏幕截图,包含状态栏 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) + * 获取当前屏幕截图,包含状态栏 (顶部灰色TitleBar高度,没有设置 android:theme 的 NoTitleBar 时会显示) * @param activity * @return */ @@ -660,9 +663,6 @@ public static Bitmap snapShotWithoutStatusBar(Activity activity) { // 获取状态栏高度 int statusBarHeight = getStatusBarHeight(activity); - if(statusBarHeight == -1) { - statusBarHeight = 0; - } Rect frame = new Rect(); activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); @@ -689,7 +689,7 @@ public static int getNavigationBarHeight() { if (resourceId > 0 && checkDeviceHasNavigationBar()) { navigationBarHeight = resources.getDimensionPixelSize(resourceId); } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getNavigationBarHeight"); } return navigationBarHeight; @@ -710,8 +710,8 @@ public static boolean checkDeviceHasNavigationBar() { } try { Class systemPropertiesClass = Class.forName("android.os.SystemProperties"); - Method m = systemPropertiesClass.getMethod("get", String.class); - String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys"); + 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)) { @@ -720,7 +720,7 @@ public static boolean checkDeviceHasNavigationBar() { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "checkDeviceHasNavigationBar - SystemProperties"); } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "checkDeviceHasNavigationBar"); } return hasNavigationBar; diff --git a/DevLibUtils/src/main/java/dev/utils/app/ServiceUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ServiceUtils.java index e4deef4cdc..a74b5a9125 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ServiceUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ServiceUtils.java @@ -16,9 +16,7 @@ /** * detail: 服务相关工具类 - * Created by Blankj - * http://blankj.com - * Update to Ttt + * Created by Ttt */ public final class ServiceUtils { @@ -42,7 +40,7 @@ public static boolean isServiceRunning(final String className) { for (RunningServiceInfo rInfo : listInfos) { if (className.equals(rInfo.service.getClassName())) return true; } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isServiceRunning"); } return false; @@ -72,7 +70,7 @@ public static Set getAllRunningService() { names.add(rInfo.service.getClassName()); } return names; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getAllRunningService"); } return Collections.emptySet(); @@ -126,7 +124,7 @@ public static boolean stopService(final Class cls) { try { Intent intent = new Intent(DevUtils.getContext(), cls); return DevUtils.getContext().stopService(intent); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "stopService"); return false; } @@ -170,7 +168,7 @@ public static void bindService(final Class cls, final ServiceConnection conn, try { Intent intent = new Intent(DevUtils.getContext(), cls); DevUtils.getContext().bindService(intent, conn, flags); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "bindService"); } } @@ -182,7 +180,7 @@ public static void bindService(final Class cls, final ServiceConnection conn, public static void unbindService(final ServiceConnection conn) { try { DevUtils.getContext().unbindService(conn); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "unbindService"); } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/ShapeUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ShapeUtils.java index dec8114091..987825bbac 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ShapeUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ShapeUtils.java @@ -34,13 +34,24 @@ private ShapeUtils(Builder builder) { drawable = builder.gradientDrawable; } + /** + * 获取 GradientDrawable + * @return + */ public GradientDrawable getDrawable() { return drawable; } - public void setDrawable(View view){ - if (view != null){ - view.setBackground(drawable); + /** + * 设置 Drawable 背景 + * @param view + */ + public void setDrawable(View view) { + if (view != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) + view.setBackground(drawable); + else + view.setBackgroundDrawable(drawable); } } @@ -50,15 +61,15 @@ public void setDrawable(View view){ * detail: 构造者模式 * Created by Ttt */ - public final static class Builder { + public static final class Builder { private GradientDrawable gradientDrawable = new GradientDrawable(); public Builder() { } - public Builder(GradientDrawable drawable){ - if (drawable != null){ + public Builder(GradientDrawable drawable) { + if (drawable != null) { this.gradientDrawable = drawable; } } @@ -67,7 +78,7 @@ public Builder(GradientDrawable drawable){ * 获取 Shape 工具类 * @return */ - public ShapeUtils build(){ + public ShapeUtils build() { return new ShapeUtils(this); } @@ -80,8 +91,8 @@ public ShapeUtils build(){ * @param radius * @return */ - public Builder setRadius(float radius){ - if (gradientDrawable != null){ + public Builder setRadius(float radius) { + if (gradientDrawable != null) { gradientDrawable.setCornerRadius(radius); } return this; @@ -94,7 +105,7 @@ public Builder setRadius(float radius){ * @param left * @return */ - public Builder setRadiusLeft(float left){ + public Builder setRadiusLeft(float left) { setCornerRadii(left, 0, 0, left); return this; } @@ -105,7 +116,7 @@ public Builder setRadiusLeft(float left){ * @param leftBottom * @return */ - public Builder setRadiusLeft(float leftTop, float leftBottom){ + public Builder setRadiusLeft(float leftTop, float leftBottom) { setCornerRadii(leftTop, 0, 0, leftBottom); return this; } @@ -117,7 +128,7 @@ public Builder setRadiusLeft(float leftTop, float leftBottom){ * @param right * @return */ - public Builder setRadiusRight(float right){ + public Builder setRadiusRight(float right) { setCornerRadii(0, right, right, 0); return this; } @@ -128,7 +139,7 @@ public Builder setRadiusRight(float right){ * @param rightBottom * @return */ - public Builder setRadiusRight(float rightTop, float rightBottom){ + public Builder setRadiusRight(float rightTop, float rightBottom) { setCornerRadii(0, rightTop, rightBottom, 0); return this; } @@ -149,7 +160,7 @@ public Builder setCornerRadii(float leftTop, float rightTop, float rightBottom, // android:topLeftRadius="8dp" // android:topRightRadius="8dp" /> - if (gradientDrawable != null){ + 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 }); } @@ -165,11 +176,11 @@ public Builder setCornerRadii(float leftTop, float rightTop, float rightBottom, * @param color * @return */ - public Builder setColor(String color){ - if (gradientDrawable != null && !TextUtils.isEmpty(color)){ + public Builder setColor(String color) { + if (gradientDrawable != null && !TextUtils.isEmpty(color)) { try { gradientDrawable.setColor(Color.parseColor(color)); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setColor"); } } @@ -181,11 +192,11 @@ public Builder setColor(String color){ * @param color * @return */ - public Builder setColor(@ColorRes int color){ - if (gradientDrawable != null){ + public Builder setColor(@ColorRes int color) { + if (gradientDrawable != null) { try { gradientDrawable.setColor(ContextCompat.getColor(DevUtils.getContext(), color)); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setColor"); } } @@ -207,11 +218,11 @@ public Builder setColor(@ColorRes int color){ * @param color * @return */ - public Builder setStroke(int width, String color){ - if (gradientDrawable != null){ + public Builder setStroke(int width, String color) { + if (gradientDrawable != null) { try { gradientDrawable.setStroke(width, Color.parseColor(color)); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setStroke"); } } @@ -224,11 +235,11 @@ public Builder setStroke(int width, String color){ * @param color * @return */ - public Builder setStroke(int width, @ColorRes int color){ - if (gradientDrawable != null){ + public Builder setStroke(int width, @ColorRes int color) { + if (gradientDrawable != null) { try { gradientDrawable.setStroke(width, ContextCompat.getColor(DevUtils.getContext(), color)); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setStroke"); } } @@ -242,11 +253,11 @@ public Builder setStroke(int width, @ColorRes int color){ * @return */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public Builder setStroke(int width, ColorStateList color){ - if (gradientDrawable != null && color != null){ + public Builder setStroke(int width, ColorStateList color) { + if (gradientDrawable != null && color != null) { try { gradientDrawable.setStroke(width, color); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setStroke"); } } @@ -266,11 +277,11 @@ public Builder setStroke(int width, ColorStateList color){ * @param height * @return */ - public Builder setSize(int width, int height){ - if (gradientDrawable != null){ + public Builder setSize(int width, int height) { + if (gradientDrawable != null) { try { gradientDrawable.setSize(width, height); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setSize"); } } @@ -284,8 +295,8 @@ public Builder setSize(int width, int height){ // * @param padding // * @return // */ -// public Builder setPadding(int padding){ -// if (gradientDrawable != null){ +// public Builder setPadding(int padding) { +// if (gradientDrawable != null) { // try { // Rect rect = new Rect(); // rect.left = padding; @@ -293,7 +304,7 @@ public Builder setSize(int width, int height){ // rect.right = padding; // rect.bottom = padding; // gradientDrawable.getPadding(rect); -// } catch (Exception e){ +// } catch (Exception e) { // LogPrintUtils.eTag(TAG, e, "setPadding"); // } // } @@ -363,7 +374,7 @@ public Builder setSize(int width, int height){ * 设置渐变颜色 * @param colors */ - public Builder(@ColorInt int[] colors){ + public Builder(@ColorInt int[] colors) { this(new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors)); } @@ -372,7 +383,7 @@ public Builder(@ColorInt int[] colors){ * @param orientation * @param colors */ - public Builder(GradientDrawable.Orientation orientation, @ColorInt int[] colors){ + public Builder(GradientDrawable.Orientation orientation, @ColorInt int[] colors) { this(new GradientDrawable(orientation, colors)); } } @@ -385,7 +396,7 @@ public Builder(GradientDrawable.Orientation orientation, @ColorInt int[] colors) * @param color * @return */ - public static Builder newBuilder(float radius, @ColorRes int color){ + public static Builder newBuilder(float radius, @ColorRes int color) { return new Builder().setRadius(radius).setColor(color); } @@ -395,7 +406,7 @@ public static Builder newBuilder(float radius, @ColorRes int color){ * @param color * @return */ - public static Builder newBuilderToLeft(float left, @ColorRes int color){ + public static Builder newBuilderToLeft(float left, @ColorRes int color) { return new Builder().setRadiusLeft(left).setColor(color); } @@ -405,7 +416,7 @@ public static Builder newBuilderToLeft(float left, @ColorRes int color){ * @param color * @return */ - public static Builder newBuilderToRight(float right, @ColorRes int color){ + public static Builder newBuilderToRight(float right, @ColorRes int color) { return new Builder().setRadiusRight(right).setColor(color); } @@ -414,7 +425,7 @@ public static Builder newBuilderToRight(float right, @ColorRes int color){ * @param colors * @return */ - public static Builder newBuilderToGradient(@ColorInt int[] colors){ + public static Builder newBuilderToGradient(@ColorInt int[] colors) { return new Builder(colors); } @@ -424,7 +435,7 @@ public static Builder newBuilderToGradient(@ColorInt int[] colors){ * @param colors * @return */ - public static Builder newBuilderToGradient(GradientDrawable.Orientation orientation, @ColorInt int[] colors){ + 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 index a188d94de8..b6127feb34 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ShellUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ShellUtils.java @@ -9,9 +9,7 @@ /** * detail: Shell 相关工具类 - * Created by Blankj - * http://blankj.com - * Update to Ttt + * Created by Ttt */ public final class ShellUtils { @@ -20,7 +18,8 @@ private ShellUtils() { // 日志TAG private static final String TAG = ShellUtils.class.getSimpleName(); - + // 操作成功 + public static final int SUCCESS = 0; // 换行符 private static final String LINE_SEP = System.getProperty("line.separator"); @@ -149,7 +148,7 @@ public static CommandResult execCmd(final String[] commands, final boolean isRoo public static class CommandResult { /** 结果码 */ - public int result; + public int result; /** 成功信息 */ public String successMsg; /** 错误信息 */ @@ -160,5 +159,49 @@ public CommandResult(final int result, final String successMsg, final String err this.successMsg = successMsg; this.errorMsg = errorMsg; } + + /** + * 判断是否执行成功 + * @return + */ + public boolean isSuccess() { + return result == SUCCESS; + } + + /** + * 判断是否执行成功(判断 errorMsg) + * @return + */ + public boolean isSuccess2() { + if (result == SUCCESS && (errorMsg == null || errorMsg.length() == 0)) { + return true; + } + return false; + } + + /** + * 判断是否执行成功(判断 successMsg) + * @return + */ + public boolean isSuccess3() { + if (result == SUCCESS && successMsg != null && successMsg.length() != 0) { + return true; + } + return false; + } + + /** + * 判断是否执行成功(判断 successMsg) , 并且 successMsg 是否包含某个字符串 + * @param contains + * @return + */ + public boolean isSuccess4(String contains) { + if (result == SUCCESS && successMsg != null && successMsg.length() != 0) { + if (contains != null && contains.length() != 0 && successMsg.toLowerCase().contains(contains)) { + return true; + } + } + return false; + } } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/ShortCutUtils.java b/DevLibUtils/src/main/java/dev/utils/app/ShortCutUtils.java index 496652077d..2661af2539 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ShortCutUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ShortCutUtils.java @@ -42,7 +42,7 @@ public static boolean hasShortcut(Context context, String name) { if (cursor != null && cursor.getCount() > 0) { isInstallShortcut = true; } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "hasShortcut"); } finally { if (cursor != null) { @@ -58,17 +58,17 @@ public static boolean hasShortcut(Context context, String name) { /** * 为程序创建桌面快捷方式 * @param context - * @param clasName + * @param className * @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"); + public static void addShortcut(Context context, String className, 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); + shortcutIntent.setClassName(context, className); shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); // 快捷方式的图标 ShortcutIconResource iconRes = ShortcutIconResource.fromContext(context, res); @@ -81,11 +81,11 @@ public static void addShortcut(Context context, String clasName, String name, in * @param context * @param name */ - public static void delShortcut(Context context, String clasName, String name) { - Intent shortcut = new Intent( "com.android.launcher.action.UNINSTALL_SHORTCUT"); + public static void delShortcut(Context context, String className, String name) { + Intent shortcut = new Intent("com.android.launcher.action.UNINSTALL_SHORTCUT"); // 快捷方式的名称 shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); - String appClass = context.getPackageName() + "." + clasName; + String appClass = context.getPackageName() + "." + className; ComponentName comp = new ComponentName(context.getPackageName(), appClass); shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(Intent.ACTION_MAIN).setComponent(comp)); context.sendBroadcast(shortcut); diff --git a/DevLibUtils/src/main/java/dev/utils/app/SignaturesUtils.java b/DevLibUtils/src/main/java/dev/utils/app/SignaturesUtils.java index 358e768846..848308705b 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/SignaturesUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/SignaturesUtils.java @@ -18,7 +18,7 @@ import dev.utils.LogPrintUtils; /** - * detail: 签名工具类(获取app,签名信息) + * detail: 签名工具类(获取 App 签名信息) * Created by Ttt */ public final class SignaturesUtils { @@ -37,7 +37,7 @@ private SignaturesUtils() { * 检测应用程序是否是用"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"); + private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US"); /** * 进行转换 @@ -54,7 +54,7 @@ public static String toHexString(byte[] bData) { } /** - * 返回MD5 + * 返回 MD5 * @param signatures * @return */ @@ -73,7 +73,9 @@ public static String signatureMD5(Signature[] signatures) { } /** - * SHA1 + * 返回 SHA1 加密字符串 + * @param signatures + * @return */ public static String signatureSHA1(Signature[] signatures) { try { @@ -90,7 +92,9 @@ public static String signatureSHA1(Signature[] signatures) { } /** - * SHA256 + * 返回 SHA256 加密字符串 + * @param signatures + * @return */ public static String signatureSHA256(Signature[] signatures) { try { @@ -108,7 +112,7 @@ public static String signatureSHA256(Signature[] signatures) { /** * 判断签名是debug签名还是release签名 - * @return true = 开发(debug.keystore),false = 上线发布(非.android默认debug.keystore) + * @return true = 开发(debug.keystore),false = 上线发布(非.android默认debug.keystore) */ public static boolean isDebuggable(Signature[] signatures) { // 判断是否默认key(默认是) @@ -130,9 +134,9 @@ public static boolean isDebuggable(Signature[] signatures) { } /** - * 获取App 证书对象 + * 获取 App 证书对象 */ - public static X509Certificate getX509Certificate(Signature[] signatures){ + public static X509Certificate getX509Certificate(Signature[] signatures) { try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); ByteArrayInputStream stream = new ByteArrayInputStream(signatures[0].toByteArray()); @@ -149,7 +153,7 @@ public static X509Certificate getX509Certificate(Signature[] signatures){ * @param signatures * @return */ - public static void printSignatureName(Signature[] signatures){ + public static void printSignatureName(Signature[] signatures) { try { for (int i = 0, len = signatures.length; i < len; i++) { CertificateFactory cf = CertificateFactory.getInstance("X.509"); @@ -172,11 +176,11 @@ public static void printSignatureName(Signature[] signatures){ // -- - // Android的APK应用签名机制以及读取签名的方法 + // Android的 Apk 应用签名机制以及读取签名的方法 // http://www.jb51.net/article/79894.htm /** - * 从APK中读取签名 + * 从 Apk 中读取签名 * @param file * @return * @throws IOException @@ -193,7 +197,7 @@ public static Signature[] getSignaturesFromApk(File file) { } /** - * 从APK中读取签名 + * 从 Apk 中读取签名 * @param file * @return * @throws IOException diff --git a/DevLibUtils/src/main/java/dev/utils/app/SizeUtils.java b/DevLibUtils/src/main/java/dev/utils/app/SizeUtils.java index 8b247c2290..eea830ba98 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/SizeUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/SizeUtils.java @@ -23,6 +23,7 @@ private SizeUtils() { /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) * @param dpValue + * @return */ public static int dipConvertPx(float dpValue) { try { @@ -31,12 +32,30 @@ public static int dipConvertPx(float dpValue) { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "dipConvertPx"); } - return -1; + return 0; } + /** + * 根据手机的分辨率从 dp 的单位 转成为 px(像素) - float + * @param dpValue + * @return + */ + public static float dipConvertPxf(float dpValue) { + try { + float scale = DevUtils.getContext().getResources().getDisplayMetrics().density; + return (dpValue * scale + 0.5f); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "dipConvertPxf"); + } + return 0f; + } + + // = + /** * 根据手机的分辨率从 px(像素) 的单位 转成为 dp * @param pxValue + * @return */ public static int pxConvertDip(float pxValue) { try { @@ -45,12 +64,30 @@ public static int pxConvertDip(float pxValue) { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "pxConvertDip"); } - return -1; + return 0; + } + + /** + * 根据手机的分辨率从 px(像素) 的单位 转成为 dp - float + * @param pxValue + * @return + */ + public static float pxConvertDipf(float pxValue) { + try { + float scale = DevUtils.getContext().getResources().getDisplayMetrics().density; + return (pxValue / scale + 0.5f); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "pxConvertDipf"); + } + return 0f; } + // = + /** * 根据手机的分辨率从 px(像素) 的单位 转成为 sp * @param pxValue + * @return */ public static int pxConvertSp(float pxValue) { try { @@ -59,12 +96,30 @@ public static int pxConvertSp(float pxValue) { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "pxConvertSp"); } - return -1; + return 0; } + /** + * 根据手机的分辨率从 px(像素) 的单位 转成为 sp - float + * @param pxValue + * @return + */ + public static float pxConvertSpf(float pxValue) { + try { + float scale = DevUtils.getContext().getResources().getDisplayMetrics().scaledDensity; + return (pxValue / scale + 0.5f); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "pxConvertSpf"); + } + return 0f; + } + + // = + /** * 根据手机的分辨率从 sp 的单位 转成为 px * @param spValue + * @return */ public static int spConvertPx(float spValue) { try { @@ -73,12 +128,30 @@ public static int spConvertPx(float spValue) { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "spConvertPx"); } - return -1; + return 0; } + /** + * 根据手机的分辨率从 sp 的单位 转成为 px - float + * @param spValue + * @return + */ + public static float spConvertPxf(float spValue) { + try { + float scale = DevUtils.getContext().getResources().getDisplayMetrics().scaledDensity; + return (spValue * scale + 0.5f); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "spConvertPxf"); + } + return 0f; + } + + // = + /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 第二种 * @param dpValue + * @return */ public static int dipConvertPx2(float dpValue) { try { @@ -86,23 +159,55 @@ public static int dipConvertPx2(float dpValue) { } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "dipConvertPx2"); } - return -1; + return 0; + } + + /** + * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 第二种 - float + * @param dpValue + * @return + */ + public static float dipConvertPx2f(float dpValue) { + try { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, DevUtils.getContext().getResources().getDisplayMetrics()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "dipConvertPx2f"); + } + return 0f; } + // = + /** * 根据手机的分辨率从 sp 的单位 转成为 px 第二种 * @param spValue + * @return */ 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; + return 0; } + /** + * 根据手机的分辨率从 sp 的单位 转成为 px 第二种 - float + * @param spValue + * @return + */ + public static float spConvertPx2f(float spValue) { + try { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, DevUtils.getContext().getResources().getDisplayMetrics()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "spConvertPx2f"); + } + return 0f; + } + + // = + /** * 各种单位转换 - 该方法存在于 TypedValue * @param unit 单位 @@ -125,7 +230,7 @@ public static float applyDimension(final int unit, final float value, final Disp case TypedValue.COMPLEX_UNIT_MM: return value * metrics.xdpi * (1.0f / 25.4f); } - return -1; + return 0; } // == @@ -155,6 +260,11 @@ public void run() { /** 获取到 View 尺寸的监听 */ public interface onGetSizeListener { + + /** + * 获取到 View 尺寸 通知 + * @param view + */ void onGetSize(View view); } @@ -181,7 +291,7 @@ public static int[] measureView(final View view) { } view.measure(widthSpec, heightSpec); return new int[]{ view.getMeasuredWidth(), view.getMeasuredHeight() }; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "measureView"); return new int[] { 0, 0 }; } diff --git a/DevLibUtils/src/main/java/dev/utils/app/SnackbarUtils.java b/DevLibUtils/src/main/java/dev/utils/app/SnackbarUtils.java new file mode 100644 index 0000000000..ebbc68b7bb --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/SnackbarUtils.java @@ -0,0 +1,1569 @@ +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.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 工具类 + * Created by Ttt + */ +public final class SnackbarUtils { + + // 日志 TAG + private final String TAG = SnackbarUtils.class.getSimpleName(); + // 持有 弱引用 Snackbar, 防止所属 View 销毁等 + private static WeakReference sSnackbarReference; + // 样式构造对象 + private StyleBuilder mStyleBuilder = new StyleBuilder(); + + private SnackbarUtils(View view) { + if (view != null) { + try { + sSnackbarReference = new WeakReference<>(Snackbar.make(view, "", Snackbar.LENGTH_SHORT)); + } catch (Exception e) { + } + } + } + + // ============ + // = 构建方法 = + // ============ + + /** + * 获取 SnackbarUtils 对象 + * @param activity + * @return + */ + public static SnackbarUtils with(Activity activity) { + View view = null; + if (activity != null && activity.getWindow() != null) { + return new SnackbarUtils(activity.getWindow().getDecorView()); + } + return new SnackbarUtils(view); + } + + /** + * 获取 SnackbarUtils 对象 + * @param fragment + * @return + */ + public static SnackbarUtils with(Fragment fragment) { + return new SnackbarUtils((fragment != null) ? fragment.getView() : null); + } + + /** + * 获取 SnackbarUtils 对象 + * @param window + * @return + */ + public static SnackbarUtils with(Window window) { + return new SnackbarUtils((window != null) ? window.getDecorView() : null); + } + + /** + * 获取 SnackbarUtils 对象 + * @param view + * @return + */ + public static SnackbarUtils with(View view) { + return new SnackbarUtils(view); + } + + // ============ + // = 样式相关 = + // ============ + + /** + * 获取样式 + * @return + */ + public StyleBuilder getStyle() { + return mStyleBuilder; + } + + /** + * 设置样式 + * @param style + * @return + */ + public SnackbarUtils setStyle(Style style) { + this.mStyleBuilder = new StyleBuilder(style); + return this; + } + + // ============ + // = 对外方法 = + // ============ + + /** + * 获取 Snackbar + * @return + */ + public Snackbar getSnackbar() { + return (sSnackbarReference != null) ? sSnackbarReference.get() : null; + } + + /** + * 获取 Snackbar View + * @return + */ + public View getSnackbarView() { + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + return snackbar.getView(); + } + return null; + } + + /** + * 获取 Snackbar TextView(snackbar_text) + * @return + */ + public TextView getTextView() { + View view = getSnackbarView(); + if (view != null) { + return view.findViewById(R.id.snackbar_text); + } + return null; + } + + /** + * 获取 Snackbar Action Button(snackbar_action) + * 右边按钮(如: 撤销) + * @return + */ + public Button getActionButton() { + View view = getSnackbarView(); + if (view != null) { + return view.findViewById(R.id.snackbar_action); + } + return null; + } + + /** + * 向 Snackbar 布局中添加View (Google不建议,复杂的布局应该使用DialogFragment进行展示) + * @param layoutId + * @param index + * @return + */ + public SnackbarUtils addView(int layoutId, int index) { + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + try { + // 加载布局文件新建View + View addView = LayoutInflater.from(snackbar.getView().getContext()).inflate(layoutId,null); + return addView(addView, index); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "addView"); + } + } + return this; + } + + /** + * 向 Snackbar 布局中添加View (Google不建议,复杂的布局应该使用DialogFragment进行展示) + * @param view + * @param index + * @return + */ + public SnackbarUtils addView(View view, 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 setCallback + * @return + */ + public SnackbarUtils setCallback(Snackbar.Callback setCallback) { + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + snackbar.addCallback(setCallback); + } + return this; + } + + // = + + /** + * 设置 Action 按钮文字内容 及 点击监听 + * @param resId + * @param objs + * @return + */ + public SnackbarUtils setAction(int resId, Object... objs) { + return setAction(null, resId, objs); + } + + /** + * 设置 Action 按钮文字内容 及 点击监听 + * @param listener + * @param resId + * @param objs + * @return + */ + public SnackbarUtils setAction(View.OnClickListener listener, int resId, 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 + */ + public SnackbarUtils setAction(String text, Object... objs) { + return setAction(null, text, objs); + } + + /** + * 设置 Action 按钮文字内容 及 点击监听 + * @param listener + * @param text + * @param objs + * @return + */ + public SnackbarUtils setAction(View.OnClickListener listener, String text, 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 + */ + public void dismiss(boolean setNull) { + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + snackbar.dismiss(); + if (setNull) + sSnackbarReference = null; + } + } + + // ============== + // == 显示方法 == + // ============== + + /** + * 显示 Short Snackbar + * @param resId + * @param objs + */ + public void showShort(int resId, Object... objs) { + priShow(getFormatRes(resId, objs), Snackbar.LENGTH_SHORT); + } + + /** + * 显示 Long Snackbar + * @param resId + * @param objs + */ + public void showLong(int resId, Object... objs) { + priShow(getFormatRes(resId, objs), Snackbar.LENGTH_LONG); + } + + /** + * 显示 Indefinite Snackbar (无限时, 一直显示) + * @param resId + * @param objs + */ + public void showIndefinite(int resId, Object... objs) { + priShow(getFormatRes(resId, objs), Snackbar.LENGTH_INDEFINITE); + } + + // = + + /** + * 显示 Short Snackbar + * @param text + * @param objs + */ + public void showShort(String text, Object... objs) { + priShow(getFormatString(text, objs), Snackbar.LENGTH_SHORT); + } + + /** + * 显示 Long Snackbar + * @param text + * @param objs + */ + public void showLong(String text, Object... objs) { + priShow(getFormatString(text, objs), Snackbar.LENGTH_LONG); + } + + /** + * 显示 Indefinite Snackbar (无限时, 一直显示) + * @param text + * @param objs + */ + public void showIndefinite(String text, Object... objs) { + priShow(getFormatString(text, objs), Snackbar.LENGTH_INDEFINITE); + } + + // ============ + // = 内部方法 = + // ============ + + /** + * 内部显示方法 + * @param text + * @param duration + */ + private void priShow(String text, int duration){ + Snackbar snackbar = getSnackbar(); + if (snackbar != null) { + if (!snackbar.isShownOrQueued()) { + // 防止内容为null + if (!TextUtils.isEmpty(text)) { + setSnackbarStyle(snackbar); + if (snackbar != null){ + // 设置坐标位置 + setSnackbarLocation(snackbar); + // 显示 SnackBar + snackbar.setText(text).setDuration(duration).show(); + } + } + } + } + } + + // = + + /** + * 获取格式化字符串 + * @param format + * @param args + * @return + */ + private String getFormatString(String format, 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 + * @param objs + */ + private String getFormatRes(int resId, 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 + * @param drawable + */ + private void setBackground(View view, Drawable drawable) { + if (view != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) + view.setBackground(drawable); + else + view.setBackgroundDrawable(drawable); + } + } + + // = + + /** + * detail: Snackbar 样式配置构造类 + * Created by Ttt + */ + public static final class StyleBuilder extends Style { + + // == RootView == + // RootView 的重心 + private int rootGravity; + // RootView 背景圆角大小 + private float rootCornerRadius; + // 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; + // 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; + // 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; + // RootView 背景着色颜色 + private int actionBackgroundTintColor; + // RootView 背景图片 + private Drawable actionBackground; + + // ================ + + public StyleBuilder() { + } + + public StyleBuilder(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 + */ + @Override + public int getRootGravity() { + return rootGravity; + } + + /** + * 设置 RootView 的重心 + * @param rootGravity + * @return + */ + public StyleBuilder setRootGravity(int rootGravity) { + this.rootGravity = rootGravity; + return this; + } + + /** + * 获取 RootView 背景圆角大小 + * @return + */ + @Override + public float getRootCornerRadius() { + return rootCornerRadius; + } + + /** + * 设置 RootView 背景圆角大小 + * @return + */ + public StyleBuilder setRootCornerRadius(float rootCornerRadius) { + this.rootCornerRadius = rootCornerRadius; + return this; + } + + /** + * 获取 RootView 背景着色颜色 + * @return + */ + @Override + public int getRootBackgroundTintColor() { + return rootBackgroundTintColor; + } + + /** + * 设置 RootView 背景着色颜色 + * @param rootBackgroundTintColor + * @return + */ + public StyleBuilder setRootBackgroundTintColor(int rootBackgroundTintColor) { + this.rootBackgroundTintColor = rootBackgroundTintColor; + return this; + } + + /** + * 获取 RootView 背景图片 + * @return + */ + @Override + public Drawable getRootBackground() { + return rootBackground; + } + + /** + * 设置 RootView 背景图片 + * @param rootBackground + * @return + */ + public StyleBuilder setRootBackground(Drawable rootBackground) { + this.rootBackground = rootBackground; + return this; + } + + /** + * 获取 RootView margin 边距 - new int[] { left, top, right, bottom } + * @return + */ + @Override + public int[] getRootMargin() { + return rootMargin; + } + + /** + * 设置 RootView margin 边距 + * @param rootMargin + * @return + */ + public StyleBuilder setRootMargin(int[] rootMargin) { + this.rootMargin = rootMargin; + return this; + } + + /** + * 获取 RootView 透明度 + * @return + */ + @Override + public float getRootAlpha() { + return rootAlpha; + } + + /** + * 设置 RootView 透明度 + * @param rootAlpha + */ + public void setRootAlpha(float rootAlpha) { + this.rootAlpha = rootAlpha; + } + + // == snackbar_text TextView 相关 == + + /** + * 获取 TextView 的重心 + * @return + */ + @Override + public int getTextGravity() { + return textGravity; + } + + /** + * 设置 TextView 的重心 + * @param textGravity + * @return + */ + public StyleBuilder setTextGravity(int textGravity) { + this.textGravity = textGravity; + return this; + } + + /** + * 获取 TextView 文本颜色 + * @return + */ + @Override + public int getTextColor() { + return textColor; + } + + /** + * 设置 TextView 文本颜色 + * @param textColor + * @return + */ + public StyleBuilder setTextColor(int textColor) { + this.textColor = textColor; + return this; + } + + /** + * 获取 TextView 字体大小 + * @return + */ + @Override + public float getTextSize() { + return textSize; + } + + /** + * 设置 TextView 字体大小 + * @param textSize + * @return + */ + public StyleBuilder setTextSize(float textSize) { + this.textSize = textSize; + return this; + } + + /** + * 获取 TextView 最大行数 + * @return + */ + @Override + public int getTextMaxLines() { + return textMaxLines; + } + + /** + * 设置 TextView 最大行数 + * @param textMaxLines + * @return + */ + public StyleBuilder setTextMaxLines(int textMaxLines) { + this.textMaxLines = textMaxLines; + return this; + } + + /** + * 获取 TextView Ellipsize 效果 + * @return + */ + @Override + public TextUtils.TruncateAt getTextEllipsize() { + return textEllipsize; + } + + /** + * 设置 TextView Ellipsize 效果 + * @param textEllipsize + * @return + */ + public StyleBuilder setTextEllipsize(TextUtils.TruncateAt textEllipsize) { + this.textEllipsize = textEllipsize; + return this; + } + + /** + * 获取 TextView 字体样式 + * @return + */ + @Override + public Typeface getTextTypeface() { + return textTypeface; + } + + /** + * 设置 TextView 字体样式 + * @param textTypeface + * @return + */ + public StyleBuilder setTextTypeface(Typeface textTypeface) { + this.textTypeface = textTypeface; + return this; + } + + /** + * 获取 TextView padding 边距 - new int[] { left, top, right, bottom } + * @return + */ + @Override + public int[] getTextPadding() { + return textPadding; + } + + /** + * 设置 TextView padding 边距 - new int[] { left, top, right, bottom } + * @param textPadding + * @return + */ + public StyleBuilder setTextPadding(int[] textPadding) { + this.textPadding = textPadding; + return this; + } + + // == snackbar_action Button 相关 == + + /** + * 获取 Action Button 的重心 + * @return + */ + @Override + public int getActionGravity() { + return actionGravity; + } + + /** + * 设置 Action Button 的重心 + * @param actionGravity + * @return + */ + public StyleBuilder setActionGravity(int actionGravity) { + this.actionGravity = actionGravity; + return this; + } + + /** + * 获取 Action Button 文本颜色 + * @return + */ + @Override + public int getActionColor() { + return actionColor; + } + + /** + * 设置 Action Button 文本颜色 + * @param actionColor + * @return + */ + public StyleBuilder setActionColor(int actionColor) { + this.actionColor = actionColor; + return this; + } + + /** + * 获取 Action Button 字体大小 + * @return + */ + @Override + public float getActionSize() { + return actionSize; + } + + /** + * 设置 Action Button 字体大小 + * @param actionSize + * @return + */ + public StyleBuilder setActionSize(float actionSize) { + this.actionSize = actionSize; + return this; + } + + /** + * 获取 Action Button padding 边距 - new int[] { left, top, right, bottom } + * @return + */ + @Override + public int[] getActionPadding() { + return actionPadding; + } + + /** + * 设置 Action Button padding 边距 - new int[] { left, top, right, bottom } + * @param actionPadding + * @return + */ + public StyleBuilder setActionPadding(int[] actionPadding) { + this.actionPadding = actionPadding; + return this; + } + + /** + * 获取 Action Button 背景圆角大小 + * @return + */ + @Override + public float getActionCornerRadius() { + return actionCornerRadius; + } + + /** + * 设置 Action Button 背景圆角大小 + * @param actionCornerRadius + * @return + */ + public StyleBuilder setActionCornerRadius(float actionCornerRadius) { + this.actionCornerRadius = actionCornerRadius; + return this; + } + + /** + * 获取 Action Button 背景着色颜色 + * @return + */ + @Override + public int getActionBackgroundTintColor() { + return actionBackgroundTintColor; + } + + /** + * 设置 Action Button 背景着色颜色 + * @param actionBackgroundTintColor + * @return + */ + public StyleBuilder setActionBackgroundTintColor(int actionBackgroundTintColor) { + this.actionBackgroundTintColor = actionBackgroundTintColor; + return this; + } + + /** + * 获取 Action Button 背景图片 + * @return + */ + @Override + public Drawable getActionBackground() { + return actionBackground; + } + + /** + * 设置 Action Button 背景图片 + * @param actionBackground + * @return + */ + public StyleBuilder setActionBackground(Drawable actionBackground) { + this.actionBackground = actionBackground; + return this; + } + } + + // ====================== + // ====== 其他接口 ====== + // ====================== + + /** + * detail: Snackbar 样式配置 + * Created by Ttt + */ + public static abstract class 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; + } + } + + // ============== + // == 设置样式 == + // ============== + + /** + * 设置 Snackbar 样式配置 + * @param snackbar + * @return + */ + public Snackbar setSnackbarStyle(Snackbar snackbar) { + return setSnackbarStyle(snackbar, mStyleBuilder); + } + + /** + * 设置 Snackbar 样式配置 + * @param snackbar + * @param style + * @return + */ + public Snackbar setSnackbarStyle(Snackbar snackbar, SnackbarUtils.Style style) { + if (snackbar == null) { // 防止 Snackbar 为 null + return snackbar; + } else if (style == 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 + */ + public SnackbarUtils setShadowMargin(int shadowMargin) { + this.mShadowMargin = shadowMargin; + return this; + } + + /** + * 判断是否自动计算边距 (如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示) + * @return + */ + public boolean isAutoCalc() { + return mAutoCalc; + } + + /** + * 设置是否自动计算边距 (如: 显示在 View 下面, 但是下方距离不够, 自动设置为在 View 上方显示) + * @param autoCalc + * @return + */ + public SnackbarUtils setAutoCalc(boolean autoCalc) { + this.mAutoCalc = autoCalc; + return this; + } + + /** + * 清空坐标相关信息 + */ + private void clearLocations() { + // 重置处理 + mViewHeight = 0; + mViewGravity = -1; + mViewLocations = null; + mAppendTopMargin = 0; + } + + /** + * 设置 Snackbar 显示在指定 View 的上方 + * @param targetView + * @param appendTopMargin 追加边距(如: 状态栏高度) {@link ScreenUtils#getStatusBarHeight} + * @return + */ + public SnackbarUtils above(View targetView, 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 + * @param appendTopMargin 追加边距(如: 状态栏高度) {@link ScreenUtils#getStatusBarHeight} + * @return + */ + public SnackbarUtils bellow(View targetView, 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 + */ + private void setSnackbarLocation(Snackbar snackbar){ + // 获取显示的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(DevUtils.getContext()); + // 防止等于 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(); + } + + /** + * 获取屏幕的高度(单位:px) + * @param context + * @return 屏幕高 + */ + private int getScreenHeight(Context context) { + try { + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + if (windowManager == null) { + return context.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) { + } + return 0; + } + + /** + * 测量视图尺寸 + * @param view 视图 + * @return arr[0]: 视图宽度, arr[1]: 视图高度 + */ + private 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) { + return new int[] { 0, 0 }; + } + } + + /** + * 获取测量视图高度 + * @param view 视图 + * @return 视图高度 + */ + private 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 index 9bb603c6d4..dedfcb780b 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/StateListUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/StateListUtils.java @@ -36,10 +36,10 @@ private StateListUtils() { // android:state_middle 是否处于中间 // android:state_pressed 是否处于按下状态 . // android:state_selected 是否处于选中状态 -// android:state_window_focused 是否窗口已获得焦点 +// android:state_window_focused 是否窗口已获取焦点 /** - * 通过上下文获取 + * 通过 Context 获取 ColorStateList * @param id * @return */ diff --git a/DevLibUtils/src/main/java/dev/utils/app/TextViewUtils.java b/DevLibUtils/src/main/java/dev/utils/app/TextViewUtils.java index 099624eec4..5dfbdb110f 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/TextViewUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/TextViewUtils.java @@ -3,6 +3,7 @@ import android.app.Activity; import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.Typeface; import android.support.annotation.ColorInt; import android.support.annotation.IdRes; import android.text.Html; @@ -29,11 +30,11 @@ private TextViewUtils() { * @param view * @return */ - public static TextView getTextView(View view){ - if (view != null){ + public static TextView getTextView(View view) { + if (view != null) { try { return (TextView) view; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getTextView"); } } @@ -45,11 +46,11 @@ public static TextView getTextView(View view){ * @param view * @return */ - public static TextView getTextView(View view, @IdRes int id){ - if (view != null){ + public static TextView getTextView(View view, @IdRes int id) { + if (view != null) { try { return view.findViewById(id); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getTextView"); } } @@ -61,11 +62,11 @@ public static TextView getTextView(View view, @IdRes int id){ * @param activity * @return */ - public static TextView getTextView(Activity activity, @IdRes int id){ - if (activity != null){ + public static TextView getTextView(Activity activity, @IdRes int id) { + if (activity != null) { try { return activity.findViewById(id); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getTextView"); } } @@ -77,11 +78,11 @@ public static TextView getTextView(Activity activity, @IdRes int id){ * @param window * @return */ - public static TextView getTextView(Window window, @IdRes int id){ - if (window != null){ + public static TextView getTextView(Window window, @IdRes int id) { + if (window != null) { try { return window.findViewById(id); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getTextView"); } } @@ -95,8 +96,8 @@ public static TextView getTextView(Window window, @IdRes int id){ * @param textView * @return */ - public static String getText(TextView textView){ - if (textView != null){ + public static String getText(TextView textView) { + if (textView != null) { return textView.getText().toString(); } return null; @@ -108,27 +109,26 @@ public static String getText(TextView textView){ * @return */ public static String getText(View view) { - if (view != null){ + if (view != null) { try { return ((TextView) view).getText().toString(); - } catch (Exception e){ + } 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){ + public static String getText(View view, @IdRes int id) { + if (view != null) { try { return ((TextView) view.findViewById(id)).getText().toString(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getText"); } } @@ -140,11 +140,11 @@ public static String getText(View view, @IdRes int id){ * @param activity * @return */ - public static String getText(Activity activity, @IdRes int id){ - if (activity != null){ + public static String getText(Activity activity, @IdRes int id) { + if (activity != null) { try { return ((TextView) activity.findViewById(id)).getText().toString(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getText"); } } @@ -156,11 +156,11 @@ public static String getText(Activity activity, @IdRes int id){ * @param window * @return */ - public static String getText(Window window, @IdRes int id){ - if (window != null){ + public static String getText(Window window, @IdRes int id) { + if (window != null) { try { return ((TextView) window.findViewById(id)).getText().toString(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getText"); } } @@ -169,15 +169,43 @@ public static String getText(Window window, @IdRes int id){ // = + /** + * 设置是否加粗 + * @param view + * @param isBold + * @return + */ + public static void setBold(View view, boolean isBold) { + if (view != null) { + if (view instanceof TextView) { + ((TextView) view).setTypeface(Typeface.defaultFromStyle(isBold ? Typeface.BOLD : Typeface.NORMAL)); + } + } + } + + /** + * 设置是否加粗 + * @param view + * @param typeface + * @param isBold + * @return + */ + public static void setBold(View view, Typeface typeface, boolean isBold) { + if (view != null && typeface != null) { + if (view instanceof TextView) { + ((TextView) view).setTypeface(typeface, isBold ? Typeface.BOLD : Typeface.NORMAL); + } + } + } /** * 设置字体颜色 * @param view * @param color */ - public static void setTextColor(View view, @ColorInt int color){ - if (view != null){ - if (view instanceof TextView){ + public static void setTextColor(View view, @ColorInt int color) { + if (view != null) { + if (view instanceof TextView) { ((TextView) view).setTextColor(color); } } @@ -188,8 +216,8 @@ public static void setTextColor(View view, @ColorInt int color){ * @param textView * @param color */ - public static void setTextColor(TextView textView, @ColorInt int color){ - if (textView != null){ + public static void setTextColor(TextView textView, @ColorInt int color) { + if (textView != null) { textView.setTextColor(color); } } @@ -200,11 +228,11 @@ public static void setTextColor(TextView textView, @ColorInt int color){ * @param id * @param color */ - public static void setTextColor(View view, @IdRes int id, @ColorInt int color){ - if (view != null){ + 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setTextColor"); } } @@ -216,11 +244,11 @@ public static void setTextColor(View view, @IdRes int id, @ColorInt int color){ * @param id * @param color */ - public static void setTextColor(Activity activity, @IdRes int id, @ColorInt int color){ - if (activity != null){ + 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setTextColor"); } } @@ -232,11 +260,11 @@ public static void setTextColor(Activity activity, @IdRes int id, @ColorInt int * @param id * @param color */ - public static void setTextColor(Window window, @IdRes int id, @ColorInt int color){ - if (window != null){ + 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setTextColor"); } } @@ -249,9 +277,9 @@ public static void setTextColor(Window window, @IdRes int id, @ColorInt int colo * @param view * @param content */ - public static void setText(View view, String content){ - if (view != null && content != null){ - if (view instanceof TextView){ + public static void setText(View view, String content) { + if (view != null && content != null) { + if (view instanceof TextView) { ((TextView) view).setText(content); } } @@ -263,11 +291,11 @@ public static void setText(View view, String content){ * @param id * @param content */ - public static void setText(View view, @IdRes int id, String content){ - if (view != null){ + public static void setText(View view, @IdRes int id, String content) { + if (view != null) { try { ((TextView) view.findViewById(id)).setText(content); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setText"); } } @@ -279,11 +307,11 @@ public static void setText(View view, @IdRes int id, String content){ * @param id * @param content */ - public static void setText(Activity activity, @IdRes int id, String content){ - if (activity != null){ + public static void setText(Activity activity, @IdRes int id, String content) { + if (activity != null) { try { ((TextView) activity.findViewById(id)).setText(content); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setText"); } } @@ -295,11 +323,11 @@ public static void setText(Activity activity, @IdRes int id, String content){ * @param id * @param content */ - public static void setText(Window window, @IdRes int id, String content){ - if (window != null){ + public static void setText(Window window, @IdRes int id, String content) { + if (window != null) { try { ((TextView) window.findViewById(id)).setText(content); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setText"); } } @@ -312,9 +340,9 @@ public static void setText(Window window, @IdRes int id, String content){ * @param view * @param content */ - public static void setHtmlText(View view, String content){ - if (view != null && content != null){ - if (view instanceof TextView){ + public static void setHtmlText(View view, String content) { + if (view != null && content != null) { + if (view instanceof TextView) { ((TextView) view).setText(Html.fromHtml(content)); } } @@ -326,11 +354,11 @@ public static void setHtmlText(View view, String content){ * @param id * @param content */ - public static void setHtmlText(View view, @IdRes int id, String content){ - if (view != null){ + 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setHtmlText"); } } @@ -342,11 +370,11 @@ public static void setHtmlText(View view, @IdRes int id, String content){ * @param id * @param content */ - public static void setHtmlText(Activity activity, @IdRes int id, String content){ - if (activity != null){ + 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setHtmlText"); } } @@ -358,11 +386,11 @@ public static void setHtmlText(Activity activity, @IdRes int id, String content) * @param id * @param content */ - public static void setHtmlText(Window window, @IdRes int id, String content){ - if (window != null){ + 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setHtmlText"); } } @@ -375,8 +403,8 @@ public static void setHtmlText(Window window, @IdRes int id, String content){ * @param textView * @param content */ - public static void setText(TextView textView, String content){ - if (textView != null && content != null){ + public static void setText(TextView textView, String content) { + if (textView != null && content != null) { textView.setText(content); } } @@ -386,8 +414,8 @@ public static void setText(TextView textView, String content){ * @param textView * @param content */ - public static void setHtmlText(TextView textView, String content){ - if (textView != null && content != null){ + public static void setHtmlText(TextView textView, String content) { + if (textView != null && content != null) { textView.setText(Html.fromHtml(content)); } } @@ -397,7 +425,7 @@ public static void setHtmlText(TextView textView, String content){ * @param textView */ public static void setTVUnderLine(TextView textView) { - if (textView != null){ + if (textView != null) { Paint paint = textView.getPaint(); paint.setFlags(Paint.UNDERLINE_TEXT_FLAG); paint.setAntiAlias(true); @@ -409,11 +437,11 @@ public static void setTVUnderLine(TextView textView) { * @param textView * @return */ - public static int getTextHeight(TextView textView){ - if (textView != null){ + public static int getTextHeight(TextView textView) { + if (textView != null) { return getTextHeight(textView.getPaint()); } - return -1; + return 0; } /** @@ -421,7 +449,7 @@ public static int getTextHeight(TextView textView){ * @param paint * @return */ - public static int getTextHeight(Paint paint){ + public static int getTextHeight(Paint paint) { // https://blog.csdn.net/superbigcupid/article/details/47153139 // 获取字体高度 Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); @@ -436,11 +464,11 @@ public static int getTextHeight(Paint paint){ * @param textView * @return */ - public static int getTextTopOffsetHeight(TextView textView){ - if (textView != null){ + public static int getTextTopOffsetHeight(TextView textView) { + if (textView != null) { return getTextTopOffsetHeight(textView.getPaint()); } - return -1; + return 0; } /** @@ -448,7 +476,7 @@ public static int getTextTopOffsetHeight(TextView textView){ * @param paint * @return */ - public static int getTextTopOffsetHeight(Paint paint){ + public static int getTextTopOffsetHeight(Paint paint) { // 获取字体高度 Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); // 计算字体偏差(顶部偏差) @@ -462,7 +490,7 @@ public static int getTextTopOffsetHeight(Paint paint){ * @param textView * @return */ - public static float getTextWidth(TextView textView){ + public static float getTextWidth(TextView textView) { return getTextWidth(textView.getPaint(), textView.getText().toString()); } @@ -472,7 +500,7 @@ public static float getTextWidth(TextView textView){ * @param hintStr * @return */ - public static float getTextWidth(Paint paint, String hintStr){ + public static float getTextWidth(Paint paint, String hintStr) { return paint.measureText(hintStr); } @@ -482,7 +510,7 @@ public static float getTextWidth(Paint paint, String hintStr){ * @param paint * @return */ - public static int getCenterRectY(Rect targetRect, Paint paint){ + public static int getCenterRectY(Rect targetRect, Paint paint) { // 将字画在矩形背景的正中位置 // https://blog.csdn.net/superbigcupid/article/details/47153139 // 获取字体高度 @@ -497,7 +525,7 @@ public static int getCenterRectY(Rect targetRect, Paint paint){ * @param tHeight 需要的字体高度 * @return */ - public static float reckonTextSize(int tHeight){ + public static float reckonTextSize(int tHeight) { // 创建画笔 Paint paint = new Paint(); // 默认字体大小 @@ -505,7 +533,7 @@ public static float reckonTextSize(int tHeight){ // 计算内容高度 int calcTextHeight = -1; // 循环计算 - while (true){ + while (true) { // 设置画笔大小 paint.setTextSize(textSize); // 获取字体高度 @@ -513,9 +541,9 @@ public static float reckonTextSize(int tHeight){ // 计算内容高度 calcTextHeight = (int) Math.ceil((fontMetrics.descent - fontMetrics.ascent)); // 符合条件则直接返回 - if (calcTextHeight == tHeight){ + if (calcTextHeight == tHeight) { return textSize; - } else if (calcTextHeight > tHeight){ // 如果计算的字体高度大于 + } else if (calcTextHeight > tHeight) { // 如果计算的字体高度大于 textSize -= 0.5f; } else { textSize += 0.5f; @@ -529,8 +557,8 @@ public static float reckonTextSize(int tHeight){ * @param width * @return -1 表示没超过 */ - public static int calcTextWidth(TextView textView, float width){ - if (textView != null){ + public static int calcTextWidth(TextView textView, float width) { + if (textView != null) { return calcTextWidth(textView.getPaint(), textView.getText().toString(), width); } return -1; @@ -542,11 +570,11 @@ public static int calcTextWidth(TextView textView, float width){ * @param width * @return -1 表示没超过 */ - public static int calcTextWidth(Paint paint, String text, float width){ + public static int calcTextWidth(Paint paint, String text, float width) { // 先获取宽度 float textWidth = getTextWidth(paint, text); // 判断是否超过 - if (textWidth <= width){ + if (textWidth <= width) { return -1; // 表示没超过 } // 获取数据长度 @@ -562,18 +590,18 @@ public static int calcTextWidth(Paint paint, String text, float width){ // 计算宽度 textWidth = getTextWidth(paint, text.substring(0, length)); // 判断是否小于宽度 - 进行返回长度 - if (textWidth <= width){ + if (textWidth <= width) { break; } } // 遍历计算 - for (int i = length, len = text.length(); i < len; i++){ + for (int i = length, len = text.length(); i < len; i++) { // 获取字体内容宽度 float tWidth = paint.measureText(text.substring(0, i)); // 判断是否大于指定宽度 - if (tWidth > width){ + if (tWidth > width) { return i - 1; // 返回超过前的长度 - } else if (tWidth == width){ + } else if (tWidth == width) { return i; // 返回超过前的长度 } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/UriUtils.java b/DevLibUtils/src/main/java/dev/utils/app/UriUtils.java index c3f704c1c1..b73c20c945 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/UriUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/UriUtils.java @@ -1,7 +1,14 @@ 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; @@ -11,9 +18,7 @@ /** * detail: Uri 工具类 - * Created by Blankj - * http://blankj.com - * Update to Ttt + * Created by Ttt */ public final class UriUtils { @@ -43,11 +48,11 @@ private UriUtils() { * getUriForFileToName(file, "fileProvider"); * getUriForFile(file, "包名.fileProvider"); */ - public static Uri getUriForFileToName(final File file, String name){ + public static Uri getUriForFileToName(final File file, String name) { try { String authority = DevUtils.getContext().getPackageName() + "." + name; return getUriForFile(file, authority); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getUriForFileToName"); } return null; @@ -67,4 +72,102 @@ public static Uri getUriForFile(final File file, final String authority) { return Uri.fromFile(file); } } + + /** + * 通过 Uri 获取 文件路径 + * @param context + * @param uri + * @return + */ + 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/VibrationUtils.java b/DevLibUtils/src/main/java/dev/utils/app/VibrationUtils.java index 3e5715e4e7..101fb5c51e 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/VibrationUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/VibrationUtils.java @@ -2,7 +2,9 @@ import android.content.Context; import android.os.Vibrator; +import android.support.annotation.RequiresPermission; +import dev.DevUtils; import dev.utils.LogPrintUtils; /** @@ -20,41 +22,43 @@ private VibrationUtils() { /** * 震动 * - * @param context 上下文 - * @param milliseconds 振动时长 + * @param milliseconds 震动时长 */ - public static void vibrate(final Context context, final long milliseconds) { + @RequiresPermission(android.Manifest.permission.VIBRATE) + public static void vibrate(final long milliseconds) { try { - Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + Vibrator vibrator = (Vibrator) DevUtils.getContext().getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(milliseconds); - } catch (Exception e){ + } 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的指定下标开始重复振动。 + * 指定手机以pattern模式震动 + * + * @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) { + @RequiresPermission(android.Manifest.permission.VIBRATE) + public static void vibrate(final long[] pattern, final int repeat) { try { - Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + Vibrator vibrator = (Vibrator) DevUtils.getContext().getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(pattern, repeat); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "vibrate"); } } /** - * 取消振动 - * @param context 上下文 + * 取消震动 + * */ - public static void cancel(final Context context) { + @RequiresPermission(android.Manifest.permission.VIBRATE) + public static void cancel() { try { - ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).cancel(); - } catch (Exception e){ + ((Vibrator) DevUtils.getContext().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 index ec4b9d6e3a..ef6977c1ac 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/ViewUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/ViewUtils.java @@ -3,6 +3,8 @@ import android.app.Activity; import android.content.Context; import android.content.ContextWrapper; +import android.os.Build; +import android.support.annotation.RequiresApi; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -29,12 +31,12 @@ private ViewUtils() { private static final String TAG = ViewUtils.class.getSimpleName(); /** - * 获取上下文 + * 获取 Context * @param view * @return */ - public static Context getContext(View view){ - if (view != null){ + public static Context getContext(View view) { + if (view != null) { return view.getContext(); } return null; @@ -45,7 +47,7 @@ public static Context getContext(View view){ * @param view * @return */ - public static boolean isEmpty(View view){ + public static boolean isEmpty(View view) { return view == null; } @@ -54,11 +56,11 @@ public static boolean isEmpty(View view){ * @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++){ + 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){ + if (view == null) { return true; } } @@ -72,7 +74,7 @@ public static boolean isEmpty(View... views){ * @param view * @return */ - public static boolean isVisibility(View view){ + public static boolean isVisibility(View view) { return isVisibility(view, true); } @@ -82,8 +84,8 @@ public static boolean isVisibility(View view){ * @param isDf * @return */ - public static boolean isVisibility(View view, boolean isDf){ - if (view != null){ + public static boolean isVisibility(View view, boolean isDf) { + if (view != null) { // 判断是否显示 return (view.getVisibility() == View.VISIBLE); } @@ -96,11 +98,11 @@ public static boolean isVisibility(View view, boolean isDf){ * @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++){ + 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){ + if (view != null && view.getVisibility() == View.VISIBLE) { } else { return false; } @@ -115,7 +117,7 @@ public static boolean isVisibilitys(View... views){ * @param view * @return */ - public static boolean isVisibilityIN(View view){ + public static boolean isVisibilityIN(View view) { return isVisibilityIN(view, false); } @@ -125,8 +127,8 @@ public static boolean isVisibilityIN(View view){ * @param isDf * @return */ - public static boolean isVisibilityIN(View view, boolean isDf){ - if (view != null){ + public static boolean isVisibilityIN(View view, boolean isDf) { + if (view != null) { // 判断是否显示 return (view.getVisibility() == View.INVISIBLE); } @@ -139,7 +141,7 @@ public static boolean isVisibilityIN(View view, boolean isDf){ * @param view * @return */ - public static boolean isVisibilityGone(View view){ + public static boolean isVisibilityGone(View view) { return isVisibilityGone(view, false); } @@ -149,8 +151,8 @@ public static boolean isVisibilityGone(View view){ * @param isDf * @return */ - public static boolean isVisibilityGone(View view, boolean isDf){ - if (view != null){ + public static boolean isVisibilityGone(View view, boolean isDf) { + if (view != null) { // 判断是否显示 return (view.getVisibility() == View.GONE); } @@ -165,7 +167,7 @@ public static boolean isVisibilityGone(View view, boolean isDf){ * @param isVisibility * @return */ - public static int getVisibility(boolean isVisibility){ + public static int getVisibility(boolean isVisibility) { return isVisibility ? View.VISIBLE : View.GONE; } @@ -174,7 +176,7 @@ public static int getVisibility(boolean isVisibility){ * @param isVisibility * @return */ - public static int getVisibilityIN(boolean isVisibility){ + public static int getVisibilityIN(boolean isVisibility) { return isVisibility ? View.VISIBLE : View.INVISIBLE; } @@ -185,8 +187,8 @@ public static int getVisibilityIN(boolean isVisibility){ * @param isVisibility * @param view */ - public static boolean setVisibility(boolean isVisibility, View view){ - if (view != null){ + public static boolean setVisibility(boolean isVisibility, View view) { + if (view != null) { view.setVisibility(isVisibility ? View.VISIBLE : View.GONE); } return isVisibility; @@ -197,8 +199,8 @@ public static boolean setVisibility(boolean isVisibility, View view){ * @param isVisibility * @param view */ - public static boolean setVisibility(int isVisibility, View view){ - if (view != null){ + public static boolean setVisibility(int isVisibility, View view) { + if (view != null) { view.setVisibility(isVisibility); } return (isVisibility == View.VISIBLE); @@ -211,7 +213,7 @@ public static boolean setVisibility(int isVisibility, View view){ * @param isVisibility * @param views */ - public static boolean setVisibilitys(boolean isVisibility, View... views){ + public static boolean setVisibilitys(boolean isVisibility, View... views) { return setVisibilitys(getVisibility(isVisibility), views); } @@ -220,11 +222,11 @@ public static boolean setVisibilitys(boolean isVisibility, View... views){ * @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++){ + 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){ + if (view != null) { view.setVisibility(isVisibility); } } @@ -237,8 +239,8 @@ public static boolean setVisibilitys(int isVisibility, View... views){ * @param view * @param views */ - public static void toggleVisibilitys(View view, View... views){ - if (view != null){ + public static void toggleVisibilitys(View view, View... views) { + if (view != null) { view.setVisibility(View.VISIBLE); } setVisibilitys(View.GONE, views); @@ -249,7 +251,7 @@ public static void toggleVisibilitys(View view, View... views){ * @param viewArys * @param views */ - public static void toggleVisibilitys(View[] viewArys, View... views){ + public static void toggleVisibilitys(View[] viewArys, View... views) { toggleVisibilitys(viewArys, View.GONE, views); } @@ -259,7 +261,7 @@ public static void toggleVisibilitys(View[] viewArys, View... views){ * @param status * @param views */ - public static void toggleVisibilitys(View[] viewArys, int status, View... views){ + public static void toggleVisibilitys(View[] viewArys, int status, View... views) { // 默认前面显示 setVisibilitys(View.VISIBLE, viewArys); // 更具状态处理 @@ -275,8 +277,8 @@ public static void toggleVisibilitys(View[] viewArys, int status, View... views) * @param view 需要判断的View * @return */ - public static boolean toogleView(boolean isChange, int isVisibility, View view){ - if (isChange && view != null){ + public static boolean toogleView(boolean isChange, int isVisibility, View view) { + if (isChange && view != null) { view.setVisibility(isVisibility); } return isChange; @@ -289,7 +291,7 @@ public static boolean toogleView(boolean isChange, int isVisibility, View view){ * @param draw * @param views */ - public static void setViewImageRes(int draw, ImageView... views){ + public static void setViewImageRes(int draw, ImageView... views) { setViewImageRes(draw, View.VISIBLE, views); } @@ -299,17 +301,17 @@ public static void setViewImageRes(int draw, ImageView... views){ * @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++){ + 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){ + if (view != null) { try { // 设置背景 view.setImageResource(draw); // 是否显示 view.setVisibility(isVisibility); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setViewImageRes"); } } @@ -337,7 +339,7 @@ public static T findViewById(View view, int id) { * @param * @return */ - public static T findViewById(Window window, int id){ + public static T findViewById(Window window, int id) { return window.findViewById(id); } @@ -400,23 +402,27 @@ public static void requestLayoutParent(View view, boolean isAll) { } /** - * 测量 view + * 测量 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); + 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); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "measureView"); } - view.measure(childWidthSpec, childHeightSpec); } /** @@ -440,7 +446,7 @@ public static int getViewHeight(View view) { } /** - * 获取view的上下文 + * 获取 View 的 Context * @param view * @return */ @@ -453,7 +459,7 @@ public static Activity getActivity(View view) { } context = ((ContextWrapper) context).getBaseContext(); } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getActivity"); } return null; @@ -496,7 +502,7 @@ public static int calcListViewItemHeight(ListView listView, boolean isSet) { // 累加分割线高度 totalHeight += (listView.getDividerHeight() * (listAdapter.getCount() - 1)); // 判断是否需要设置高度 - if (isSet){ + if (isSet) { ViewGroup.LayoutParams params = listView.getLayoutParams(); params.height = totalHeight; listView.setLayoutParams(params); @@ -512,6 +518,7 @@ public static int calcListViewItemHeight(ListView listView, boolean isSet) { * @param gridView * @param numColumns */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) public static int calcGridViewItemHeight(GridView gridView, int numColumns) { return calcGridViewItemHeight(gridView, numColumns, false); } @@ -524,6 +531,7 @@ public static int calcGridViewItemHeight(GridView gridView, int numColumns) { * == * hint: 解决 ScrollView 嵌套 GridView 时, 会无法正确的计算ListView的大小 */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) public static int calcGridViewItemHeight(GridView gridView, int numColumns, boolean isSet) { // 获取 Adapter ListAdapter listAdapter = gridView.getAdapter(); @@ -544,16 +552,16 @@ public static int calcGridViewItemHeight(GridView gridView, int numColumns, bool // 默认表示第一个的高度 singleMax = getItemHeighet(listAdapter, gridView, i * numColumns); // 遍历判断 - for (int eqI = 1; eqI < numColumns; eqI++){ + for (int eqI = 1; eqI < numColumns; eqI++) { // 临时高度 int tempHeight = 0; // 进行判断处理 - if (i * numColumns + eqI <= count){ + if (i * numColumns + eqI <= count) { // 获取对应的高度 tempHeight = getItemHeighet(listAdapter, gridView, i * numColumns + eqI); } // 判断是否在最大高度 - if (tempHeight > singleMax){ + if (tempHeight > singleMax) { singleMax = tempHeight; } } @@ -567,7 +575,7 @@ public static int calcGridViewItemHeight(GridView gridView, int numColumns, bool // 最后获取整个gridView完整显示需要的高度 totalHeight += (vSpace * (count - 1)); // 判断是否需要设置高度 - if (isSet){ + if (isSet) { ViewGroup.LayoutParams params = gridView.getLayoutParams(); params.height = totalHeight; gridView.setLayoutParams(params); @@ -584,8 +592,8 @@ public static int calcGridViewItemHeight(GridView gridView, int numColumns, bool * @param pos * @return */ - public static int getItemHeighet(AbsListView absViews, int pos){ - if (absViews != null){ + public static int getItemHeighet(AbsListView absViews, int pos) { + if (absViews != null) { return getItemHeighet(absViews.getAdapter(), absViews, pos, 0); } return 0; @@ -598,8 +606,8 @@ public static int getItemHeighet(AbsListView absViews, int pos){ * @param dfHeight * @return */ - public static int getItemHeighet(AbsListView absViews, int pos, int dfHeight){ - if (absViews != null){ + public static int getItemHeighet(AbsListView absViews, int pos, int dfHeight) { + if (absViews != null) { return getItemHeighet(absViews.getAdapter(), absViews, pos, dfHeight); } return dfHeight; @@ -612,7 +620,7 @@ public static int getItemHeighet(AbsListView absViews, int pos, int dfHeight){ * @param pos * @return */ - public static int getItemHeighet(ListAdapter listAdapter, AbsListView absViews, int pos){ + public static int getItemHeighet(ListAdapter listAdapter, AbsListView absViews, int pos) { return getItemHeighet(listAdapter, absViews, pos, 0); } @@ -624,7 +632,7 @@ public static int getItemHeighet(ListAdapter listAdapter, AbsListView absViews, * @param dfHeight * @return */ - public static int getItemHeighet(ListAdapter listAdapter, AbsListView absViews, int pos, int dfHeight){ + public static int getItemHeighet(ListAdapter listAdapter, AbsListView absViews, int pos, int dfHeight) { try { // listAdapter.getCount()返回数据项的数目 View listItem = listAdapter.getView(pos, null, absViews); @@ -632,7 +640,7 @@ public static int getItemHeighet(ListAdapter listAdapter, AbsListView absViews, listItem.measure(0, 0); // 统计所有子项的总高度 return listItem.getMeasuredHeight(); - } catch (Exception e){ + } 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 index 7bd4beb5be..7d9a05924e 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/anim/AnimationUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/anim/AnimationUtils.java @@ -3,8 +3,10 @@ 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.RotateAnimation; import android.view.animation.ScaleAnimation; +import android.view.animation.TranslateAnimation; /** * detail: 动画工具类 @@ -15,9 +17,7 @@ public final class AnimationUtils { private AnimationUtils() { } - /** - * 默认动画持续时间 - */ + /** 默认动画持续时间 */ public static final long DEFAULT_ANIMATION_DURATION = 400; /** @@ -202,7 +202,7 @@ public static AlphaAnimation getShowAlphaAnimation() { * 获取一个缩小动画 * @param durationMillis * @param animationListener - * @return + * @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); @@ -214,7 +214,7 @@ public static ScaleAnimation getLessenScaleAnimation(long durationMillis, Animat /** * 获取一个缩小动画 * @param durationMillis - * @return + * @return 一个缩放动画 */ public static ScaleAnimation getLessenScaleAnimation(long durationMillis) { return getLessenScaleAnimation(durationMillis, null); @@ -223,7 +223,7 @@ public static ScaleAnimation getLessenScaleAnimation(long durationMillis) { /** * 获取一个缩小动画 * @param animationListener - * @return + * @return 一个缩放动画 */ public static ScaleAnimation getLessenScaleAnimation(AnimationListener animationListener) { return getLessenScaleAnimation(DEFAULT_ANIMATION_DURATION, animationListener); @@ -233,7 +233,7 @@ public static ScaleAnimation getLessenScaleAnimation(AnimationListener animation * 获取一个放大动画 * @param durationMillis * @param animationListener - * @return + * @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); @@ -245,7 +245,7 @@ public static ScaleAnimation getAmplificationAnimation(long durationMillis, Anim /** * 获取一个放大动画 * @param durationMillis - * @return + * @return 一个缩放动画 */ public static ScaleAnimation getAmplificationAnimation(long durationMillis) { return getAmplificationAnimation(durationMillis, null); @@ -254,10 +254,81 @@ public static ScaleAnimation getAmplificationAnimation(long durationMillis) { /** * 获取一个放大动画 * @param animationListener - * @return + * @return 一个缩放动画 */ public static ScaleAnimation getAmplificationAnimation(AnimationListener animationListener) { return getAmplificationAnimation(DEFAULT_ANIMATION_DURATION, animationListener); } + // ============ + // 视图移动动画 + // ============ + + /** + * 视图移动 + * @param fromXDelta X轴开始坐标 + * @param toXDelta X轴结束坐标 + * @param fromYDelta Y轴开始坐标 + * @param toYDelta Y轴结束坐标 + * @param cycles 重复 + * @param durationMillis 持续时间 + * @return 一个平移动画 + */ + public static TranslateAnimation translate(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta, float cycles, long durationMillis) { + TranslateAnimation translateAnimation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta); + translateAnimation.setDuration(durationMillis); + if (cycles > 0.0) { + translateAnimation.setInterpolator(new CycleInterpolator(cycles)); + } + return translateAnimation; + } + + /** + * 视图摇晃 + * @param fromXDelta X轴开始坐标 + * @param toXDelta X轴结束坐标 + * @param cycles 重复次数 + * @param durationMillis 持续时间 + * @return 一个平移动画 + */ + public static TranslateAnimation shake(float fromXDelta, float toXDelta, float cycles, long durationMillis) { + return translate(fromXDelta, toXDelta, 0.0f, 0.0f, cycles, durationMillis); + } + + /** + * 视图摇晃,默认摇晃幅度为10 + * @param cycles 重复次数 + * @param durationMillis 持续时间 + * @return 一个平移动画 + */ + public static TranslateAnimation shake(float cycles, long durationMillis) { + return translate(0.0f, 10.0f, 0.0f, 0.0f, cycles, durationMillis); + } + + /** + * 视图摇晃,默认摇晃幅度为10,持续700毫秒 + * @param cycles 重复次数 + * @return 一个平移动画 + */ + public static TranslateAnimation shake(float cycles) { + return translate(0.0f, 10.0f, 0.0f, 0.0f, cycles, 700); + } + + /** + * 视图摇晃,默认摇晃幅度为10,重复7次 + * @param durationMillis 持续时间 + * @return 一个平移动画 + */ + public static TranslateAnimation shake(long durationMillis) { + return translate(0.0f, 10.0f, 0.0f, 0.0f, 7, durationMillis); + } + + /** + * 视图摇晃,默认摇晃幅度为10,重复7次,持续700毫秒 + * @return 一个平移动画 + */ + public static TranslateAnimation shake() { + return translate(0.0f, 10.0f, 0.0f, 0.0f, 7, 700); + } + } 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 index cbfaf649a1..1befdc35c5 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/anim/ViewAnimationUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/anim/ViewAnimationUtils.java @@ -4,11 +4,10 @@ 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: 视图动画工具箱,提供简单的控制视图的动画的工具方法 + * detail: 视图动画工具箱(AnimationUtils 基础上封装),提供简单的控制视图的动画的工具方法 * Created by Ttt */ public final class ViewAnimationUtils { @@ -21,7 +20,7 @@ private ViewAnimationUtils() { // ================== /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) + * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) * @param view 被处理的视图 * @param isBanClick 在执行动画的过程中是否禁止点击 * @param durationMillis 持续时间,毫秒 @@ -64,7 +63,7 @@ public void onAnimationEnd(Animation animation) { } /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) + * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 * @param animationListener 动画监听器 @@ -74,7 +73,7 @@ public static void invisibleViewByAlpha(final View view, long durationMillis, fi } /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) + * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 * @param isBanClick 在执行动画的过程中是否禁止点击 @@ -84,7 +83,7 @@ public static void invisibleViewByAlpha(final View view, long durationMillis, bo } /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) + * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 */ @@ -93,7 +92,7 @@ public static void invisibleViewByAlpha(final View view, long durationMillis) { } /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), + * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 * @param isBanClick 在执行动画的过程中是否禁止点击 @@ -104,7 +103,7 @@ public static void invisibleViewByAlpha(final View view, boolean isBanClick, fin } /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), + * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 * @param animationListener 动画监听器 @@ -114,7 +113,7 @@ public static void invisibleViewByAlpha(final View view, final AnimationListener } /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), + * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 * @param isBanClick 在执行动画的过程中是否禁止点击 @@ -124,7 +123,7 @@ public static void invisibleViewByAlpha(final View view, boolean isBanClick) { } /** - * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), + * 将给定视图渐渐隐去(view.setVisibility(View.INVISIBLE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 */ @@ -133,7 +132,7 @@ public static void invisibleViewByAlpha(final View view) { } /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) + * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 * @param isBanClick 在执行动画的过程中是否禁止点击 @@ -176,7 +175,7 @@ public void onAnimationEnd(Animation animation) { } /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) + * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 * @param animationListener 动画监听器 @@ -186,7 +185,7 @@ public static void goneViewByAlpha(final View view, long durationMillis, final A } /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) + * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 * @param isBanClick 在执行动画的过程中是否禁止点击 @@ -196,7 +195,7 @@ public static void goneViewByAlpha(final View view, long durationMillis, final b } /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) + * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 */ @@ -205,7 +204,7 @@ public static void goneViewByAlpha(final View view, long durationMillis) { } /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), + * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 * @param isBanClick 在执行动画的过程中是否禁止点击 @@ -216,7 +215,7 @@ public static void goneViewByAlpha(final View view, final boolean isBanClick, fi } /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), + * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 * @param animationListener 动画监听器 @@ -226,7 +225,7 @@ public static void goneViewByAlpha(final View view, final AnimationListener anim } /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), + * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 * @param isBanClick 在执行动画的过程中是否禁止点击 @@ -236,7 +235,7 @@ public static void goneViewByAlpha(final View view, final boolean isBanClick) { } /** - * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), + * 将给定视图渐渐隐去最后从界面中移除(view.setVisibility(View.GONE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 */ @@ -245,7 +244,7 @@ public static void goneViewByAlpha(final View view) { } /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) + * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 * @param isBanClick 在执行动画的过程中是否禁止点击 @@ -288,7 +287,7 @@ public void onAnimationEnd(Animation animation) { } /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) + * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 * @param animationListener 动画监听器 @@ -298,7 +297,7 @@ public static void visibleViewByAlpha(final View view, long durationMillis, fina } /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) + * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 * @param isBanClick 在执行动画的过程中是否禁止点击 @@ -308,7 +307,7 @@ public static void visibleViewByAlpha(final View view, long durationMillis, fina } /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) + * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)) * @param view 被处理的视图 * @param durationMillis 持续时间,毫秒 */ @@ -317,7 +316,7 @@ public static void visibleViewByAlpha(final View view, long durationMillis) { } /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), + * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 * @param animationListener 动画监听器 @@ -328,7 +327,7 @@ public static void visibleViewByAlpha(final View view, final boolean isBanClick, } /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), + * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 * @param animationListener 动画监听器 @@ -338,7 +337,7 @@ public static void visibleViewByAlpha(final View view, final AnimationListener a } /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), + * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 * @param isBanClick 在执行动画的过程中是否禁止点击 @@ -348,7 +347,7 @@ public static void visibleViewByAlpha(final View view, final boolean isBanClick) } /** - * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), + * 将给定视图渐渐显示出来(view.setVisibility(View.VISIBLE)), * 默认的持续时间为DEFAULT_ALPHA_ANIMATION_DURATION * @param view 被处理的视图 */ @@ -372,11 +371,7 @@ public static void visibleViewByAlpha(final View view) { * @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 translateAnimation = AnimationUtils.translate(fromXDelta, toXDelta, fromYDelta, toYDelta, cycles, durationMillis); translateAnimation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/AsyncExecutor.java b/DevLibUtils/src/main/java/dev/utils/app/assist/AsyncExecutor.java index 1dca496507..9bd8185d84 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/AsyncExecutor.java +++ b/DevLibUtils/src/main/java/dev/utils/app/assist/AsyncExecutor.java @@ -14,33 +14,37 @@ /** * detail: 异步执行 - * @author MaTianyu + * Created by MaTianyu + * Update to Ttt */ public class AsyncExecutor { // 日志TAG private final String TAG = AsyncExecutor.class.getSimpleName(); // 线程池 - private static ExecutorService threadPool; + private ExecutorService threadPool; // 主线程 Hander - public static Handler handler = new Handler(Looper.getMainLooper()); + private Handler handler = new Handler(Looper.getMainLooper()); public AsyncExecutor() { this(null); } - public AsyncExecutor(ExecutorService threadPool) { - if (AsyncExecutor.threadPool != null) { + public AsyncExecutor(ExecutorService pool) { + if (threadPool != null) { shutdownNow(); } if (threadPool == null) { - AsyncExecutor.threadPool = Executors.newCachedThreadPool(); + threadPool = Executors.newCachedThreadPool(); } else { - AsyncExecutor.threadPool = threadPool; + threadPool = pool; } } - public static synchronized void shutdownNow() { + /** + * 立即关闭线程池任务 + */ + public synchronized void shutdownNow() { if (threadPool != null && !threadPool.isShutdown()) threadPool.shutdownNow(); threadPool = null; } @@ -48,7 +52,7 @@ public static synchronized void shutdownNow() { /** * 将任务投入线程池执行 * @param worker - * @return + * @return {@link FutureTask } */ public FutureTask execute(final Worker worker) { Callable call = new Callable() { @@ -84,7 +88,7 @@ protected void done() { * 将子线程结果传递到UI线程 * @param worker * @param result - * @return + * @return */ private T postResult(final Worker worker, final T result) { handler.post(new Runnable() { @@ -99,7 +103,6 @@ public void run() { /** * 将子线程结果传递到UI线程 * @param worker - * @return */ private void postCancel(final Worker worker) { handler.post(new Runnable() { @@ -110,20 +113,40 @@ public void run() { }); } + /** + * 执行任务 + * @param call + * @param + * @return {@link FutureTask} + */ public FutureTask execute(Callable call) { FutureTask task = new FutureTask(call); threadPool.execute(task); return task; } - public static abstract class Worker { + public abstract class Worker { + /** + * 后台运行 + * @return + */ protected abstract T doInBackground(); + /** + * 将子线程结果传递到UI线程 + * @param data + */ 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 index 3f00033fde..5d8a017cea 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/BeepVibrateAssist.java +++ b/DevLibUtils/src/main/java/dev/utils/app/assist/BeepVibrateAssist.java @@ -7,6 +7,7 @@ import android.media.MediaPlayer; import android.os.Vibrator; import android.support.annotation.RawRes; +import android.support.annotation.RequiresPermission; import java.io.Closeable; @@ -20,8 +21,8 @@ public final class BeepVibrateAssist implements Closeable { // 日志TAG private static final String TAG = BeepVibrateAssist.class.getSimpleName(); - // 上下文 - private final Context context; + // Context + private final Context mContext; // 播放资源对象 private MediaPlayer mediaPlayer = null; // 是否需要震动 @@ -34,7 +35,7 @@ public final class BeepVibrateAssist implements Closeable { * @param context */ public BeepVibrateAssist(Context context) { - this.context = context; + this.mContext = context; } /** @@ -43,7 +44,7 @@ public BeepVibrateAssist(Context context) { * @param rawId */ public BeepVibrateAssist(Context context, @RawRes int rawId) { - this.context = context; + this.mContext = context; this.mediaPlayer = buildMediaPlayer(context, rawId); } @@ -53,7 +54,7 @@ public BeepVibrateAssist(Context context, @RawRes int rawId) { * @param path 只支持本地资源 */ public BeepVibrateAssist(Context context, String path) { - this.context = context; + this.mContext = context; this.mediaPlayer = buildMediaPlayer(path); } @@ -61,18 +62,18 @@ public BeepVibrateAssist(Context context, String path) { /** * 检查是否允许播放声音 - * @return + * @return true: 允许, false: 不允许 */ 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); + // RINGER_MODE_NORMAL(普通)、RINGER_MODE_SILENT(静音)、RINGER_MODE_VIBRATE(震动) + AudioManager audioService = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) { shouldPlayBeep = false; // 进入只有属于, 静音、震动,才不播放 } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "shouldBeep"); } } @@ -87,8 +88,8 @@ private synchronized void update() { // 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){ + ((Activity) mContext).setVolumeControlStream(AudioManager.STREAM_MUSIC); + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "update"); } } @@ -98,7 +99,7 @@ private synchronized void update() { /** * 判断是否允许播放声音 - * @return + * @return true: 允许, false: 不允许 */ public boolean isPlayBeep() { return shouldBeep(); @@ -106,7 +107,7 @@ public boolean isPlayBeep() { /** * 获取是否允许震动 - * @return + * @return true: 允许, false: 不允许 */ public boolean isVibrate() { return vibrate; @@ -115,6 +116,7 @@ public boolean isVibrate() { /** * 设置是否允许震动 * @param vibrate + * @return {@link BeepVibrateAssist} */ public BeepVibrateAssist setVibrate(boolean vibrate) { setVibrate(vibrate, 200l); @@ -125,6 +127,7 @@ public BeepVibrateAssist setVibrate(boolean vibrate) { * 设置是否允许震动 * @param vibrate * @param vibrateDuration 震动时间 + * @return {@link BeepVibrateAssist} */ public BeepVibrateAssist setVibrate(boolean vibrate, long vibrateDuration) { this.vibrate = vibrate; @@ -134,6 +137,7 @@ public BeepVibrateAssist setVibrate(boolean vibrate, long vibrateDuration) { /** * 设置播放资源对象 * @param mediaPlayer + * @return {@link BeepVibrateAssist} */ public BeepVibrateAssist setMediaPlayer(MediaPlayer mediaPlayer) { this.mediaPlayer = mediaPlayer; @@ -143,27 +147,32 @@ public BeepVibrateAssist setMediaPlayer(MediaPlayer mediaPlayer) { } /** - * 进行播放声音, 并且振动 + * 进行播放声音, 并且震动 + * */ + @RequiresPermission(android.Manifest.permission.VIBRATE) public synchronized void playBeepSoundAndVibrate() { // 判断是否允许播放 if (shouldBeep() && mediaPlayer != null) { try { // 播放 mediaPlayer.start(); - } catch (Exception e){ + } catch (Exception e) { } } // 判断是否允许震动 if (vibrate) { try { - Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(vibrateDuration); - } catch (Exception e){ + } catch (Exception e) { } } } + /** + * 关闭震动、提示声, 并释放资源 + */ @Override public synchronized void close() { if (mediaPlayer != null) { @@ -176,29 +185,29 @@ public synchronized void close() { /** * 创建 MediaPlayer 对象 - * @param context 上下文 + * @param context * @param rawId 响声资源id - * @return + * @return {@link MediaPlayer} */ - public static final MediaPlayer buildMediaPlayer(Context context, @RawRes int rawId) { + public static MediaPlayer buildMediaPlayer(Context context, @RawRes int rawId) { return buildMediaPlayer(context, rawId, 0.1f); } /** * 创建 MediaPlayer 对象 - * @param context 上下文 + * @param context * @param rawId 响声资源id * @param beepVolume 音量 - * @return + * @return {@link MediaPlayer} */ - public static final MediaPlayer buildMediaPlayer(Context context, @RawRes int rawId, float beepVolume) { + public static 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){ +// if (mediaPlayer != null) { // mediaPlayer.seekTo(0); // } } @@ -233,9 +242,9 @@ public synchronized boolean onError(MediaPlayer mp, int what, int extra) { /** * 创建 MediaPlayer 对象 * @param path 响声资源路径(只支持本地资源) - * @return + * @return {@link MediaPlayer} */ - public static final MediaPlayer buildMediaPlayer(String path) { + public static MediaPlayer buildMediaPlayer(String path) { return buildMediaPlayer(path, 0.1f); } @@ -243,16 +252,16 @@ public static final MediaPlayer buildMediaPlayer(String path) { * 创建 MediaPlayer 对象 * @param path 响声资源路径(只支持本地资源) * @param beepVolume 音量 - * @return + * @return {@link MediaPlayer} */ - public static final MediaPlayer buildMediaPlayer(String path, float beepVolume) { + public static 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){ +// if (mediaPlayer != null) { // mediaPlayer.seekTo(0); // } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java b/DevLibUtils/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java index ecd5f9444c..95fa30a81f 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java +++ b/DevLibUtils/src/main/java/dev/utils/app/assist/InactivityTimerAssist.java @@ -43,7 +43,7 @@ public InactivityTimerAssist(Activity activity, long inactivityTime) { // = 内部方法 = /** - * 开始任务 + * 开始计时任务 */ public synchronized void start() { // 取消任务 @@ -59,7 +59,7 @@ public synchronized void start() { } /** - * 取消任务 + * 取消计时任务 */ private synchronized void cancel() { AsyncTask task = inactivityTask; @@ -81,18 +81,18 @@ public synchronized void onPause() { try { // 取消注册广播 activity.unregisterReceiver(powerStatusReceiver); - } catch (Exception e){ + } catch (Exception e) { } } /** - * 回到页面处理 + * 回到 Activity/xx 处理 */ public synchronized void onResume() { try { // 注册广播 activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - } catch (Exception e){ + } catch (Exception e) { } // 开始检测 start(); @@ -108,7 +108,8 @@ public void onDestroy() { // = /** - * 电池广播 + * detail: 电池监听广播 + * Created by Ttt */ private class PowerStatusReceiver extends BroadcastReceiver { @Override @@ -126,7 +127,8 @@ public void onReceive(Context context, Intent intent) { } /** - * 定时检测任务 + * detail: 定时检测任务 + * Created by Ttt */ private class InactivityAsyncTask extends AsyncTask { @Override @@ -134,7 +136,7 @@ protected Object doInBackground(Object... objects) { try { Thread.sleep(inactivityTime); // 关闭页面 - if (activity != null){ + if (activity != null) { activity.finish(); } } catch (InterruptedException e) { diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java b/DevLibUtils/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java index 0828bbd4ff..f1b0f6d99d 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java +++ b/DevLibUtils/src/main/java/dev/utils/app/assist/ScreenSensorAssist.java @@ -44,13 +44,13 @@ public final class ScreenSensorAssist { public static final int CHANGE_ORIENTATION_WHAT = 9919; // ========= 变量 ============= /** 是否允许切屏 */ - private boolean isAllowChange = false; + private boolean allowChange = false; /** 是否是竖屏 */ - private boolean isPortrait = true; + private boolean portrait = true; /** 回调操作 */ private Handler handler; /** 角度处理Handler */ - private Handler rotateHandler = new Handler(){ + private Handler rotateHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case CHANGE_ORIENTATION_WHAT: @@ -62,9 +62,9 @@ public void handleMessage(Message msg) { if (rotation > 45 && rotation < 135) { // 横屏 - 屏幕对着别人 LogPrintUtils.dTag(TAG, "切换成横屏 - 屏幕对着自己"); // - - if (isPortrait) { - isPortrait = false; - if(handler != null){ + if (portrait) { + portrait = false; + if(handler != null) { Message vMsg = new Message(); vMsg.what = CHANGE_ORIENTATION_WHAT; vMsg.arg1 = 1; @@ -74,9 +74,9 @@ public void handleMessage(Message msg) { } else if (rotation > 135 && rotation < 225) { // 竖屏 - 屏幕对着别人 LogPrintUtils.dTag(TAG,"切换成竖屏 - 屏幕对着别人"); // - - if (!isPortrait) { - isPortrait = true; - if(handler != null){ + if (!portrait) { + portrait = true; + if(handler != null) { Message vMsg = new Message(); vMsg.what = CHANGE_ORIENTATION_WHAT; vMsg.arg1 = 2; @@ -86,9 +86,9 @@ public void handleMessage(Message msg) { } else if (rotation > 225 && rotation < 315) { // 横屏 - 屏幕对着自己 LogPrintUtils.dTag(TAG, "切换成横屏 - 屏幕对着自己"); // - - if (isPortrait) { - isPortrait = false; - if(handler != null){ + if (portrait) { + portrait = false; + if(handler != null) { Message vMsg = new Message(); vMsg.what = CHANGE_ORIENTATION_WHAT; vMsg.arg1 = 1; @@ -98,9 +98,9 @@ public void handleMessage(Message msg) { } else if ((rotation > 315 && rotation < 360) || (rotation > 0 && rotation < 45)) { // 竖屏 - 屏幕对着自己 LogPrintUtils.dTag(TAG,"切换成竖屏 - 屏幕对着自己"); // - - if (!isPortrait) { - isPortrait = true; - if(handler != null){ + if (!portrait) { + portrait = true; + if(handler != null) { Message vMsg = new Message(); vMsg.what = CHANGE_ORIENTATION_WHAT; vMsg.arg1 = 2; @@ -118,10 +118,10 @@ public void handleMessage(Message msg) { // === /** * 初始化操作 - * @param context 上下文 + * @param context * @param handler 回调Handler */ - private void init(Context context, Handler handler){ + private void init(Context context, Handler handler) { this.handler = handler; // 注册重力感应器,监听屏幕旋转 sMamager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); @@ -139,7 +139,7 @@ private void init(Context context, Handler handler){ * @param context */ public void start(Context context, Handler handler) { - isAllowChange = true; + allowChange = true; try { LogPrintUtils.dTag(TAG, "start orientation listener."); // 初始化操作 @@ -153,7 +153,7 @@ public void start(Context context, Handler handler) { /** 停止监听 */ public void stop() { - isAllowChange = false; + allowChange = false; LogPrintUtils.dTag(TAG, "stop orientation listener."); try { sMamager.unregisterListener(sListener); @@ -165,15 +165,21 @@ public void stop() { } catch (Exception e) { } } - - /** 是否竖屏 */ - public boolean isPortrait(){ - return this.isPortrait; + + /** + * 是否竖屏 + * @return true: 是竖屏, false: 非竖屏 + */ + public boolean isPortrait() { + return this.portrait; } - - /** 是否允许切屏 */ - public boolean isAllowChange(){ - return this.isAllowChange; + + /** + * 是否允许切屏 + * @return true: 允许, false: 不允许 + */ + public boolean isAllowChange() { + return this.allowChange; } // === @@ -239,12 +245,12 @@ public void onSensorChanged(SensorEvent event) { } } if (orientation > 225 && orientation < 315) {// 检测到当前实际是横屏 - if (!isPortrait) { + if (!portrait) { sMamager.registerListener(sListener, sensor, SensorManager.SENSOR_DELAY_UI); sManagerChange.unregisterListener(slistenerChange); } } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) {// 检测到当前实际是竖屏 - if (isPortrait) { + if (portrait) { sMamager.registerListener(sListener, sensor, SensorManager.SENSOR_DELAY_UI); sManagerChange.unregisterListener(slistenerChange); } 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 index 3100bece34..593cb5ce5e 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/manager/ActivityManager.java +++ b/DevLibUtils/src/main/java/dev/utils/app/assist/manager/ActivityManager.java @@ -32,9 +32,9 @@ public static ActivityManager getInstance() { } /** - * 通过上下文 获取Activity + * 通过 Context 获取Activity * @param context - * @return + * @return context 所属的 Activity */ public static Activity getActivity(Context context) { try { @@ -51,10 +51,10 @@ public static Activity getActivity(Context context) { /** * 判断页面是否关闭 * @param activity - * @return + * @return true: 关闭, false: 未关闭 */ - public static boolean isFinishing(Activity activity){ - if (activity != null){ + public static boolean isFinishing(Activity activity) { + if (activity != null) { return activity.isFinishing(); } return false; @@ -63,13 +63,13 @@ public static boolean isFinishing(Activity activity){ /** * 判断页面是否关闭 * @param context - * @return + * @return true: 关闭, false: 未关闭 */ - public static boolean isFinishingCtx(Context context){ - if (context != null){ + public static boolean isFinishingCtx(Context context) { + if (context != null) { try { return ((Activity) context).isFinishing(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isFinishingCtx"); } } @@ -83,7 +83,7 @@ public static boolean isFinishingCtx(Context context){ /** * 获取 Activity 栈 - * @return + * @return Activity 栈 */ public Stack getActivityStacks() { return activityStacks; @@ -127,23 +127,23 @@ public void removeActivity(Activity activity) { * @param activitys */ public void removeActivity(Activity... activitys) { - if (activitys != null && activitys.length != 0){ - for (int i = 0, len = activitys.length; i < len; i++){ + if (activitys != null && activitys.length != 0) { + for (int i = 0, len = activitys.length; i < len; i++) { removeActivity(activitys[i]); } } } /** - * 获取当前Activity - * @return + * 获取最后一个(当前)Activity + * @return lastElement() activity */ public Activity currentActivity() { return activityStacks.lastElement(); } /** - * 结束当前Activity + * 结束最后一个(当前)Activity */ public void finishActivity() { finishActivity(activityStacks.lastElement()); @@ -168,8 +168,8 @@ public void finishActivity(Activity activity) { * @param activitys */ public void finishActivity(Activity... activitys) { - if (activitys != null && activitys.length != 0){ - for (int i = 0, len = activitys.length; i < len; i++){ + if (activitys != null && activitys.length != 0) { + for (int i = 0, len = activitys.length; i < len; i++) { finishActivity(activitys[i]); } } @@ -192,7 +192,7 @@ public void finishActivity(Class cls) { Activity activity = iterator.next(); // 判断是否想要关闭的Activity if (activity != null) { - if (activity.getClass() == cls){ + if (activity.getClass() == cls) { // 如果页面没有finish 则进行finish if (!activity.isFinishing()) { activity.finish(); @@ -218,12 +218,7 @@ public void finishActivity(Class cls) { * @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){ + if (clss != null && clss.length != 0) { synchronized (activityStacks) { // 保存新的任务,防止出现同步问题 Stack aStacks = new Stack<>(); @@ -241,15 +236,15 @@ public void finishActivity(Class... clss) { // 默认不需要销毁 isRemove = false; // 循环判断 - for (int i = 0, len = clss.length; i < len; i++){ + for (int i = 0, len = clss.length; i < len; i++) { // 判断是否相同 - if (activity.getClass() == clss[i]){ + if (activity.getClass() == clss[i]) { isRemove = true; break; } } // 判断是否销毁 - if (isRemove){ + if (isRemove) { // 如果页面没有finish 则进行finish if (!activity.isFinishing()) { activity.finish(); @@ -275,7 +270,7 @@ public void finishActivity(Class... clss) { * 结束全部Activity 除忽略的页面外 * @param cls */ - public void finishAllActivityToIgnore(Class cls){ + public void finishAllActivityToIgnore(Class cls) { synchronized (activityStacks) { // 保存新的任务,防止出现同步问题 Stack aStacks = new Stack<>(); @@ -288,7 +283,7 @@ public void finishAllActivityToIgnore(Class cls){ Activity activity = iterator.next(); // 判断是否想要关闭的Activity if (activity != null) { - if (!(activity.getClass() == cls)){ + if (!(activity.getClass() == cls)) { // 如果页面没有finish 则进行finish if (!activity.isFinishing()) { activity.finish(); @@ -313,8 +308,8 @@ public void finishAllActivityToIgnore(Class cls){ * 结束全部Activity 除忽略的页面外 * @param clss */ - public void finishAllActivityToIgnore(Class... clss){ - if (clss != null && clss.length != 0){ + public void finishAllActivityToIgnore(Class... clss) { + if (clss != null && clss.length != 0) { synchronized (activityStacks) { // 保存新的任务,防止出现同步问题 Stack aStacks = new Stack<>(); @@ -332,15 +327,15 @@ public void finishAllActivityToIgnore(Class... clss){ // 默认需要销毁 isRemove = true; // 循环判断 - for (int i = 0, len = clss.length; i < len; i++){ + for (int i = 0, len = clss.length; i < len; i++) { // 判断是否相同 - if (activity.getClass() == clss[i]){ + if (activity.getClass() == clss[i]) { isRemove = false; break; } } // 判断是否销毁 - if (isRemove){ + if (isRemove) { // 如果页面没有finish 则进行finish if (!activity.isFinishing()) { activity.finish(); @@ -388,31 +383,15 @@ public void finishAllActivity() { } } - /** - * 退出应用程序 - * @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的都为异常退出) + // 退出JVM(java虚拟机),释放所占内存资源,0表示正常退出(非0的都为异常退出) System.exit(0); - //从操作系统中结束掉当前程序的进程 + // 从操作系统中结束掉当前程序的进程 android.os.Process.killProcess(android.os.Process.myPid()); } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "appExit"); @@ -422,9 +401,9 @@ public void appExit() { } /** - * 重启app + * 重启 App */ - public static void restartApplication() { + public void restartApplication() { try { Intent intent = DevUtils.getContext().getPackageManager().getLaunchIntentForPackage(DevUtils.getContext().getPackageName()); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 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 index dd342e52ea..f56fb75875 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/manager/TimerManager.java +++ b/DevLibUtils/src/main/java/dev/utils/app/assist/manager/TimerManager.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Timer; import dev.utils.LogPrintUtils; @@ -35,20 +36,20 @@ private TimerManager() { private static final String TAG = TimerManager.class.getSimpleName(); /** 内部保存定时器对象,防止忘记关闭等其他情况,以及便于控制处理 */ - private static final ArrayList listAbsTimers = new ArrayList(); + private static final List listAbsTimers = new ArrayList<>(); // ============= ArrayList 对外公开的方法 ============= /** 回收资源 */ public static void gc() { synchronized (listAbsTimers) { // 临时数据源 - ArrayList lists = new ArrayList(listAbsTimers); + List lists = new ArrayList<>(listAbsTimers); // 清空旧的数据 listAbsTimers.clear(); // 开始删除无用资源 Iterator iterator = lists.iterator(); while (iterator.hasNext()) { - if (iterator.next().isMarkSweep) { // 需要回收,则进行回收 + if (iterator.next().markSweep) { // 需要回收,则进行回收 iterator.remove(); } } @@ -60,7 +61,10 @@ public static void gc() { } } - /** 获取全部任务总数 */ + /** + * 获取全部任务总数 + * @return 全部任务总数 + */ public static int timerSize() { return listAbsTimers.size(); } @@ -68,7 +72,7 @@ public static int timerSize() { /** * 获取属于对应字符串标记的定时器任务(优先获取符合的) * @param markStr - * @return + * @return 获取对应标记的定时任务 {@link AbsTimer} 对象 */ public static AbsTimer getTimer(String markStr) { try { @@ -88,7 +92,7 @@ public static AbsTimer getTimer(String markStr) { /** * 获取属于标记id的定时器任务(优先获取符合的) * @param markId - * @return + * @return 获取对应标记的定时任务 {@link AbsTimer} 对象 */ public static AbsTimer getTimer(int markId) { try { @@ -171,34 +175,71 @@ public static void closeMark(int markId) { // =============== 对外公开初始化AbsTimer方法(内部控制对 TimerTask的生成) ============= - /** 创建定时器 => 立即执行,无限循环,通知默认what */ - public static AbsTimer creTimer(Handler handler, long period) { - return creTimer(handler, AbsTimer.TIMER_NOTIFY_WHAT, 0l, period, -1); + /** + * 创建定时器 => 立即执行,无限循环,通知默认what + * @param handler 通知的Handler + * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 + */ + public static AbsTimer createTimer(Handler handler, long period) { + return createTimer(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 + * @param handler 通知的Handler + * @param delay 延迟时间 - 多少毫秒后开始执行 + * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 + */ + public static AbsTimer createTimer(Handler handler, long delay, long period) { + return createTimer(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); + /** + * 创建定时器 => 立即执行,通知默认what + * @param handler 通知的Handler + * @param period 循环时间 - 每隔多少秒执行一次 + * @param triggerLimit 触发次数上限(-1,表示无限循环) + * @return 定时器抽象对象 + */ + public static AbsTimer createTimer(Handler handler, long period, int triggerLimit) { + return createTimer(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); + /** + * 创建定时器 => 立即执行,无限循环 + * @param handler 通知的Handler + * @param what 通知的what + * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 + */ + public static AbsTimer createTimer(Handler handler, int what, long period) { + return createTimer(handler, what, 0l, period, -1); } - /** 创建定时器 => 无限循环 */ - public static AbsTimer creTimer(Handler handler, int what, long delay, long period) { - return creTimer(handler, what, delay, period, -1); + /** + * 创建定时器 => 无限循环 + * @param handler 通知的Handler + * @param what 通知的what + * @param delay 延迟时间 - 多少毫秒后开始执行 + * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 + */ + public static AbsTimer createTimer(Handler handler, int what, long delay, long period) { + return createTimer(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 period 循环时间 - 每隔多少秒执行一次 + * @param triggerLimit 触发次数上限(-1,表示无限循环) + * @return 定时器抽象对象 + */ + public static AbsTimer createTimer(Handler handler, int what, long period, int triggerLimit) { + return createTimer(handler, what, 0l, period, triggerLimit); } /** @@ -208,9 +249,9 @@ public static AbsTimer creTimer(Handler handler, int what, long period, int trig * @param delay 延迟时间 - 多少毫秒后开始执行 * @param period 循环时间 - 每隔多少秒执行一次 * @param triggerLimit 触发次数上限(-1,表示无限循环) - * @return + * @return 定时器抽象对象 */ - public static AbsTimer creTimer(Handler handler, int what, long delay, long period, int triggerLimit) { + public static AbsTimer createTimer(Handler handler, int what, long delay, long period, int triggerLimit) { return new TimerTask(handler, what, delay, period, triggerLimit); } @@ -223,28 +264,45 @@ public static abstract class AbsTimer { /** 默认通知的what */ public static final int TIMER_NOTIFY_WHAT = 50000; /** 状态标识 - 是否标记清除 */ - private boolean isMarkSweep = true; - // -- + private boolean markSweep = true; /** int 标记 */ private int markId = -1; /** String 标记 */ private String markStr = null; + /** + * 获取标记id + * @return markId + */ public final int getMarkId() { return markId; } + /** + * 获取标记字符串 + * @return markStr + */ public final String getMarkStr() { return markStr; } // -- + /** + * 设置标记id + * @param markId + * @return 定时器抽象对象 + */ public final AbsTimer setMarkId(int markId) { this.markId = markId; return this; } + /** + * 设置标记字符串 + * @param markStr + * @return 定时器抽象对象 + */ public final AbsTimer setMarkStr(String markStr) { this.markStr = markStr; return this; @@ -253,9 +311,9 @@ public final AbsTimer setMarkStr(String markStr) { // ======= 定时器任务,功能实现方法 ========= /** 运行定时器 */ - public void startTimer() { // 如果外部通过了creTimer或者直接new AbsTimer 初始化了对象,没有调用startTimer,都不会保存到 listAbsTimers 并不影响对定时器的控制 + public void startTimer() { // 如果外部通过了createTimer或者直接new AbsTimer 初始化了对象,没有调用startTimer,都不会保存到 listAbsTimers 并不影响对定时器的控制 // 标记状态 - 不需要回收 - this.isMarkSweep = false; + this.markSweep = false; synchronized (listAbsTimers) { // 不存在才进行添加 if (!listAbsTimers.contains(this)) { @@ -267,7 +325,7 @@ public void startTimer() { // 如果外部通过了creTimer或者直接new AbsTi /** 关闭定时器 */ public void closeTimer() { // 标记状态 - 需要回收 - this.isMarkSweep = true; + this.markSweep = true; } /** 判断是否运行中 */ @@ -288,18 +346,21 @@ public void closeTimer() { /** * 设置通知的Handler * @param handler + * @return 定时器抽象对象 */ public abstract AbsTimer setHandler(Handler handler); /** * 设置通知的What * @param notifyWhat + * @return 定时器抽象对象 */ public abstract AbsTimer setNotifyWhat(int notifyWhat); /** * 设置通知的Obj * @param notifyObj + * @return 定时器抽象对象 */ public abstract AbsTimer setNotifyObject(Object notifyObj); @@ -307,12 +368,14 @@ public void closeTimer() { * 设置时间 * @param delay 延迟时间 - 多少毫秒后开始执行 * @param period 循环时间 - 每隔多少秒执行一次 + * @return 定时器抽象对象 */ public abstract AbsTimer setTime(long delay, long period); /** * 设置触发次数上限 * @param triggerLimit + * @return 定时器抽象对象 */ public abstract AbsTimer setTriggerLimit(int triggerLimit); } @@ -343,7 +406,7 @@ private static final class TimerTask extends AbsTimer { /** 触发次数 */ private int triggerNumber = 0; /** 定时器是否运行中 */ - private boolean isRunTimer = false; + private boolean running = false; public TimerTask (Handler handler, int what, long delay, long period, int triggerLimit) { this.handler = handler; @@ -358,7 +421,7 @@ private void start() { // 先关闭旧的定时器 close(); // 表示运行定时器中 - isRunTimer = true; + running = true; // 每次重置触发次数 triggerNumber = 0; // 开启定时器 @@ -368,7 +431,7 @@ private void start() { @Override public void run() { // 表示运行定时器中 - isRunTimer = true; + running = true; // 累积触发次数 triggerNumber++; // 进行通知 @@ -389,7 +452,7 @@ public void run() { timer.schedule(timerTask, delay, period); } catch (Exception e) { // 表示非运行定时器中 - isRunTimer = false; + running = false; // 关闭任务,进行标记需要回收 closeTimer(); // 启动失败,则进行标记,标记需要回收 } @@ -398,7 +461,7 @@ public void run() { /** 关闭定时器任务 */ private void close() { // 表示非运行定时器中 - isRunTimer = false; + running = false; // 取消定时器任务 try { if (timer != null) { @@ -432,7 +495,7 @@ public void closeTimer() { @Override public boolean isRunTimer() { - return isRunTimer; + return running; } @Override diff --git a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCache.java b/DevLibUtils/src/main/java/dev/utils/app/cache/DevCache.java index 4d958e1af6..eff3e3301b 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCache.java +++ b/DevLibUtils/src/main/java/dev/utils/app/cache/DevCache.java @@ -32,8 +32,7 @@ /** * detail: 缓存工具类 - * Created by 杨福海(michael) www.yangfuhai.com - * https://github.com/yangfuhai/ASimpleCache + * Created by 杨福海(michael) www.yangfuhai.com * Update to Ttt */ public final class DevCache { @@ -52,7 +51,7 @@ private DevCache() { // public static final int SAVE_SUC = 4; // // 保存失败 // public static final int SAVE_FAIL = 5; - // ========= + // === // 日志Tag private static final String TAG = DevCache.class.getSimpleName(); @@ -67,18 +66,18 @@ private DevCache() { // 不限制存放数据的数量 private static final int MAX_COUNT = Integer.MAX_VALUE; // 不同地址配置缓存对象 - private static Map mInstanceMap = new HashMap(); + private static Map mInstanceMap = new HashMap<>(); // 缓存管理类 private DevCacheManager mCache; // 缓存地址 private static File ctxCacheDir = null; /** - * 内部处理防止 Context 为null奔溃问题 - * @return + * 内部处理防止 Context 为 null 崩溃问题 + * @return {@link Context} */ - private static Context getContext(Context context){ - if (context != null){ + private static Context getContext(Context context) { + if (context != null) { return context; } else { // 设置全局Context @@ -89,10 +88,10 @@ private static Context getContext(Context context){ /** * 获取缓存地址 * @param ctx - * @return + * @return 应用缓存地址 */ - public static File getCacheDir(Context ctx){ - if (ctxCacheDir == null){ + public static File getCacheDir(Context ctx) { + if (ctxCacheDir == null) { ctxCacheDir = getContext(ctx).getCacheDir(); } return ctxCacheDir; @@ -101,7 +100,7 @@ public static File getCacheDir(Context ctx){ /** * 默认缓存地址 * @param ctx - * @return + * @return {@link DevCache} 缓存工具类对象 */ public static DevCache get(Context ctx) { return get(ctx, DF_FILE_NAME); @@ -111,7 +110,7 @@ public static DevCache get(Context ctx) { * 获取缓存地址 * @param ctx * @param cacheName - * @return + * @return {@link DevCache} 缓存工具类对象 */ public static DevCache get(Context ctx, String cacheName) { // 进行处理 @@ -123,7 +122,7 @@ public static DevCache get(Context ctx, String cacheName) { /** * 设置自定义缓存地址 * @param cacheDir - * @return + * @return {@link DevCache} 缓存工具类对象 */ public static DevCache get(File cacheDir) { return get(cacheDir, MAX_SIZE, MAX_COUNT); @@ -134,7 +133,7 @@ public static DevCache get(File cacheDir) { * @param ctx * @param max_zise * @param max_count - * @return + * @return {@link DevCache} 缓存工具类对象 */ public static DevCache get(Context ctx, long max_zise, int max_count) { File file = new File(getCacheDir(ctx), DF_FILE_NAME); @@ -147,7 +146,7 @@ public static DevCache get(Context ctx, long max_zise, int max_count) { * @param cacheDir * @param max_zise * @param max_count - * @return + * @return {@link DevCache} 缓存工具类对象 */ public static DevCache get(File cacheDir, long max_zise, int max_count) { // 判断是否存在缓存信息 @@ -162,7 +161,7 @@ public static DevCache get(File cacheDir, long max_zise, int max_count) { /** * 获取进程pid - * @return + * @return _android.os.Process.myPid() */ private static String myPid() { return "_" + android.os.Process.myPid(); @@ -173,9 +172,10 @@ private static String myPid() { * @param cacheDir * @param max_size * @param max_count + * @return {@link DevCache} 缓存工具类对象 */ private DevCache(File cacheDir, long max_size, int max_count) { - if (cacheDir == null){ + if (cacheDir == null) { new Exception("cacheDir is null"); } else if (!cacheDir.exists() && !cacheDir.mkdirs()) { new Exception("can't make dirs in " + cacheDir.getAbsolutePath()); @@ -186,11 +186,7 @@ private DevCache(File cacheDir, long max_size, int 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() } + * its contents will be registered in the cache */ class xFileOutputStream extends FileOutputStream { File file; @@ -217,7 +213,7 @@ public void close() throws IOException { */ public void put(String key, String value) { File file = mCache.newFile(key); - if (file == null || value == null){ + if (file == null || value == null) { return; } BufferedWriter out = null; @@ -245,7 +241,7 @@ public void put(String key, String value) { * @param saveTime 保存的时间,单位:秒 */ public void put(String key, String value, int saveTime) { - if (key != null && value != null){ + if (key != null && value != null) { put(key, DevCacheUtils.newStringWithDateInfo(saveTime, value)); } } @@ -257,7 +253,7 @@ public void put(String key, String value, int saveTime) { */ public String getAsString(String key) { File file = mCache.get(key); - if (file == null){ + if (file == null) { return null; } if (!file.exists()) @@ -303,10 +299,10 @@ public String getAsString(String key) { * @param value 保存的JSON数据 */ public void put(String key, JSONObject value) { - if (value != null){ + if (value != null) { try { put(key, value.toString()); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "put JSONObject"); } } @@ -319,10 +315,10 @@ public void put(String key, JSONObject value) { * @param saveTime 保存的时间,单位:秒 */ public void put(String key, JSONObject value, int saveTime) { - if (value != null){ + if (value != null) { try { put(key, value.toString(), saveTime); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "put JSONObject"); } } @@ -331,11 +327,11 @@ public void put(String key, JSONObject value, int saveTime) { /** * 读取 JSONObject 数据 * @param key - * @return JSONObject 数据 + * @return {@link JSONObject} */ public JSONObject getAsJSONObject(String key) { String JSONString = getAsString(key); - if (JSONString != null){ + if (JSONString != null) { try { JSONObject obj = new JSONObject(JSONString); return obj; @@ -356,10 +352,10 @@ public JSONObject getAsJSONObject(String key) { * @param value 保存的JSONArray数据 */ public void put(String key, JSONArray value) { - if (value != null){ + if (value != null) { try { put(key, value.toString()); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "put JSONArray"); } } @@ -372,10 +368,10 @@ public void put(String key, JSONArray value) { * @param saveTime 保存的时间,单位:秒 */ public void put(String key, JSONArray value, int saveTime) { - if (value != null){ + if (value != null) { try { put(key, value.toString(), saveTime); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "put JSONArray"); } } @@ -384,7 +380,7 @@ public void put(String key, JSONArray value, int saveTime) { /** * 读取 JSONArray 数据 * @param key - * @return JSONArray 数据 + * @return {@link JSONArray} */ public JSONArray getAsJSONArray(String key) { String JSONString = getAsString(key); @@ -409,7 +405,7 @@ public JSONArray getAsJSONArray(String key) { * @param value 保存的数据 */ public void put(String key, byte[] value) { - if (key == null || value == null){ + if (key == null || value == null) { return; } File file = mCache.newFile(key); @@ -432,14 +428,14 @@ public void put(String key, byte[] value) { } /** - * Cache for a stream + * 返回缓存流写入数据对象 * @param key the file name. - * @return OutputStream stream for writing data. + * @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){ + if (file != null) { return new xFileOutputStream(file); } return null; @@ -452,7 +448,7 @@ public OutputStream put(String key) throws FileNotFoundException { */ public InputStream get(String key) throws FileNotFoundException { File file = mCache.get(key); - if (file != null && file.exists()){ + if (file != null && file.exists()) { return new FileInputStream(file); } return null; @@ -469,9 +465,9 @@ public void put(String key, byte[] value, int saveTime) { } /** - * 获取 byte 数据 + * 获取 byte[] 数据 * @param key - * @return byte 数据 + * @return byte[] 数据 */ public byte[] getAsBinary(String key) { RandomAccessFile RAFile = null; @@ -670,7 +666,7 @@ public Drawable getAsDrawable(String key) { */ public File file(String key) { File f = mCache.newFile(key); - if (f != null && f.exists()){ + if (f != null && f.exists()) { return f; } return null; diff --git a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheManager.java b/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheManager.java index 0fbd5e5c05..388c4e6bde 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheManager.java +++ b/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheManager.java @@ -10,8 +10,7 @@ /** * detail: 缓存管理类 - * Created by 杨福海(michael) www.yangfuhai.com - * https://github.com/yangfuhai/ASimpleCache + * Created by 杨福海(michael) www.yangfuhai.com * Update to Ttt */ final class DevCacheManager { @@ -98,11 +97,11 @@ protected void put(File file) { /** * 获取文件 * @param key - * @return + * @return {@link File} */ protected File get(String key) { File file = newFile(key); - if (file != null){ + if (file != null) { Long currentTime = System.currentTimeMillis(); file.setLastModified(currentTime); lastUsageDates.put(file, currentTime); @@ -114,10 +113,10 @@ protected File get(String key) { /** * 创建文件对象 * @param key - * @return + * @return {@link File} */ protected File newFile(String key) { - if (key != null){ + if (key != null) { return new File(cacheDir, key.hashCode() + ""); } return null; @@ -126,11 +125,11 @@ protected File newFile(String key) { /** * 删除文件 * @param key - * @return + * @return true: 删除成功, false: 删除失败 */ protected boolean remove(String key) { File file = get(key); - if (file != null){ + if (file != null) { return file.delete(); } return false; @@ -152,7 +151,7 @@ protected void clear() { /** * 移除旧的文件 - * @return + * @return 返回移除的文件大小 */ private long removeNext() { if (lastUsageDates.isEmpty()) { @@ -186,10 +185,10 @@ private long removeNext() { /** * 计算文件大小 * @param file - * @return + * @return 文件大小 */ private long calculateSize(File file) { - if (file != null){ + if (file != null) { return file.length(); } return 0l; diff --git a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheUtils.java b/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheUtils.java index 230204ac58..643e4a5ac5 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheUtils.java @@ -13,13 +13,12 @@ /** * detail: 缓存检查(时间)工具类 - * Created by 杨福海(michael) www.yangfuhai.com - * https://github.com/yangfuhai/ASimpleCache + * Created by 杨福海(michael) www.yangfuhai.com * Update to Ttt */ final class DevCacheUtils { - private DevCacheUtils(){ + private DevCacheUtils() { } // 日志Tag @@ -28,7 +27,7 @@ private DevCacheUtils(){ /** * 判断缓存的 String 数据是否到期 * @param str - * @return true: 到期了 false: 还没有到期 + * @return true: 到期了, false: 还没有到期 */ public static boolean isDue(String str) { return isDue(str.getBytes()); @@ -37,7 +36,7 @@ public static boolean isDue(String str) { /** * 判断缓存的 byte 数据是否到期 * @param data - * @return true: 到期了 false: 还没有到期 + * @return true: 到期了, false: 还没有到期 */ public static boolean isDue(byte[] data) { // 获取时间数据信息 @@ -80,14 +79,14 @@ public static String newStringWithDateInfo(int second, String strInfo) { * @return */ public static byte[] newByteArrayWithDateInfo(int second, byte[] data) { - if (data != null){ + 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){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "newByteArrayWithDateInfo"); } } @@ -130,7 +129,7 @@ public static byte[] clearDateInfo(byte[] data) { if (hasDateInfo(data)) { try { return copyOfRange(data, indexOf(data, mSeparator) + 1, data.length); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "clearDateInfo"); } } @@ -160,7 +159,7 @@ private static String[] getDateInfoFromDate(byte[] data) { String deleteAfter = new String(copyOfRange(data, 14, indexOf(data, mSeparator))); // 返回数据 return new String[]{saveDate, deleteAfter}; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getDateInfoFromDate"); } } @@ -201,7 +200,7 @@ public static byte[] bitmap2Bytes(Bitmap bm) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, 100, baos); return baos.toByteArray(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "bitmap2Bytes"); } return null; @@ -216,7 +215,7 @@ public static Bitmap bytes2Bimap(byte[] bytes) { if (bytes != null && bytes.length != 0) { try { return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "bytes2Bimap"); } } @@ -246,7 +245,7 @@ public static Bitmap drawable2Bitmap(Drawable drawable) { // 把 drawable 内容画到画布中 drawable.draw(canvas); return bitmap; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "drawable2Bitmap"); } return null; @@ -266,7 +265,7 @@ public static Drawable bitmap2Drawable(Bitmap bm) { BitmapDrawable bd = new BitmapDrawable(bm); bd.setTargetDensity(bm.getDensity()); return new BitmapDrawable(bm); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "bitmap2Drawable"); } return null; diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/AutoFocusAssist.java b/DevLibUtils/src/main/java/dev/utils/app/camera1/AutoFocusAssist.java similarity index 84% rename from DevLibUtils/src/main/java/dev/utils/app/assist/camera/AutoFocusAssist.java rename to DevLibUtils/src/main/java/dev/utils/app/camera1/AutoFocusAssist.java index 3b0aebbf53..b5800518de 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/AutoFocusAssist.java +++ b/DevLibUtils/src/main/java/dev/utils/app/camera1/AutoFocusAssist.java @@ -1,4 +1,4 @@ -package dev.utils.app.assist.camera; +package dev.utils.app.camera1; import android.annotation.SuppressLint; import android.hardware.Camera; @@ -20,12 +20,12 @@ 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_CALLING_AF; static { // 对焦模式 // https://blog.csdn.net/fulinwsuafcie/article/details/49558001 - FOCUS_MODES_CALLING_AF = new ArrayList<>(2); + FOCUS_MODES_CALLING_AF = new ArrayList<>(); FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO); // 自动对焦 FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO); // 微距 } @@ -34,7 +34,7 @@ public final class AutoFocusAssist implements Camera.AutoFocusCallback { // 间隔获取焦点时间 private long interval = 2000L; // 摄像头对象 - private final Camera camera; + private final Camera mCamera; // 判断摄像头是否使用对焦 private final boolean useAutoFocus; // 判断是否停止对焦 @@ -44,19 +44,19 @@ public final class AutoFocusAssist implements Camera.AutoFocusCallback { // 对焦任务 private AsyncTask outstandingTask; // 判断是否需要自动对焦 - private boolean isAutoFocus = true; + private boolean autoFocus = true; // == 构造函数 == - public AutoFocusAssist(Camera camera){ + public AutoFocusAssist(Camera camera) { this(camera, 2000L); } public AutoFocusAssist(Camera camera, long interval) { - this.camera = camera; + this.mCamera = camera; this.interval = interval; // 防止为null - if (camera != null){ + if (camera != null) { // 获取对象对焦模式 String currentFocusMode = camera.getParameters().getFocusMode(); // 判断是否(使用/支持)对焦 @@ -69,12 +69,25 @@ public AutoFocusAssist(Camera camera, long interval) { start(); } + /** + * 设置对焦模式 + * @param collection + */ + public static void setFocusModes(Collection collection) { + // 清空旧的 + FOCUS_MODES_CALLING_AF.clear(); + // 防止为null + if (collection != null) { + FOCUS_MODES_CALLING_AF.addAll(collection); + } + } + /** * 是否允许自动对焦 - * @return + * @return true: 自动对焦, false: 非自动对焦 */ public boolean isAutoFocus() { - return isAutoFocus; + return autoFocus; } /** @@ -82,9 +95,9 @@ public boolean isAutoFocus() { * @param autoFocus */ public void setAutoFocus(boolean autoFocus) { - isAutoFocus = autoFocus; + this.autoFocus = autoFocus; // 判断是否开启自动对焦 - if (isAutoFocus){ + if (autoFocus) { start(); } else { stop(); @@ -92,7 +105,7 @@ public void setAutoFocus(boolean autoFocus) { } /** - * Camera.AutoFocusCallback 重写方法 + * 对焦回调 {@link Camera.AutoFocusCallback} 重写方法 * @param success 是否对焦成功 * @param theCamera 对焦的摄像头 */ @@ -134,7 +147,7 @@ private synchronized void autoFocusAgainLater() { */ public synchronized void start() { // 如果不使用自动对焦, 则不处理 - if (!isAutoFocus){ + if (!autoFocus) { return; } // 支持对焦才处理 @@ -145,7 +158,7 @@ public synchronized void start() { if (!stopped && !focusing) { try { // 设置自动对焦回调 - camera.autoFocus(this); + mCamera.autoFocus(this); // 表示对焦中 focusing = true; } catch (RuntimeException re) { @@ -169,7 +182,7 @@ public synchronized void stop() { cancelOutstandingTask(); try { // 取消对焦 - camera.cancelAutoFocus(); + mCamera.cancelAutoFocus(); } catch (RuntimeException re) { LogPrintUtils.eTag(TAG, re,"stop"); } diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraAssist.java b/DevLibUtils/src/main/java/dev/utils/app/camera1/CameraAssist.java similarity index 90% rename from DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraAssist.java rename to DevLibUtils/src/main/java/dev/utils/app/camera1/CameraAssist.java index 83dfa70e41..fb5743b4dc 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraAssist.java +++ b/DevLibUtils/src/main/java/dev/utils/app/camera1/CameraAssist.java @@ -1,4 +1,4 @@ -package dev.utils.app.assist.camera; +package dev.utils.app.camera1; import android.content.pm.PackageManager; import android.hardware.Camera; @@ -34,7 +34,7 @@ public final class CameraAssist { // 自动获取焦点辅助类 private AutoFocusAssist autoFocusAssist; - public CameraAssist(){ + public CameraAssist() { } public CameraAssist(Camera camera) { @@ -51,6 +51,7 @@ public CameraAssist(Camera camera, long interval) { /** * 打开摄像头程序 * @param holder + * @return 返回自身对象(摄像头辅助类) */ public synchronized CameraAssist openDriver(SurfaceHolder holder) throws IOException { Camera theCamera = mCamera; @@ -97,14 +98,14 @@ public synchronized void startPreview() { // 初始化自动获取焦点 autoFocusAssist = new AutoFocusAssist(mCamera, autoInterval); // 开始预览通知 - if (previewNotify != null){ + if (previewNotify != null) { previewNotify.startPreviewNotify(); } } } /** - * 停止 Cmaera 画面预览 + * 停止 Camera 画面预览 */ public synchronized void stopPreview() { if (autoFocusAssist != null) { @@ -117,7 +118,7 @@ public synchronized void stopPreview() { // 表示非预览中 previewing = false; // 停止预览通知 - if (previewNotify != null){ + if (previewNotify != null) { previewNotify.stopPreviewNotify(); } } @@ -148,10 +149,10 @@ private void freeCameraResource() { /** * 获取相机分辨率 - * @return + * @return Camera 分辨率 */ public Camera.Size getCameraResolution() { - if (mPreviewSize == null){ + if (mPreviewSize == null) { // 获取预览大小 mPreviewSize = cameraSizeAssist.getPreviewSize(); return mPreviewSize; @@ -161,7 +162,7 @@ public Camera.Size getCameraResolution() { /** * 获取预览大小 - * @return + * @return Camera 预览分辨率 */ public Camera.Size getPreviewSize() { if (null != mCamera) { @@ -172,7 +173,7 @@ public Camera.Size getPreviewSize() { /** * 获取 Camera.Size 计算辅助类 - * @return + * @return {@link CameraSizeAssist} */ public CameraSizeAssist getCameraSizeAssist() { return cameraSizeAssist; @@ -180,7 +181,7 @@ public CameraSizeAssist getCameraSizeAssist() { /** * 获取摄像头 - * @return + * @return {@link android.hardware.Camera} */ public Camera getCamera() { return mCamera; @@ -199,6 +200,7 @@ public void setCamera(Camera camera) { /** * 设置预览回调 * @param previewNotify + * @return 返回自身对象(摄像头辅助类) */ public CameraAssist setPreviewNotify(PreviewNotify previewNotify) { this.previewNotify = previewNotify; @@ -208,9 +210,10 @@ public CameraAssist setPreviewNotify(PreviewNotify previewNotify) { /** * 设置是否开启自动对焦 * @param autoFocus + * @return 返回自身对象(摄像头辅助类) */ public CameraAssist setAutoFocus(boolean autoFocus) { - if (autoFocusAssist != null){ + if (autoFocusAssist != null) { autoFocusAssist.setAutoFocus(autoFocus); } return this; @@ -218,7 +221,7 @@ public CameraAssist setAutoFocus(boolean autoFocus) { /** * 是否预览中 - * @return + * @return true: 预览中, false: 非预览 */ public boolean isPreviewing() { return previewing; @@ -266,7 +269,7 @@ public void setFlashlightOff() { /** * 是否打开闪光灯 - * @return + * @return true: 打开, false: 关闭 */ public boolean isFlashlightOn() { if (mCamera == null) { @@ -278,7 +281,7 @@ public boolean isFlashlightOn() { /** * 是否支持手机闪光灯 - * @return + * @return true: 支持, false: 不支持 */ public static boolean isFlashlightEnable() { return DevUtils.getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH); @@ -287,7 +290,7 @@ public static boolean isFlashlightEnable() { // = 接口 = /** - * detail: 预览通知 + * detail: 预览通知接口 * Created by Ttt */ public interface PreviewNotify { diff --git a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraSizeAssist.java b/DevLibUtils/src/main/java/dev/utils/app/camera1/CameraSizeAssist.java similarity index 87% rename from DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraSizeAssist.java rename to DevLibUtils/src/main/java/dev/utils/app/camera1/CameraSizeAssist.java index 1093b6b1b2..7284a313c5 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/assist/camera/CameraSizeAssist.java +++ b/DevLibUtils/src/main/java/dev/utils/app/camera1/CameraSizeAssist.java @@ -1,4 +1,4 @@ -package dev.utils.app.assist.camera; +package dev.utils.app.camera1; import android.graphics.Point; import android.hardware.Camera; @@ -38,7 +38,7 @@ public CameraSizeAssist(Camera camera) { /** * 获取摄像头 - * @return + * @return {@link android.hardware.Camera} */ public Camera getCamera() { return mCamera; @@ -51,44 +51,47 @@ public Camera getCamera() { /** * 设置预览大小 * @param previewSize - * @return + * @return {@link Camera.Parameters} */ - public CameraSizeAssist setPreviewSize(Camera.Size previewSize){ - return setPreviewSize(mCamera, previewSize); + public Camera.Parameters setPreviewSize(Camera.Size previewSize) { + return setPreviewSize(null, previewSize); } /** * 设置预览大小 - * @param camera + * @param parameters * @param previewSize - * @return + * @return {@link Camera.Parameters} */ - public CameraSizeAssist setPreviewSize(Camera camera, Camera.Size previewSize){ - if (camera != null && previewSize != null){ + public Camera.Parameters setPreviewSize(Camera.Parameters parameters, Camera.Size previewSize) { + if (mCamera != null && previewSize != null) { try { + if (parameters == null) { + parameters = mCamera.getParameters(); + } // 设置预览大小 - camera.getParameters().setPreviewSize(previewSize.width, previewSize.height); - } catch (Exception e){ + parameters.setPreviewSize(previewSize.width, previewSize.height); + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setPreviewSize"); } } - return this; + return parameters; } // = /** * 根据手机支持的预览分辨率计算,设置预览尺寸 - * @return + * @return Camera 预览分辨率 */ - public Camera.Size getPreviewSize(){ + public Camera.Size getPreviewSize() { return getPreviewSize(null, -1d); } /** * 根据手机支持的预览分辨率计算,设置预览尺寸 * @param point - * @return + * @return Camera 预览分辨率 */ public Camera.Size getPreviewSize(Point point) { return getPreviewSize(point, -1d); @@ -97,7 +100,7 @@ public Camera.Size getPreviewSize(Point point) { /** * 根据手机支持的预览分辨率计算,设置预览尺寸 * @distortion point - * @return + * @return Camera 预览分辨率 */ public Camera.Size getPreviewSize(double distortion) { return getPreviewSize(null, distortion); @@ -107,17 +110,17 @@ public Camera.Size getPreviewSize(double distortion) { * 根据手机支持的预览分辨率计算,设置预览尺寸(无任何操作, 单独把Camera显示到SurfaceView 预览尺寸) * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) * @param distortion 偏差比例值 - * @return + * @return Camera 预览分辨率 */ public Camera.Size getPreviewSize(Point point, double distortion) { - if (mCamera == null){ + if (mCamera == null) { LogPrintUtils.dTag(TAG, "camera is null"); return null; } try { // 计算大小并返回 return calcPreviewSize(point, distortion); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getPreviewSize"); } return null; @@ -130,56 +133,60 @@ public Camera.Size getPreviewSize(Point point, double distortion) { /** * 设置拍照图片大小 * @param pictureSize - * @return + * @return {@link Camera.Parameters} */ - public CameraSizeAssist setPictureSize(Camera.Size pictureSize){ - return setPictureSize(mCamera, pictureSize); + public Camera.Parameters setPictureSize(Camera.Size pictureSize) { + return setPictureSize(null, pictureSize); } /** * 设置拍照图片大小 - * @param camera + * @param parameters * @param pictureSize - * @return + * @return {@link Camera.Parameters} */ - public CameraSizeAssist setPictureSize(Camera camera, Camera.Size pictureSize){ - if (camera != null && pictureSize != null){ + public Camera.Parameters setPictureSize(Camera.Parameters parameters, Camera.Size pictureSize) { + if (mCamera != null && pictureSize != null) { try { + if (parameters == null) { + parameters = mCamera.getParameters(); + } // 设置预览大小 - camera.getParameters().setPictureSize(pictureSize.width, pictureSize.height); + parameters.setPictureSize(pictureSize.width, pictureSize.height); // // 设置拍照输出格式 -// camera.getParameters().setPictureFormat(PixelFormat.JPEG); +// parameters.setPictureFormat(PixelFormat.JPEG); // // 照片质量 -// camera.getParameters().set("jpeg-quality", 70); - } catch (Exception e){ +// parameters.set("jpeg-quality", 70); + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setPictureSize"); } } - return this; + return parameters; } // = /** * 根据手机支持的拍照分辨率计算 - * @return + * @return Camera 拍照分辨率 */ - public Camera.Size getPictureSize(){ + public Camera.Size getPictureSize() { return getPictureSize(false, null, -1d); } /** * 根据手机支持的拍照分辨率计算 * @param max - * @return + * @return Camera 拍照分辨率 */ - public Camera.Size getPictureSize(boolean max){ + public Camera.Size getPictureSize(boolean max) { return getPictureSize(max, null, -1d); } /** * 根据手机支持的拍照分辨率计算 * @param point + * @return Camera 拍照分辨率 */ public Camera.Size getPictureSize(Point point) { return getPictureSize(false, point, -1d); @@ -188,6 +195,7 @@ public Camera.Size getPictureSize(Point point) { /** * 根据手机支持的拍照分辨率计算 * @param distortion + * @return Camera 拍照分辨率 */ public Camera.Size getPictureSize(double distortion) { return getPictureSize(false, null, distortion); @@ -197,6 +205,7 @@ public Camera.Size getPictureSize(double distortion) { * 根据手机支持的拍照分辨率计算 * @param point * @param distortion + * @return Camera 拍照分辨率 */ public Camera.Size getPictureSize(Point point, double distortion) { return getPictureSize(false, point, distortion); @@ -207,16 +216,17 @@ public Camera.Size getPictureSize(Point point, double distortion) { * @param max 是否使用最大的尺寸 * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) * @param distortion 偏差比例值 + * @return Camera 拍照分辨率 */ public Camera.Size getPictureSize(boolean max, Point point, double distortion) { - if (mCamera == null){ + if (mCamera == null) { LogPrintUtils.dTag(TAG, "camera is null"); return null; } try { // 计算大小并返回 return calcPictureSize(max, point, distortion); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getPictureSize"); } return null; @@ -228,24 +238,25 @@ public Camera.Size getPictureSize(boolean max, Point point, double distortion) { /** * 根据手机支持的视频录制分辨率计算 - * @return + * @return Camera 视频分辨率 */ - public Camera.Size getVideoSize(){ + public Camera.Size getVideoSize() { return getVideoSize(false, null, -1d, false); } /** * 根据手机支持的视频录制分辨率计算 * @param max - * @return + * @return Camera 视频分辨率 */ - public Camera.Size getVideoSize(boolean max){ + public Camera.Size getVideoSize(boolean max) { return getVideoSize(max, null, -1d, false); } /** * 根据手机支持的视频录制分辨率计算 * @param point + * @return Camera 视频分辨率 */ public Camera.Size getVideoSize(Point point) { return getVideoSize(false, point, -1d, false); @@ -254,6 +265,7 @@ public Camera.Size getVideoSize(Point point) { /** * 根据手机支持的视频录制分辨率计算 * @param distortion + * @return Camera 视频分辨率 */ public Camera.Size getVideoSize(double distortion) { return getVideoSize(false, null, distortion, false); @@ -263,6 +275,7 @@ public Camera.Size getVideoSize(double distortion) { * 根据手机支持的视频录制分辨率计算 * @param point * @param distortion + * @return Camera 视频分辨率 */ public Camera.Size getVideoSize(Point point, double distortion) { return getVideoSize(false, point, distortion, false); @@ -274,16 +287,17 @@ public Camera.Size getVideoSize(Point point, double distortion) { * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) * @param distortion 偏差比例值 * @param minAccord 是否存在最小使用最小 + * @return Camera 视频分辨率 */ public Camera.Size getVideoSize(boolean max, Point point, double distortion, boolean minAccord) { - if (mCamera == null){ + if (mCamera == null) { LogPrintUtils.dTag(TAG, "camera is null"); return null; } try { // 计算大小并返回 return calcVideoSize(max, point, distortion, minAccord); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getVideoSize"); } return null; @@ -298,9 +312,10 @@ public Camera.Size getVideoSize(boolean max, Point point, double distortion, boo * 根据对应的尺寸, 计算相应最符合的大小 * @param lists 摄像头尺寸大小(预览、拍照、视频) * @param point point.x = > 宽, point.y => 高 (从指定的宽高, 开始往下(超过的不处理)选择最符合的尺寸) + * @return Camera (预览、拍照、视频) 分辨率 */ private Camera.Size calcSize(List lists, Point point) { - if (lists == null){ + if (lists == null) { return null; } // 判断是否竖屏 @@ -329,33 +344,33 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // 遍历尺寸大小 for (Camera.Size size : lists) { // 判断横竖屏 - if (isPortrait){ // 属于竖屏 => 高度 > 宽度 + if (isPortrait) { // 属于竖屏 => 高度 > 宽度 // 因为是竖屏, 所以判断需要倒着过来 - if (sWidth == size.height && sHeight == size.width){ + if (sWidth == size.height && sHeight == size.width) { // 保存符合比例的大小 mSize = size; break; } // 计算合适的比例 - if (size.width >= sHeight){ + if (size.width >= sHeight) { mSize = size; } } else { // 属于横屏 => 宽度 > 高度 // 因为是横屏, 所以判断需要正常 - if (sWidth == size.width && sHeight == size.height){ + if (sWidth == size.width && sHeight == size.height) { // 保存符合比例的大小 mSize = size; break; } // 计算合适的比例 - if (size.height >= sWidth){ + if (size.height >= sWidth) { mSize = size; } } } // 获取最合适的比例 LogPrintUtils.dTag(TAG, "返回 calcSize -> 宽度: " + mSize.width + ", 高度: " + mSize.height + ", 是否竖屏: " + isPortrait); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "异常 calcSize - 是否竖屏: " + isPortrait); } return mSize; @@ -369,15 +384,16 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { * 根据对应的尺寸, 计算相应最符合的大小 * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) * @param distortion 偏差比例值 + * @return Camera 预览分辨率 * hint: point.x = > 宽, point.y => 高 */ private Camera.Size calcPreviewSize(Point point, double distortion) { // 判断是否为null - if (point == null){ + if (point == null) { point = ScreenUtils.getScreenWidthHeightToPoint(); } // 如果误差为负数, 则使用默认值 - if (distortion < 0){ + if (distortion < 0) { distortion = MAX_ASPECT_DISTORTION; } // 获取 Camera 参数 @@ -385,7 +401,7 @@ private Camera.Size calcPreviewSize(Point point, double distortion) { // 获取手机支持的分辨率集合,并以宽度为基准降序排序 List listPreviewSizes = params.getSupportedPreviewSizes(); // 防止数据为null - if (listPreviewSizes == null){ + if (listPreviewSizes == null) { // 获取默认预览大小 Camera.Size defaultSize = params.getPreviewSize(); return defaultSize; @@ -409,11 +425,11 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { }); // ==== 打印信息 ==== - if (LogPrintUtils.isPrintLog()){ + if (LogPrintUtils.isPrintLog()) { StringBuilder builder = new StringBuilder(); builder.append("预览支持尺寸: \r\n"); // 打印信息 - for (Camera.Size previewSize : listPreviewSizes){ + for (Camera.Size previewSize : listPreviewSizes) { // 例: 1080x1920 builder.append(previewSize.width).append("x").append(previewSize.height).append("\r\n"); } @@ -426,7 +442,7 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // 判断是否竖屏 point.x = > 宽, point.y => 高 boolean isPortrait = point.y > point.x; // 如果是竖屏, 则修改 - if (isPortrait){ + if (isPortrait) { int tempY = point.y; int tempX = point.x; // 进行转换 @@ -495,15 +511,16 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { * @param max 是否使用最大的尺寸 * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) * @param distortion 偏差比例值 + * @return Camera 拍照分辨率 * hint: point.x = > 宽, point.y => 高 */ private Camera.Size calcPictureSize(boolean max, Point point, double distortion) { // 判断是否为null - if (point == null){ + if (point == null) { point = ScreenUtils.getScreenWidthHeightToPoint(); } // 如果误差为负数, 则使用默认值 - if (distortion < 0){ + if (distortion < 0) { distortion = MAX_ASPECT_DISTORTION; } // 获取 Camera 参数 @@ -511,7 +528,7 @@ private Camera.Size calcPictureSize(boolean max, Point point, double distortion) // 获取手机支持的分辨率集合,并以宽度为基准降序排序 List listPictureSizes = params.getSupportedPictureSizes(); // 防止数据为null - if (listPictureSizes == null){ + if (listPictureSizes == null) { // 获取默认拍照大小 Camera.Size defaultSize = params.getPictureSize(); return defaultSize; @@ -550,7 +567,7 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // == // 判断是否拿最大支持的尺寸 - if (max){ + if (max) { if (!listPictureSizes.isEmpty()) { // 获取最大的尺寸 Camera.Size largestPicture = listPictureSizes.get(0); @@ -565,7 +582,7 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // 判断是否竖屏 point.x = > 宽, point.y => 高 boolean isPortrait = point.y > point.x; // 如果是竖屏, 则修改 - if (isPortrait){ + if (isPortrait) { int tempY = point.y; int tempX = point.x; // 进行转换 @@ -615,13 +632,13 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { } // 保存最大相符的尺寸 - if (maxAccordSize == null){ + if (maxAccordSize == null) { maxAccordSize = pictureSize; } } // 如果存在最相符的则返回 - if (maxAccordSize != null){ + if (maxAccordSize != null) { return maxAccordSize; } @@ -648,15 +665,16 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { * @param point 指定的尺寸(为null, 则使用屏幕尺寸) (从指定的宽高, 开始往下(超过的不处理) 选择最接近尺寸) * @param distortion 偏差比例值 * @param minAccord 是否判断存在最小使用最小 + * @return Camera 视频分辨率 * hint: point.x = > 宽, point.y => 高 */ private Camera.Size calcVideoSize(boolean max, Point point, double distortion, boolean minAccord) { // 判断是否为null - if (point == null){ + if (point == null) { point = ScreenUtils.getScreenWidthHeightToPoint(); } // 如果误差为负数, 则使用默认值 - if (distortion < 0){ + if (distortion < 0) { distortion = MAX_ASPECT_DISTORTION; } // 获取 Camera 参数 @@ -664,7 +682,7 @@ private Camera.Size calcVideoSize(boolean max, Point point, double distortion, b // 获取手机支持的分辨率集合,并以宽度为基准降序排序 List listVideoSizes = params.getSupportedVideoSizes(); // 防止数据为null - if (listVideoSizes == null){ + if (listVideoSizes == null) { // 获取默认拍照大小 Camera.Size defaultSize = params.getPreferredPreviewSizeForVideo(); return defaultSize; @@ -703,7 +721,7 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // == // 判断是否拿最大支持的尺寸 - if (max){ + if (max) { if (!listVideoSizes.isEmpty()) { // 获取最大的尺寸 Camera.Size largestVideo = listVideoSizes.get(0); @@ -718,7 +736,7 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // 判断是否竖屏 point.x = > 宽, point.y => 高 boolean isPortrait = point.y > point.x; // 如果是竖屏, 则修改 - if (isPortrait){ + if (isPortrait) { int tempY = point.y; int tempX = point.x; // 进行转换 @@ -770,19 +788,19 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { } // 保存最大相符的尺寸 - if (maxAccordSize == null){ + if (maxAccordSize == null) { maxAccordSize = videoSize; } // 保存最小符合的 minAccordSize = videoSize; } - if (minAccord && minAccordSize != null){ + if (minAccord && minAccordSize != null) { return minAccordSize; } // 如果存在最相符的则返回 - if (maxAccordSize != null){ + if (maxAccordSize != null) { return maxAccordSize; } @@ -803,9 +821,10 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // * 根据手机支持的视频分辨率,设置录制尺寸 // * @param point point.x = > 宽, point.y => 高 (从指定的宽高, 开始往下(超过的不处理)选择最符合的尺寸) // * @param designated 指定的尺寸 - 可多个如 竖屏 => new Point(480, 640), new Point(360, 640), 横屏 => new Point(640, 480), new Point(640, 360) +// * @return Camera 视频分辨率 // */ // public Camera.Size getVideoSize(Point point, Point... designated) { -// if (mCamera == null){ +// if (mCamera == null) { // // 打印支持的尺寸 // LogPrintUtils.dTag(TAG, "camera is null"); // return null; @@ -830,10 +849,10 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // // 打印准备计算的信息 // LogPrintUtils.dTag(TAG, "getVideoSize - sWidth: " + sWidth + ", sHeight: " + sHeight + ", isPortrait: " + isPortrait + ", isDesignated(是否存在指定尺寸): " + isDesignated); // // 打印信息 -// if (isDesignated){ -// for (int i = 0, len = designated.length; i < len; i++){ +// if (isDesignated) { +// for (int i = 0, len = designated.length; i < len; i++) { // Point appoint = designated[i]; -// if (appoint != null){ +// if (appoint != null) { // LogPrintUtils.dTag(TAG, "appoint.x: " + appoint.x + ", appoint.y: " + appoint.y); // } // } @@ -865,7 +884,7 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // ratio = Float.parseFloat(decimalFormat.format(ratio)); // // 遍历预览大小 // for (Camera.Size size : listVideoSizes) { -// if (isBreak){ +// if (isBreak) { // break; // } // // 打印支持的尺寸 @@ -873,25 +892,25 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // // 判断横竖屏 // if (isPortrait) { // 属于竖屏 => 高度 > 宽度 // // 因为是竖屏, 所以判断需要倒着过来 -// if (sWidth == size.height && sHeight == size.width){ +// if (sWidth == size.height && sHeight == size.width) { // // 保存符合比例的大小 // mVideoSize = size; // } // // 处理宽大于高的, 因为是使用竖屏, 参数判断都反着处理 -// if (size.width > size.height){ +// if (size.width > size.height) { // // 获取比例 // float ratioCalc = ((float) size.width / (float) size.height) - 1; // // 转换保留两位小数点 // ratioCalc = Float.parseFloat(decimalFormat.format(ratioCalc)); // // 判断符合规则的 -// if (ratioCalc == ratio){ +// if (ratioCalc == ratio) { // // 保存尺寸 // fixedSize = size; // // 如果存在才处理 -// if (isDesignated){ -// for (int i = 0, len = designated.length; i < len; i++){ +// if (isDesignated) { +// for (int i = 0, len = designated.length; i < len; i++) { // Point appoint = designated[i]; -// if (appoint != null){ +// if (appoint != null) { // // 判断是否支持固定的大小 // if (size.width == appoint.y && size.height == appoint.x) { // isBreak = true; @@ -902,16 +921,16 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // } // } else { // // 最小支持到640 -// if (size.width < 640){ +// if (size.width < 640) { // break; // } // // 保存尺寸 // fixedSize = size; // // 如果存在才处理 -// if (isDesignated){ -// for (int i = 0, len = designated.length; i < len; i++){ +// if (isDesignated) { +// for (int i = 0, len = designated.length; i < len; i++) { // Point appoint = designated[i]; -// if (appoint != null){ +// if (appoint != null) { // // 判断是否支持固定的大小 // if (size.width == appoint.y && size.height == appoint.x) { // isBreak = true; @@ -924,25 +943,25 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // } // } else { // 属于横屏 => 宽度 > 高度 // // 因为是横屏, 所以判断需要正常 -// if (sWidth == size.width && sHeight == size.height){ +// if (sWidth == size.width && sHeight == size.height) { // // 保存符合比例的大小 // mVideoSize = size; // } // // 处理高大于宽的, 因为是使用横屏, 参数判断需要正常 -// if (size.height > size.width){ +// if (size.height > size.width) { // // 获取比例 // float ratioCalc = ((float) size.height / (float) size.width) - 1; // // 转换保留两位小数点 // ratioCalc = Float.parseFloat(decimalFormat.format(ratioCalc)); // // 判断符合规则的 -// if (ratioCalc == ratio){ +// if (ratioCalc == ratio) { // // 保存尺寸 // fixedSize = size; // // 如果存在才处理 -// if (isDesignated){ -// for (int i = 0, len = designated.length; i < len; i++){ +// if (isDesignated) { +// for (int i = 0, len = designated.length; i < len; i++) { // Point appoint = designated[i]; -// if (appoint != null){ +// if (appoint != null) { // // 判断是否支持固定的大小 // if (size.width == appoint.y && size.height == appoint.x) { //// isBreak = true; @@ -953,16 +972,16 @@ public int compare(Camera.Size lhs, Camera.Size rhs) { // } // } else { // // 最小支持到 640 -// if (size.height < 640){ +// if (size.height < 640) { // break; // } // // 保存尺寸 // fixedSize = size; // // 如果存在才处理 -// if (isDesignated){ -// for (int i = 0, len = designated.length; i < len; i++){ +// if (isDesignated) { +// for (int i = 0, len = designated.length; i < len; i++) { // Point appoint = designated[i]; -// if (appoint != null){ +// if (appoint != null) { // // 判断是否支持固定的大小 // if (size.width == appoint.y && size.height == appoint.x) { //// isBreak = true; diff --git a/DevLibUtils/src/main/java/dev/utils/app/CameraUtils.java b/DevLibUtils/src/main/java/dev/utils/app/camera1/CameraUtils.java similarity index 83% rename from DevLibUtils/src/main/java/dev/utils/app/CameraUtils.java rename to DevLibUtils/src/main/java/dev/utils/app/camera1/CameraUtils.java index 0dd1fe628e..74aedfb9da 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/CameraUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/camera1/CameraUtils.java @@ -1,4 +1,4 @@ -package dev.utils.app; +package dev.utils.app.camera1; import android.hardware.Camera; @@ -10,7 +10,7 @@ */ public final class CameraUtils { - private CameraUtils(){ + private CameraUtils() { } // 日志Tag @@ -22,7 +22,7 @@ private CameraUtils(){ * 判断是否支持反转摄像头(是否存在前置摄像头) * @return */ - public static boolean isSupportReverse(){ + public static boolean isSupportReverse() { try { // 默认是不支持 int isSupportReverse = 0; @@ -62,7 +62,7 @@ public static boolean checkCameraFacing(int facing) { return true; } } - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "checkCameraFacing"); } return false; @@ -73,7 +73,7 @@ public static boolean checkCameraFacing(int facing) { * @param facing * @return */ - public static boolean isFrontCamera(int facing){ + public static boolean isFrontCamera(int facing) { return facing == Camera.CameraInfo.CAMERA_FACING_FRONT; } @@ -82,7 +82,7 @@ public static boolean isFrontCamera(int facing){ * @param facing * @return */ - public static boolean isBackCamera(int facing){ + public static boolean isBackCamera(int facing) { return facing == Camera.CameraInfo.CAMERA_FACING_BACK; } @@ -91,7 +91,7 @@ public static boolean isBackCamera(int facing){ * @param isFrontCamera 是否前置摄像头 * @return */ - public static int isUseCameraFacing(boolean isFrontCamera){ + public static int isUseCameraFacing(boolean isFrontCamera) { // 默认使用后置摄像头 int cameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK; try { @@ -102,7 +102,7 @@ public static int isUseCameraFacing(boolean isFrontCamera){ // 判断是否支持后置 cFacingArys[1] = checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_BACK); // 进行判断想要使用的是前置,还是后置 - if (isFrontCamera && cFacingArys[0]){ // 使用前置, 必须也支持前置 + if (isFrontCamera && cFacingArys[0]) { // 使用前置, 必须也支持前置 // 表示使用前置摄像头 cameraFacing = Camera.CameraInfo.CAMERA_FACING_FRONT; } else { @@ -119,49 +119,49 @@ public static int isUseCameraFacing(boolean isFrontCamera){ /** * 释放摄像头资源 - * @param mCamera 摄像头对象 + * @param camera 摄像头对象 */ - public static void freeCameraResource(android.hardware.Camera mCamera) { + public static void freeCameraResource(android.hardware.Camera camera) { try { - if (mCamera != null) { - mCamera.setPreviewCallback(null); - mCamera.stopPreview(); - mCamera.lock(); - mCamera.release(); + if (camera != null) { + camera.setPreviewCallback(null); + camera.stopPreview(); + camera.lock(); + camera.release(); } } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "freeCameraResource"); } finally { - mCamera = null; + camera = null; } } /** * 初始化摄像头 - * @param mCamera + * @param camera * @param isFrontCamera 是否前置摄像头 = true = 前置(屏幕面), false = 后置(手机背面) = 正常默认使用背面 * @return 使用的摄像头 */ - public static Camera initCamera(Camera mCamera, boolean isFrontCamera) { + public static Camera initCamera(Camera camera, boolean isFrontCamera) { // 如果之前存在摄像头数据, 则释放资源 - if (mCamera != null) { - freeCameraResource(mCamera); + if (camera != null) { + freeCameraResource(camera); } try { // 进行判断想要使用的是前置,还是后置 - if (isFrontCamera && checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_FRONT)){ // 使用前置, 必须也支持前置 + if (isFrontCamera && checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_FRONT)) { // 使用前置, 必须也支持前置 // 初始化前置摄像头 - mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT); + camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT); } else { // 初始化后置摄像头 - mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); + camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); } } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "initCamera"); // 释放资源 - freeCameraResource(mCamera); + freeCameraResource(camera); } - return mCamera; + return camera; } /** @@ -169,7 +169,7 @@ public static Camera initCamera(Camera mCamera, boolean isFrontCamera) { * @param cameraId {@link Camera.CameraInfo } CAMERA_FACING_FRONT(前置), CAMERA_FACING_BACK(后置) * @return */ - public static Camera open(int cameraId){ + public static Camera open(int cameraId) { // 判断支持的摄像头数量 int numCameras = Camera.getNumberOfCameras(); if (numCameras == 0) { diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapCropUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/BitmapCropUtils.java index 5482538c1f..a0ab82e533 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapCropUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/image/BitmapCropUtils.java @@ -11,7 +11,7 @@ */ public final class BitmapCropUtils { - private BitmapCropUtils(){ + private BitmapCropUtils() { } // 日志Tag @@ -23,7 +23,7 @@ private BitmapCropUtils(){ * @param fPath 保存路径 * @param bitmap 图片 */ - public static void cropBitmap(String fPath, Bitmap bitmap){ + public static void cropBitmap(String fPath, Bitmap bitmap) { cropBitmap(fPath, bitmap, 16.0f, 9.0f); } @@ -34,10 +34,10 @@ public static void cropBitmap(String fPath, Bitmap bitmap){ * @param wScale 宽度比例 * @param hScale 高度比例 */ - public static void cropBitmap(String fPath, Bitmap bitmap, float wScale, float hScale){ - if(TextUtils.isEmpty(fPath)){ + public static void cropBitmap(String fPath, Bitmap bitmap, float wScale, float hScale) { + if(TextUtils.isEmpty(fPath)) { return; // 防止保存路径为null - } else if (bitmap == null){ + } else if (bitmap == null) { return; // 防止图片为null } // 裁剪处理后的图片 @@ -53,7 +53,7 @@ public static void cropBitmap(String fPath, Bitmap bitmap, float wScale, float h int dHeight = iHeight - rHeight; // -- // 判断裁剪方式 - if (dHeight >= 0){ // 属于宽度 * 对应比例 >= 高度 -> 以高度做偏移 + if (dHeight >= 0) { // 属于宽度 * 对应比例 >= 高度 -> 以高度做偏移 // 计算偏移的y轴 int offsetY = dHeight / 2; // 创建图片 @@ -68,7 +68,7 @@ public static void cropBitmap(String fPath, Bitmap bitmap, float wScale, float h // 创建图片 cBitmap = Bitmap.createBitmap(bitmap, offsetX, 0, rWidth, iHeight, null, false); } - if (cBitmap != null){ + if (cBitmap != null) { // 保存图片 BitmapUtils.saveBitmapToSDCardPNG(cBitmap, fPath, 85); } @@ -77,7 +77,7 @@ public static void cropBitmap(String fPath, Bitmap bitmap, float wScale, float h } finally { // -- 清空资源 -- try { - if(cBitmap != null && !cBitmap.isRecycled()){ + if(cBitmap != null && !cBitmap.isRecycled()) { cBitmap.recycle(); } } catch (Exception e) { diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapExtendUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/BitmapExtendUtils.java index a8891f11ce..f044cca85f 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapExtendUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/image/BitmapExtendUtils.java @@ -43,7 +43,7 @@ private BitmapExtendUtils() { private static final String TAG = BitmapExtendUtils.class.getSimpleName(); /** - * 图片压缩处理(使用Options的方法) + * 图片压缩处理(使用Options的方法) * ================ * 说明 使用方法: * 首先你要将Options的inJustDecodeBounds属性设置为true,BitmapFactory.decode一次图片 。 @@ -135,7 +135,7 @@ public static Bitmap getBitmapFromByteArray(byte[] data, int offset, int length, /** - * Stream转换成Byte + * Stream 转换成 byte[] * @param inputStream InputStream * @return Byte数组 */ @@ -245,7 +245,7 @@ public static Bitmap zoom(Bitmap bitmap, int w, int h) { } /** - * 获得圆角图片的方法 + * 获取圆角图片的方法 * @param bitmap 源Bitmap * @param roundPx 圆角大小 * @return 期望Bitmap @@ -271,7 +271,7 @@ public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) { } /** - * 获得带倒影的图片方法 + * 获取带倒影的图片方法 * @param bitmap 源Bitmap * @return 带倒影的Bitmap */ @@ -413,12 +413,9 @@ public static Bitmap getRoundBitmap(Bitmap bitmap) { } /** - * 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. + * 创建图片缩略图 + * @param bitmap + * @return */ public static Bitmap createThumbnailBitmap(Bitmap bitmap) { int sIconWidth = -1; @@ -496,7 +493,7 @@ public static Bitmap createWatermarkBitmap(Bitmap src, Bitmap watermark) { // draw watermark into cv.drawBitmap(watermark, w - ww + 5, h - wh + 5, null);// 在src的右下角画入水印 // save all clip - cv.save(Canvas.ALL_SAVE_FLAG);// 保存 + cv.save();// 保存 // store cv.restore();// 存储 return newb; @@ -505,7 +502,7 @@ public static Bitmap createWatermarkBitmap(Bitmap src, Bitmap watermark) { /** * 重新编码Bitmap * @param src 需要重新编码的Bitmap - * @param format 编码后的格式(目前只支持png和jpeg这两种格式) + * @param format 编码后的格式(目前只支持png和jpeg这两种格式) * @param quality 重新生成后的bitmap的质量 * @return 返回重新生成后的bitmap */ @@ -517,13 +514,13 @@ public static Bitmap codec(Bitmap src, Bitmap.CompressFormat format, int quality } /** - * 图片压缩方法:(使用compress的方法) + * 图片压缩方法:(使用compress的方法) * 说明 如果bitmap本身的大小小于maxSize,则不作处理 * @param bitmap 要压缩的图片 * @param maxSize 压缩后的大小,单位kb */ public static void compress(Bitmap bitmap, double maxSize) { - // 将bitmap放至数组中,意在获得bitmap的大小(与实际读取的原文件要大) + // 将bitmap放至数组中,意在获取bitmap的大小(与实际读取的原文件要大) ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 格式、质量、输出流 bitmap.compress(Bitmap.CompressFormat.PNG, 70, baos); @@ -535,7 +532,7 @@ public static void compress(Bitmap bitmap, double maxSize) { // 判断bitmap占用空间是否大于允许最大空间 如果大于则压缩 小于则不压缩 if (i > 1) { // 缩放图片 此处用到平方根 将宽带和高度压缩掉对应的平方根倍 - // (保持宽高不变,缩放后也达到了最大占用空间的大小) + // (保持宽高不变,缩放后也达到了最大占用空间的大小) bitmap = scale(bitmap, bitmap.getWidth() / Math.sqrt(i),bitmap.getHeight() / Math.sqrt(i)); } } @@ -1180,7 +1177,7 @@ public static Bitmap emboss(Bitmap bitmap) { * @param height 源图片的高 * @return */ - public static final byte[] yuvLandscapeToPortrait(byte[] sourceData, int width, int height) { + public static 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++) diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/BitmapUtils.java index 9cd1069885..954f71c084 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/image/BitmapUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/image/BitmapUtils.java @@ -1,14 +1,20 @@ 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.PorterDuff; import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; 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.util.DisplayMetrics; import android.view.View; import android.view.ViewGroup.LayoutParams; @@ -38,6 +44,98 @@ private BitmapUtils() { // 日志Tag private static final String TAG = BitmapUtils.class.getSimpleName(); + /** + * 将10进制颜色(Int)转换为Drawable对象 + * @param color + * @return + */ + public static Drawable intToDrawable(int color) { + try { + return new ColorDrawable(color); + } catch (Exception e){ + LogPrintUtils.eTag(TAG, e, "intToDrawable"); + } + return null; + } + + /** + * 将16进制颜色(String)转化为Drawable对象 + * @param color + * @return + */ + public static Drawable stringToDrawable(String color) { + try { + return new ColorDrawable(Color.parseColor(color)); + } catch (Exception e){ + LogPrintUtils.eTag(TAG, e, "stringToDrawable"); + } + return null; + } + + /** + * 图片着色 + * @param drawable + * @param tintColor + * @return + */ + public static Drawable tintIcon(@NonNull Drawable drawable, @ColorInt 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 + * @param tintColor + * @param id + * @return + */ + public static Drawable tint9PatchDrawableFrame(Context context, @ColorInt int tintColor, @DrawableRes int id) { + try { + final NinePatchDrawable toastDrawable = (NinePatchDrawable) getDrawable(context, id); + return tintIcon(toastDrawable, tintColor); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "tint9PatchDrawableFrame"); + } + return null; + } + + /** + * 设置背景 + * @param view + * @param drawable + */ + public static void setBackground(@NonNull View view, 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 + * @param id + * @return + */ + public static Drawable getDrawable(Context context, @DrawableRes 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); + } + /** * 通过Resources获取Bitmap * @param context @@ -45,8 +143,7 @@ private BitmapUtils() { * @return */ public static Bitmap getBitmapFromResources(Context context, int resId) { - Resources res = context.getResources(); - return BitmapFactory.decodeResource(res, resId); + return BitmapFactory.decodeResource(context.getResources(), resId); } /** @@ -56,8 +153,7 @@ public static Bitmap getBitmapFromResources(Context context, int resId) { * @return */ public static Drawable getDrawableFromResources(Context context, int resId) { - Resources res = context.getResources(); - return res.getDrawable(resId); + return context.getResources().getDrawable(resId); } /** @@ -242,6 +338,29 @@ public static Bitmap drawable2Bitmap(final Drawable drawable) { return bitmap; } + /** + * Drawable 转换 Bitmap + * @param drawable The drawable. + * @return bitmap + */ + public static Bitmap drawable3Bitmap(final Drawable drawable) { + if (drawable == null) { + return null; + } + 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; + } + // === /** @@ -330,7 +449,7 @@ public static Bitmap getBitmapFromDrawable(Drawable drawable) { drawable.setBounds(0, 0, width, height); drawable.draw(canvas); return bitmap; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getBitmapFromDrawable"); } return null; @@ -354,7 +473,7 @@ public static Bitmap bitmapToViewBackGround(View view) { } view.draw(canvas); return ret; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "bitmapToViewBackGround"); } return null; @@ -373,7 +492,7 @@ public static Bitmap getBitmapFromView(View view) { view.layout(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); view.draw(canvas); return bitmap; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getBitmapFromView"); } return null; @@ -408,7 +527,7 @@ public static Bitmap getBitmapFromView2(View view) { view.setWillNotCacheDrawing(willNotCache); view.setDrawingCacheBackgroundColor(color); return bitmap; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getBitmapFromView2"); } return null; @@ -528,7 +647,7 @@ private static int getImageViewFieldValue(Object object, String fieldName) { * @param path * @return */ - public static int[] getImageWidthHeight(String path){ + public static int[] getImageWidthHeight(String path) { BitmapFactory.Options options = new BitmapFactory.Options(); // 不解析图片信息 options.inJustDecodeBounds = true; diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/FastBlurUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/FastBlurUtils.java index 3925a69905..1b8885377f 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/image/FastBlurUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/image/FastBlurUtils.java @@ -19,8 +19,19 @@ private FastBlurUtils() { * @param canReuseInBitmap 是否重用 * @return 位图 */ - public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { + public static Bitmap blur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { + if (sentBitmap == null) { + return null; + } Bitmap bitmap; + // 如果修改原图 + if (canReuseInBitmap) { + // 判断 Bitmap 是否可变 + if (!sentBitmap.isMutable()) { // 不可变则修改 + canReuseInBitmap = false; + } + } + if (canReuseInBitmap) { bitmap = sentBitmap; } else { @@ -220,39 +231,43 @@ public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBit } } - bitmap.setPixels(pix, 0, w, 0, 0, w, h); + try { + bitmap.setPixels(pix, 0, w, 0, 0, w, h); + } catch (Exception e) { + return null; + } return (bitmap); } /** - * 对图片进行毛玻璃化 数值越大效果越明显 + * 对图片进行毛玻璃化 数值越大效果越明显 * @param originBitmap 位图 * @param scaleRatio 缩放比率 * @param blurRadius 毛玻璃化比率,虚化程度 * @return 位图 */ - public static Bitmap doBlur(Bitmap originBitmap, int scaleRatio, int blurRadius){ + public static Bitmap blur(Bitmap originBitmap, int scaleRatio, int blurRadius) { Bitmap scaledBitmap = Bitmap.createScaledBitmap(originBitmap, originBitmap.getWidth() / scaleRatio, originBitmap.getHeight() / scaleRatio, false); - Bitmap blurBitmap = doBlur(scaledBitmap, blurRadius, false); + Bitmap blurBitmap = blur(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){ + public static Bitmap blur(Bitmap originBitmap, int width, int height, int blurRadius) { Bitmap thumbnail = ThumbnailUtils.extractThumbnail(originBitmap, width, height); - Bitmap blurBitmap = doBlur(thumbnail, blurRadius, true); + Bitmap blurBitmap = blur(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 index f499c3effb..83cb5fc2f6 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/image/ImageBmpUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/image/ImageBmpUtils.java @@ -13,7 +13,7 @@ */ public final class ImageBmpUtils { - private ImageBmpUtils(){ + private ImageBmpUtils() { } // 日志Tag @@ -141,9 +141,9 @@ private static byte[] addBMP_ARGB_8888(int[] b, int w, int h) { * @param bitmap * @return */ - private static byte[] getBmpBytes(Bitmap bitmap){ + private static byte[] getBmpBytes(Bitmap bitmap) { try { - if(bitmap != null){ + 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); @@ -172,9 +172,9 @@ private static byte[] getBmpBytes(Bitmap bitmap){ * 保存Bmp图片 * @param path 保存路径 * @param bitmap 图片信息 - * @return + * @return true: 保存成功, false: 保存失败 */ - public static boolean saveBmpImg(String path, Bitmap bitmap){ + public static boolean saveBmpImg(String path, Bitmap bitmap) { FileOutputStream fos = null; try { // 转换Bmp byte数据 @@ -188,13 +188,13 @@ public static boolean saveBmpImg(String path, Bitmap bitmap){ LogPrintUtils.eTag(TAG, e, "saveBmpImg"); } finally { // 最终关闭写入流 try { - if(fos != null){ + if(fos != null) { fos.flush(); } } catch (Exception e) { } try { - if(fos != null){ + if(fos != null) { fos.close(); } } catch (Exception e) { diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/ImageProcessor.java b/DevLibUtils/src/main/java/dev/utils/app/image/ImageProcessor.java index 4f747e46df..92112a8d7b 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/image/ImageProcessor.java +++ b/DevLibUtils/src/main/java/dev/utils/app/image/ImageProcessor.java @@ -69,7 +69,7 @@ public Bitmap scaleByHeight(int newHeight) { * @param bitmap 原图 * @return 水平翻转后的图片 */ - public Bitmap reverseByHorizontal(Bitmap bitmap){ + 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); @@ -80,7 +80,7 @@ public Bitmap reverseByHorizontal(Bitmap bitmap){ * @param bitmap 原图 * @return 垂直翻转后的图片 */ - public Bitmap reverseByVertical(Bitmap bitmap){ + 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); @@ -88,7 +88,7 @@ public Bitmap reverseByVertical(Bitmap bitmap){ /** * 将给定资源ID的Drawable转换成Bitmap - * @param context 上下文 + * @param context * @param resId Drawable资源文件的ID * @return 新的Bitmap */ @@ -164,7 +164,7 @@ public Bitmap reflection() { * @param py 旋转中心点在Y轴的坐标 * @return 旋转后的图片 */ - public Bitmap rotate(float angle, float px, float py){ + 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); @@ -175,7 +175,7 @@ public Bitmap rotate(float angle, float px, float py){ * @param angle 旋转角度 * @return 旋转后的图片 */ - public Bitmap rotate(float angle){ + public Bitmap rotate(float angle) { Matrix matrix = new Matrix(); matrix.postRotate(angle); return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); @@ -186,7 +186,7 @@ public Bitmap rotate(float angle){ * @param saturationValue 新的饱和度值 * @return 改变了饱和度值之后的图片 */ - public Bitmap saturation(int saturationValue){ + public Bitmap saturation(int saturationValue) { //计算出符合要求的饱和度值 float newSaturationValue = saturationValue * 1.0F / 127; //创建一个颜色矩阵 @@ -209,7 +209,7 @@ public Bitmap saturation(int saturationValue){ * @param lumValue 新的亮度值 * @return 改变了亮度值之后的图片 */ - public Bitmap lum(int lumValue){ + public Bitmap lum(int lumValue) { //计算出符合要求的亮度值 float newlumValue = lumValue * 1.0F / 127; //创建一个颜色矩阵 @@ -232,7 +232,7 @@ public Bitmap lum(int lumValue){ * @param hueValue 新的色相值 * @return 改变了色相值之后的图片 */ - public Bitmap hue(int hueValue){ + public Bitmap hue(int hueValue) { //计算出符合要求的色相值 float newHueValue = (hueValue - 127) * 1.0F / 127 * 180; //创建一个颜色矩阵 @@ -261,7 +261,7 @@ public Bitmap hue(int hueValue){ * @param saturationValue 饱和度值 * @return 亮度、色相、饱和度处理后的图片 */ - public Bitmap lumAndHueAndSaturation(int lumValue, int hueValue, int saturationValue){ + public Bitmap lumAndHueAndSaturation(int lumValue, int hueValue, int saturationValue) { //计算出符合要求的饱和度值 float newSaturationValue = saturationValue * 1.0F / 127; //计算出符合要求的亮度值 diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/ImageUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/ImageUtils.java index b4afb59e0b..838e506d39 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/image/ImageUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/image/ImageUtils.java @@ -1,6 +1,5 @@ package dev.utils.app.image; -import android.annotation.TargetApi; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; @@ -30,6 +29,7 @@ import android.support.annotation.DrawableRes; import android.support.annotation.FloatRange; import android.support.annotation.IntRange; +import android.support.annotation.RequiresApi; import android.support.v4.content.ContextCompat; import java.io.BufferedOutputStream; @@ -203,7 +203,7 @@ public static Bitmap scale(final Bitmap src, final int newWidth, final int newHe 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(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -231,7 +231,7 @@ public static Bitmap scale(final Bitmap src, final float scaleWidth, final float 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(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -261,7 +261,7 @@ public static Bitmap clip(final Bitmap src, final int x, final int y, final int 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(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -316,7 +316,7 @@ public static Bitmap skew(final Bitmap src, final float kx, final float ky, fina 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(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -347,7 +347,7 @@ public static Bitmap rotate(final Bitmap src, final int degrees, final float px, 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(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -426,7 +426,9 @@ public static Bitmap toRound(final Bitmap src, @IntRange(from = 0) int borderSiz 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); + if (width != height) { + 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); @@ -440,7 +442,7 @@ public static Bitmap toRound(final Bitmap src, @IntRange(from = 0) int borderSiz float radius = center - borderSize / 2f; canvas.drawCircle(width / 2f, height / 2f, radius, paint); } - if (recycle && !src.isRecycled()) src.recycle(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -507,7 +509,7 @@ public static Bitmap toRoundCorner(final Bitmap src, final float radius, @IntRan paint.setStrokeCap(Paint.Cap.ROUND); canvas.drawRoundRect(rectF, radius, radius, paint); } - if (recycle && !src.isRecycled()) src.recycle(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -627,7 +629,7 @@ public static Bitmap addReflection(final Bitmap src, final int reflectionHeight, 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(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -668,7 +670,7 @@ public static Bitmap addTextWatermark(final Bitmap src, final String content, fi Rect bounds = new Rect(); paint.getTextBounds(content, 0, content.length(), bounds); canvas.drawText(content, x, y + textSize, paint); - if (recycle && !src.isRecycled()) src.recycle(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -704,7 +706,7 @@ public static Bitmap addImageWatermark(final Bitmap src, final Bitmap watermark, paint.setAlpha(alpha); canvas.drawBitmap(watermark, x, y, paint); } - if (recycle && !src.isRecycled()) src.recycle(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -726,7 +728,7 @@ public static Bitmap toAlpha(final Bitmap src) { 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(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -755,7 +757,7 @@ public static Bitmap toGray(final Bitmap src, final boolean recycle) { ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix); paint.setColorFilter(colorMatrixColorFilter); canvas.drawBitmap(src, 0, 0, paint); - if (recycle && !src.isRecycled()) src.recycle(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -801,12 +803,12 @@ public static Bitmap fastBlur(final Bitmap src, scaleBitmap = stackBlur(scaleBitmap, (int) radius, recycle); } if (scale == 1) { - if (recycle && !src.isRecycled()) src.recycle(); + if (recycle && !src.isRecycled() && scaleBitmap != src) src.recycle(); return scaleBitmap; } Bitmap ret = Bitmap.createScaledBitmap(scaleBitmap, width, height, true); if (!scaleBitmap.isRecycled()) scaleBitmap.recycle(); - if (recycle && !src.isRecycled()) src.recycle(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); return ret; } @@ -816,7 +818,7 @@ public static Bitmap fastBlur(final Bitmap src, * @param radius 模糊半径(0...25) * @return 模糊后的图片 */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + @RequiresApi(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); } @@ -828,7 +830,7 @@ public static Bitmap renderScriptBlur(final Bitmap src, @FloatRange(from = 0, to * @param recycle 是否回收 * @return 模糊后的图片 */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + @RequiresApi(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); @@ -1467,7 +1469,7 @@ private static int calculateInSampleSize(final BitmapFactory.Options options, fi * @param filePath * @return */ - private static File getFileByPath(final String filePath){ + private static File getFileByPath(final String filePath) { return filePath != null ? new File(filePath) : null; } diff --git a/DevLibUtils/src/main/java/dev/utils/app/image/RSBlurUtils.java b/DevLibUtils/src/main/java/dev/utils/app/image/RSBlurUtils.java new file mode 100644 index 0000000000..02ea1af34c --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/image/RSBlurUtils.java @@ -0,0 +1,54 @@ +package dev.utils.app.image; + +import android.graphics.Bitmap; +import android.os.Build; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RSRuntimeException; +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: Android 自己的 RenderScript 实现图片模糊 + * Created by Ttt + * 注:仅在 SDK >= 17 时有用 + */ +public final class RSBlurUtils { + + private RSBlurUtils() { + } + + // 日志TAG + private static final String TAG = RSBlurUtils.class.getSimpleName(); + + /** + * RenderScript 实现图片模糊 + * @param bitmap 待模糊图片 + * @param radius 模糊度(0-25) + * @return + * @throws RSRuntimeException + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public static Bitmap blur(Bitmap bitmap, @IntRange(from = 0, to = 25) int radius) throws RSRuntimeException { + try { + RenderScript rs = RenderScript.create(DevUtils.getContext()); + Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + Allocation output = Allocation.createTyped(rs, input.getType()); + ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + blur.setInput(input); + blur.setRadius(radius); + blur.forEach(output); + output.copyTo(bitmap); + rs.destroy(); + return bitmap; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "blur"); + } + return null; + } +} diff --git a/DevLibUtils/src/main/java/dev/utils/app/info/ApkInfoItem.java b/DevLibUtils/src/main/java/dev/utils/app/info/ApkInfoItem.java index 5707bbd116..1141127e04 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/info/ApkInfoItem.java +++ b/DevLibUtils/src/main/java/dev/utils/app/info/ApkInfoItem.java @@ -1,19 +1,20 @@ 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.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; @@ -30,184 +31,335 @@ public final class ApkInfoItem { // 日志Tag private static final String TAG = ApkInfoItem.class.getSimpleName(); - // apk文件地址 - private String apkUri; - // App 信息实体类 + @Keep// App 基本信息实体类 private AppInfoBean appInfoBean; - // App 参数集 - private ArrayList listKeyValues = new ArrayList<>(); + @Keep // App MD5 签名 + private String appMD5; + @Keep // App SHA1 签名 + private String appSHA1; + @Keep // App SHA256 签名 + private String appSHA256; + @Keep // App 最低支持版本 + 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 // 证书是否过期 true = 过期,false = 未过期 + 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<>(); - 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; + /** + * 初始化 ApkInfoItem 对象 + * @param pInfo + * @return + */ + protected static ApkInfoItem obtain(PackageInfo pInfo) { + try { + return new ApkInfoItem(pInfo); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "obtain"); } + return null; + } - // https://blog.csdn.net/sljjyy/article/details/17370665 - - // 获取上下文 + /** + * 初始化 AppInfoItem 对象 + * @param pInfo + */ + private ApkInfoItem(PackageInfo pInfo) { + // 获取 Context 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"); + // =========== + // 获取 App 信息 + appInfoBean = new AppInfoBean(pInfo); // 获取签名信息 - Signature[] signatures = SignaturesUtils.getSignaturesFromApk(apkFile); + Signature[] signatures = SignaturesUtils.getSignaturesFromApk(new File(appInfoBean.getSourceDir())); // =========== - // 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; + // App 签名MD5 + appMD5 = SignaturesUtils.signatureMD5(signatures); + // App SHA1 + appSHA1 = SignaturesUtils.signatureSHA1(signatures); + // App SHA256 + appSHA256 = SignaturesUtils.signatureSHA256(signatures); // 属于7.0以上才有的方法 - if (AppCommonUtils.isN()){ + if (AppCommonUtils.isN()) { + // App 最低支持版本 minSdkVersion = pInfo.applicationInfo.minSdkVersion; } - // app 兼容sdk版本 - int targetSdkVersion = pInfo.applicationInfo.targetSdkVersion; - // 获取 app 安装包大小 - String apkLength = Formatter.formatFileSize(DevUtils.getContext(), FileUtils.getFileLength(apkFile)); + // App 兼容sdk版本 + targetSdkVersion = pInfo.applicationInfo.targetSdkVersion; + // App 安装包大小 + apkLength = Formatter.formatFileSize(DevUtils.getContext(), FileUtils.getFileLength(appInfoBean.getSourceDir())); // = 临时数据存储 = // 是否保存 boolean isError = false; // 临时签名信息 - ArrayList listTemps = new ArrayList<>(); + List listTemps = new ArrayList<>(); - // Android的APK应用签名机制以及读取签名的方法 - // http://www.jb51.net/article/79894.htm try { - // 获取证书对象 - X509Certificate cert = SignaturesUtils.getX509Certificate(signatures); + // 证书对象 + cert = SignaturesUtils.getX509Certificate(signatures); + // 证书生成日期 + notBefore = cert.getNotBefore(); + // 证书有效期 + notAfter = cert.getNotAfter(); // 设置有效期 StringBuilder sbEffective = new StringBuilder(); - sbEffective.append(dFormat.format(cert.getNotBefore())); + sbEffective.append(dFormat.format(notBefore)); sbEffective.append(" " + context.getString(R.string.dev_str_to) + " "); // 至 - sbEffective.append(dFormat.format(cert.getNotAfter())); + sbEffective.append(dFormat.format(notAfter)); sbEffective.append("\n\n"); - sbEffective.append(cert.getNotBefore()); + sbEffective.append(notBefore); sbEffective.append(" " + context.getString(R.string.dev_str_to) + " "); - sbEffective.append(cert.getNotAfter()); - // 获取有效期 - String effective = sbEffective.toString(); + sbEffective.append(notAfter); + // 保存有效期转换信息 + String effectiveStr = sbEffective.toString(); // 证书是否过期 true = 过期,false = 未过期 - boolean isEffective = false; + effective = false; try { cert.checkValidity(); // CertificateExpiredException - 如果证书已过期。 // CertificateNotYetValidException - 如果证书不再有效。 } catch (CertificateExpiredException ce) { - isEffective = true; + effective = true; } catch (CertificateNotYetValidException ce) { - isEffective = true; + effective = true; } - // 判断是否过期 - String isEffectiveState = isEffective ? context.getString(R.string.dev_str_overdue) : context.getString(R.string.dev_str_notoverdue); // 证书发布方 - String principal = cert.getIssuerX500Principal().toString(); + certPrincipal = cert.getIssuerX500Principal().toString(); // 证书版本号 - String version = cert.getVersion() + ""; + certVersion = cert.getVersion() + ""; // 证书算法名称 - String sigalgname = cert.getSigAlgName(); + certSigalgname = cert.getSigAlgName(); // 证书算法OID - String sigalgoid = cert.getSigAlgOID(); + certSigalgoid = cert.getSigAlgOID(); // 证书机器码 - String serialnumber = cert.getSerialNumber().toString(); - // 证书 DER编码 - String dercode = SignaturesUtils.toHexString(cert.getTBSCertificate()); - // 获取有效期 - listTemps.add(KeyValueBean.get(R.string.dev_str_effective, effective)); + certSerialnumber = cert.getSerialNumber().toString(); + try { + // 证书 DER 编码 + certDercode = SignaturesUtils.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, isEffectiveState)); + 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, principal)); + listTemps.add(KeyValueBean.get(R.string.dev_str_principal, certPrincipal)); // 证书版本号 - listTemps.add(KeyValueBean.get(R.string.dev_str_version, version)); + listTemps.add(KeyValueBean.get(R.string.dev_str_version, certVersion)); // 证书算法名称 - listTemps.add(KeyValueBean.get(R.string.dev_str_sigalgname, sigalgname)); + listTemps.add(KeyValueBean.get(R.string.dev_str_sigalgname, certSigalgname)); // 证书算法OID - listTemps.add(KeyValueBean.get(R.string.dev_str_sigalgoid, sigalgoid)); + listTemps.add(KeyValueBean.get(R.string.dev_str_sigalgoid, certSigalgoid)); // 证书机器码 - 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"); + 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 包名 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_packname, appInfoItem.appInfoBean.getAppPackName())); + // App 包名 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_packname, appInfoBean.getAppPackName())); // 没报错才存储 MD5 信息 if (!isError) { - // app 签名MD5 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_md5, md5)); + // App 签名 MD5 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_md5, appMD5)); } - // 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 版本号 - 主要用于 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())); // 安装包地址 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_apk_uri, apkUri)); + listKeyValues.add(KeyValueBean.get(R.string.dev_str_apk_uri, appInfoBean.getSourceDir())); // 没报错才存储 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 SHA1 + listKeyValues.add(KeyValueBean.get(R.string.dev_str_sha1, appSHA1)); + // App SHA256. + listKeyValues.add(KeyValueBean.get(R.string.dev_str_sha256, appSHA256)); } - // 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)); + // App 最低支持版本 + 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) { - appInfoItem.listKeyValues.addAll(listTemps); + listKeyValues.addAll(listTemps); } - // 返回实体类 - return appInfoItem; - } - - public String getApkUri() { - return apkUri; } + /** + * 获取 AppInfoBean + * @return {@link AppInfoBean } + */ public AppInfoBean getAppInfoBean() { return appInfoBean; } - public ArrayList getListKeyValues() { + /** + * 获取 List + * @return App 信息键对值集合 + */ + public List getListKeyValues() { return listKeyValues; } + + /** + * 获取 App MD5 签名 + * @return + */ + public String getAppMD5() { + return appMD5; + } + + /** + * 获取 App SHA1 签名 + * @return + */ + public String getAppSHA1() { + return appSHA1; + } + + /** + * 获取 App SHA256 签名 + * @return + */ + public String getAppSHA256() { + return appSHA256; + } + + /** + * 获取 App 最低支持版本 + * @return + */ + public int getMinSdkVersion() { + return minSdkVersion; + } + + /** + * 获取 App 兼容sdk版本 + * @return + */ + public int getTargetSdkVersion() { + return targetSdkVersion; + } + + /** + * 获取 App 安装包大小 + * @return + */ + public String getApkLength() { + return apkLength; + } + + /** + * 获取证书对象 + * @return + */ + public X509Certificate getX509Certificate() { + return cert; + } + + /** + * 获取证书生成日期 + * @return + */ + public Date getNotBefore() { + return notBefore; + } + + /** + * 获取证书有效期 + * @return + */ + public Date getNotAfter() { + return notAfter; + } + + /** + * 获取证书是否过期 true = 过期,false = 未过期 + * @return + */ + 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 + */ + public String getCertSigalgoid() { + return certSigalgoid; + } + + /** + * 获取证书机器码 + * @return + */ + public String getCertSerialnumber() { + return certSerialnumber; + } + + /** + * 获取证书 DER编码 + * @return + */ + public String getCertDercode() { + return certDercode; + } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoBean.java b/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoBean.java index a520efaa7f..a9ff773fb5 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoBean.java +++ b/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoBean.java @@ -4,98 +4,61 @@ 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 信息实体类 + * 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 包名 + @Keep // App 包名 private String appPackName; - // app 名 + @Keep // App 名 private String appName; - // app 图标 + @Keep // App 图标 private transient Drawable appIcon; - // App 类型 + @Keep // App 类型 private AppType appType; - // 获取版本号 + @Keep // App 版本号 private int versionCode; - // 获取版本名 + @Keep // App 版本名 private String versionName; - // app 首次安装时间 + @Keep // App 首次安装时间 private long firstInstallTime; - // 获取最后一次更新时间 + @Keep // App 最后一次更新时间 private long lastUpdateTime; - // 获取 app 地址 + @Keep // App 地址 private String sourceDir; - // APK 大小 + @Keep // 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 包名 + * 获取 AppInfoBean + * @param pInfo + * @return */ - public static AppInfoBean obtainPck(String pckName){ + protected static AppInfoBean obtain(PackageInfo pInfo) { 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 new AppInfoBean(pInfo); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "obtain"); } return null; } /** - * 初始化当前 App 信息实体类 + * 初始化 App 信息实体类 + * @param pInfo */ - 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; + protected AppInfoBean(PackageInfo pInfo) { + this(pInfo, DevUtils.getContext().getPackageManager()); } /** @@ -103,37 +66,31 @@ public static AppInfoBean obtain(){ * @param pInfo * @param pManager */ - public AppInfoBean(PackageInfo pInfo, PackageManager pManager){ - // app 包名 + protected AppInfoBean(PackageInfo pInfo, PackageManager pManager) { + // App 包名 appPackName = pInfo.applicationInfo.packageName; - // app 名 + // App 名 appName = pManager.getApplicationLabel(pInfo.applicationInfo).toString(); - // app 图标 + // App 图标 appIcon = pManager.getApplicationIcon(pInfo.applicationInfo); - // 获取App 类型 + // App 类型 appType = AppInfoBean.getAppType(pInfo); - // 获取版本号 + // App 版本号 versionCode = pInfo.versionCode; - // 获取版本名 + // App 版本名 versionName = pInfo.versionName; - // app 首次安装时间 + // App 首次安装时间 firstInstallTime = pInfo.firstInstallTime; - // 获取最后一次更新时间 + // App 最后一次更新时间 lastUpdateTime = pInfo.lastUpdateTime; - // 获取 app 地址 + // App 地址 sourceDir = pInfo.applicationInfo.sourceDir; - // 获取 APK 大小 + // Apk 大小 apkSize = FileUtils.getFileLength(sourceDir); - try { - // 获取权限 - apkPermissionsArys = pInfo.requestedPermissions; - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "AppInfoBean"); - } } /** - * 获取App 包名 + * 获取 App 包名 * @return */ public String getAppPackName() { @@ -141,7 +98,7 @@ public String getAppPackName() { } /** - * 获取App 名 + * 获取 App 名 * @return */ public String getAppName() { @@ -149,7 +106,7 @@ public String getAppName() { } /** - * 获取App 图标 + * 获取 App 图标 * @return */ public Drawable getAppIcon() { @@ -164,6 +121,54 @@ public AppType getAppType() { return appType; } + /** + * 获取 versionCode + * @return + */ + public int getVersionCode() { + return versionCode; + } + + /** + * 获取 versionName + * @return + */ + public String getVersionName() { + return versionName; + } + + /** + * 获取 App 首次安装时间 + * @return + */ + public long getFirstInstallTime() { + return firstInstallTime; + } + + /** + * 获取 App 最后更新时间 + * @return + */ + public long getLastUpdateTime() { + return lastUpdateTime; + } + + /** + * 获取 Apk 地址 + * @return + */ + public String getSourceDir() { + return sourceDir; + } + + /** + * 获取 Apk 大小 + * @return + */ + public long getApkSize() { + return apkSize; + } + // = /** App 类型 */ @@ -177,19 +182,19 @@ public enum AppType { } /** - * 获取App 类型 + * 获取 App 类型 * @param pInfo * @return */ - public static AppType getAppType(PackageInfo pInfo){ - if (!isSystemApp(pInfo) && !isSystemUpdateApp(pInfo)){ + public static AppType getAppType(PackageInfo pInfo) { + if (!isSystemApp(pInfo) && !isSystemUpdateApp(pInfo)) { return AppType.USER; } return AppType.SYSTEM; } /** - * 表示系统程序 + * 是否系统程序 * @param pInfo * @return */ @@ -198,39 +203,11 @@ public static boolean isSystemApp(PackageInfo pInfo) { } /** - * 表示系统程序被手动更新后,也成为第三方应用程序 + * 是否系统程序被手动更新后,也成为第三方应用程序 * @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 index 1c9b566b21..0250dd13be 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoItem.java +++ b/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoItem.java @@ -2,17 +2,20 @@ import android.content.Context; import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.text.TextUtils; +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; @@ -24,143 +27,314 @@ */ public final class AppInfoItem { - // App 信息实体类 + // 日志Tag + private static final String TAG = AppInfoItem.class.getSimpleName(); + @Keep// App 基本信息实体类 private AppInfoBean appInfoBean; - // App 参数集 - private ArrayList listKeyValues = new ArrayList<>(); + @Keep // App MD5 签名 + private String appMD5; + @Keep // App SHA1 签名 + private String appSHA1; + @Keep // App SHA256 签名 + private String appSHA256; + @Keep // App 最低支持版本 + 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 // 证书是否过期 true = 过期,false = 未过期 + 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<>(); - private AppInfoItem(){ + /** + * 初始化 AppInfoItem 对象 + * @param pInfo + * @return + */ + protected static AppInfoItem obtain(PackageInfo pInfo) { + try { + return new AppInfoItem(pInfo); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "obtain"); + } + return null; } - public static AppInfoItem obtain(String packName) throws Exception { - // 如果包名为null, 则直接不处理 - if (TextUtils.isEmpty(packName)){ - return null; - } - // 获取上下文 + /** + * 初始化 AppInfoItem 对象 + * @param pInfo + */ + private AppInfoItem(PackageInfo pInfo) { + // 获取 Context 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; + // 获取 App 信息 + appInfoBean = new AppInfoBean(pInfo); + // App 签名MD5 + appMD5 = SignaturesUtils.signatureMD5(pInfo.signatures); + // App SHA1 + appSHA1 = SignaturesUtils.signatureSHA1(pInfo.signatures); + // App SHA256 + appSHA256 = SignaturesUtils.signatureSHA256(pInfo.signatures); // 属于7.0以上才有的方法 - if (AppCommonUtils.isN()){ + if (AppCommonUtils.isN()) { + // App 最低支持版本 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); + // App 兼容sdk版本 + targetSdkVersion = pInfo.applicationInfo.targetSdkVersion; + // App 安装包大小 + apkLength = Formatter.formatFileSize(DevUtils.getContext(), FileUtils.getFileLength(appInfoBean.getSourceDir())); + // 证书对象 + cert = SignaturesUtils.getX509Certificate(pInfo.signatures); + // 证书生成日期 + notBefore = cert.getNotBefore(); + // 证书有效期 + notAfter = cert.getNotAfter(); // 设置有效期 StringBuilder sbEffective = new StringBuilder(); - sbEffective.append(dFormat.format(cert.getNotBefore())); + sbEffective.append(dFormat.format(notBefore)); sbEffective.append(" " + context.getString(R.string.dev_str_to) + " "); // 至 - sbEffective.append(dFormat.format(cert.getNotAfter())); + sbEffective.append(dFormat.format(notAfter)); sbEffective.append("\n\n"); - sbEffective.append(cert.getNotBefore()); + sbEffective.append(notBefore); sbEffective.append(" " + context.getString(R.string.dev_str_to) + " "); - sbEffective.append(cert.getNotAfter()); - // 获取有效期 - String effective = sbEffective.toString(); + sbEffective.append(notAfter); + // 保存有效期转换信息 + String effectiveStr = sbEffective.toString(); // 证书是否过期 true = 过期,false = 未过期 - boolean isEffective = false; + effective = false; try { cert.checkValidity(); // CertificateExpiredException - 如果证书已过期。 // CertificateNotYetValidException - 如果证书不再有效。 } catch (CertificateExpiredException ce) { - isEffective = true; + effective = true; } catch (CertificateNotYetValidException ce) { - isEffective = true; + effective = true; } - // 判断是否过期 - String isEffectiveState = isEffective ? context.getString(R.string.dev_str_overdue) : context.getString(R.string.dev_str_notoverdue); // 证书发布方 - String principal = cert.getIssuerX500Principal().toString(); + certPrincipal = cert.getIssuerX500Principal().toString(); // 证书版本号 - String version = cert.getVersion() + ""; + certVersion = cert.getVersion() + ""; // 证书算法名称 - String sigalgname = cert.getSigAlgName(); + certSigalgname = cert.getSigAlgName(); // 证书算法OID - String sigalgoid = cert.getSigAlgOID(); + certSigalgoid = cert.getSigAlgOID(); // 证书机器码 - String serialnumber = cert.getSerialNumber().toString(); - // 证书 DER编码 - String dercode = SignaturesUtils.toHexString(cert.getTBSCertificate()); + certSerialnumber = cert.getSerialNumber().toString(); + try { + // 证书 DER 编码 + certDercode = SignaturesUtils.toHexString(cert.getTBSCertificate()); + } catch (CertificateEncodingException e) { + } // ================ // === 保存集合 === // ================ - // 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)); + // 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, dFormat.format(pInfo.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)); + listKeyValues.add(KeyValueBean.get(R.string.dev_str_last_update_time, dFormat.format(pInfo.lastUpdateTime))); + // App 最低支持版本 + 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)); // 判断是否过期 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_iseffective, isEffectiveState)); + listKeyValues.add(KeyValueBean.get(R.string.dev_str_iseffective, effective ? context.getString(R.string.dev_str_overdue) : context.getString(R.string.dev_str_notoverdue))); // 证书发布方 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_principal, principal)); + listKeyValues.add(KeyValueBean.get(R.string.dev_str_principal, certPrincipal)); // 证书版本号 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_version, version)); + listKeyValues.add(KeyValueBean.get(R.string.dev_str_version, certVersion)); // 证书算法名称 - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_sigalgname, sigalgname)); + listKeyValues.add(KeyValueBean.get(R.string.dev_str_sigalgname, certSigalgname)); // 证书算法OID - appInfoItem.listKeyValues.add(KeyValueBean.get(R.string.dev_str_sigalgoid, sigalgoid)); + listKeyValues.add(KeyValueBean.get(R.string.dev_str_sigalgoid, certSigalgoid)); // 证书机器码 - 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; + 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; } - public ArrayList getListKeyValues() { + /** + * 获取 List + * @return App 信息键对值集合 + */ + public List getListKeyValues() { return listKeyValues; } + + /** + * 获取 App MD5 签名 + * @return + */ + public String getAppMD5() { + return appMD5; + } + + /** + * 获取 App SHA1 签名 + * @return + */ + public String getAppSHA1() { + return appSHA1; + } + + /** + * 获取 App SHA256 签名 + * @return + */ + public String getAppSHA256() { + return appSHA256; + } + + /** + * 获取 App 最低支持版本 + * @return + */ + public int getMinSdkVersion() { + return minSdkVersion; + } + + /** + * 获取 App 兼容sdk版本 + * @return + */ + public int getTargetSdkVersion() { + return targetSdkVersion; + } + + /** + * 获取 App 安装包大小 + * @return + */ + public String getApkLength() { + return apkLength; + } + + /** + * 获取证书对象 + * @return + */ + public X509Certificate getX509Certificate() { + return cert; + } + + /** + * 获取证书生成日期 + * @return + */ + public Date getNotBefore() { + return notBefore; + } + + /** + * 获取证书有效期 + * @return + */ + public Date getNotAfter() { + return notAfter; + } + + /** + * 获取证书是否过期 true = 过期,false = 未过期 + * @return + */ + 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 + */ + public String getCertSigalgoid() { + return certSigalgoid; + } + + /** + * 获取证书机器码 + * @return + */ + public String getCertSerialnumber() { + return certSerialnumber; + } + + /** + * 获取证书 DER编码 + * @return + */ + public String getCertDercode() { + return certDercode; + } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoUtils.java b/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoUtils.java index dcf08fc0e5..f370881772 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/info/AppInfoUtils.java @@ -1,10 +1,12 @@ 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.List; @@ -17,33 +19,103 @@ */ public final class AppInfoUtils { - private AppInfoUtils(){ + private AppInfoUtils() { } // 日志Tag private static final String TAG = AppInfoUtils.class.getSimpleName(); /** - * 通过 apk路径 初始化 App 信息实体类 + * 通过 Apk 路径 初始化 PackageInfo + * @param file apk路径 + * @return + */ + public static PackageInfo getPackageInfoToFile(File file) { + if (!isFileExists(file)) return null; + return getPackageInfoToPath(file.getAbsolutePath()); + } + + /** + * 通过 Apk 路径 初始化 PackageInfo * @param apkUri apk路径 + * @return */ - public static AppInfoBean obtainUri(String apkUri){ - return AppInfoBean.obtainUri(apkUri); + public static PackageInfo getPackageInfoToPath(String apkUri) { + try { + PackageManager pManager = DevUtils.getContext().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 pInfo; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPackageInfoToPath"); + } + return null; } /** - * 通过包名 初始化 App 信息实体类 - * @param pckName 包名 + * 获取当前应用 PackageInfo + * @return */ - public static AppInfoBean obtainPck(String pckName){ - return AppInfoBean.obtainPck(pckName); + public static PackageInfo getPackageInfo() { + return getPackageInfo(DevUtils.getContext().getPackageName()); } /** - * 初始化当前 App 信息实体类 + * 通过包名 获取 PackageInfo + * @param packageName 包名 + * @return */ - public static AppInfoBean obtain(){ - return AppInfoBean.obtain(); + public static PackageInfo getPackageInfo(String packageName) { + try { + // https://blog.csdn.net/sljjyy/article/details/17370665 + PackageManager pManager = DevUtils.getContext().getPackageManager(); + // 获取对应的PackageInfo(原始的PackageInfo 获取 signatures 等于null,需要这样获取) + PackageInfo pInfo = pManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); // 64 + // 返回app信息 + return pInfo; + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getPackageInfo"); + } + return null; + } + + // ================== + // == 获取基本信息 == + // ================== + + /** + * 通过 Apk 路径 获取 AppInfoBean + * @param file apk路径 + */ + public static AppInfoBean getAppInfoBeanToFile(File file) { + return AppInfoBean.obtain(getPackageInfoToFile(file)); + } + + /** + * 通过 Apk 路径 获取 AppInfoBean + * @param apkUri apk路径 + */ + public static AppInfoBean getAppInfoBeanToPath(String apkUri) { + return AppInfoBean.obtain(getPackageInfoToPath(apkUri)); + } + + /** + * 获取当前应用 AppInfoBean + */ + public static AppInfoBean getAppInfoBean() { + return AppInfoBean.obtain(getPackageInfo()); + } + + /** + * 通过包名 获取 AppInfoBean + * @param packageName 包名 + */ + public static AppInfoBean getAppInfoBean(String packageName) { + return AppInfoBean.obtain(getPackageInfo(packageName)); } // ================== @@ -51,55 +123,74 @@ public static AppInfoBean obtain(){ // ================== /** - * 获取 apk 详细信息 + * 获取 Apk 详细信息 + * @param file + * @return + */ + public static ApkInfoItem getApkInfoItem(File file) { + if (!isFileExists(file)) return null; + return getApkInfoItem(file.getAbsolutePath()); + } + + /** + * 获取 Apk 详细信息 * @param apkUri * @return */ - public static ApkInfoItem getApkInfoItem(String apkUri){ + public static ApkInfoItem getApkInfoItem(String apkUri) { try { - return ApkInfoItem.obtain(apkUri); - } catch (Exception e){ + return ApkInfoItem.obtain(getPackageInfoToPath(apkUri)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getApkInfoItem"); return null; } } + // = + /** - * 获取 app 详细信息 - * @param pckName + * 获取 App 详细信息 * @return */ - public static AppInfoItem getAppInfoItem(String pckName){ + public static AppInfoItem getAppInfoItem() { try { - return AppInfoItem.obtain(pckName); - } catch (Exception e){ + return AppInfoItem.obtain(getPackageInfo()); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppInfoItem"); return null; } } /** - * 获取 app 详细信息 + * 获取 App 详细信息 + * @param packageName * @return */ - public static AppInfoItem getAppInfoItem(){ - return getAppInfoItem(DevUtils.getContext().getPackageName()); + public static AppInfoItem getAppInfoItem(String packageName) { + try { + return AppInfoItem.obtain(getPackageInfo(packageName)); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "getAppInfoItem"); + return null; + } } // = /** - * 获取全部App 列表 - * @return + * 获取全部 App 列表 + * @return 返回 App 信息实体类集合 */ - public static ArrayList getAppLists() { + public static List getAppLists() { return getAppLists(AppInfoBean.AppType.ALL); } /** * 获取 App 列表 - * @param appType app类型 - * @return + * @param appType App 类型 + * @return 返回 App 信息实体类集合 */ - public static ArrayList getAppLists(AppInfoBean.AppType appType) { + public static List getAppLists(AppInfoBean.AppType appType) { // App信息 ArrayList listApps = new ArrayList<>(); // 防止为null @@ -109,21 +200,21 @@ public static ArrayList getAppLists(AppInfoBean.AppType appType) { // 获取手机内所有应用 List packlist = pManager.getInstalledPackages(0); // 判断是否属于添加全部 - if (appType == AppInfoBean.AppType.ALL){ - // 遍历 app 列表 + 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 列表 + // 遍历 App 列表 for (int i = 0, len = packlist.size(); i < len; i++) { PackageInfo pInfo = packlist.get(i); - // 获取app 类型 + // 获取 App 类型 AppInfoBean.AppType cAppType = AppInfoBean.getAppType(pInfo); // 判断类型 - if (appType == cAppType){ + if (appType == cAppType) { // 添加符合条件的 App 应用信息 listApps.add(new AppInfoBean(pInfo, pManager)); } @@ -147,14 +238,22 @@ public static ArrayList getAppLists(AppInfoBean.AppType appType) { // = /** - * 获取 APK 权限 - * @param pckName - * https://www.cnblogs.com/leaven/p/5485864.html + * 获取 Apk 注册的权限 + * @return + */ + public static String[] getApkPermission() { + return getApkPermission(DevUtils.getContext().getPackageName()); + } + + /** + * 获取 Apk 注册的权限 + * @param packageName + * @return */ - public static String [] getApkPermission(String pckName){ + public static String [] getApkPermission(String packageName) { try { - PackageManager packageManager = DevUtils.getApplication().getPackageManager(); - PackageInfo packageInfo = packageManager.getPackageInfo(pckName, PackageManager.GET_PERMISSIONS); + PackageManager packageManager = DevUtils.getContext().getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); return packageInfo.requestedPermissions; } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getUsesPermission"); @@ -163,14 +262,14 @@ public static ArrayList getAppLists(AppInfoBean.AppType appType) { } /** - * 打印 APK 权限 - * @param pckName + * 打印 Apk 注册的权限 + * @param packageName * https://www.cnblogs.com/leaven/p/5485864.html */ - public static void printApkPermission(String pckName){ + public static void printApkPermission(String packageName) { try { - PackageManager packageManager = DevUtils.getApplication().getPackageManager(); - PackageInfo packageInfo = packageManager.getPackageInfo(pckName, PackageManager.GET_PERMISSIONS); + 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 @@ -180,7 +279,7 @@ public static void printApkPermission(String pckName){ // 通过usesPermissionName获取该权限的详细信息 PermissionInfo permissionInfo = packageManager.getPermissionInfo(usesPermissionName, 0); - // 获得该权限属于哪个权限组,如:网络通信 + // 获取该权限属于哪个权限组,如:网络通信 PermissionGroupInfo permissionGroupInfo = packageManager.getPermissionGroupInfo(permissionInfo.group, 0); LogPrintUtils.dTag(TAG, "permissionGroup = " + permissionGroupInfo.loadLabel(packageManager).toString()); @@ -197,4 +296,15 @@ public static void printApkPermission(String pckName){ LogPrintUtils.eTag(TAG, e, "printApkPermission"); } } + + // == + + /** + * 检查是否存在某个文件 + * @param file 文件路径 + * @return 是否存在文件 + */ + private static boolean isFileExists(final File file) { + return file != null && file.exists(); + } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/info/KeyValueBean.java b/DevLibUtils/src/main/java/dev/utils/app/info/KeyValueBean.java index b02baae48a..47354633d9 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/info/KeyValueBean.java +++ b/DevLibUtils/src/main/java/dev/utils/app/info/KeyValueBean.java @@ -1,5 +1,6 @@ package dev.utils.app.info; +import android.support.annotation.Keep; import android.support.annotation.StringRes; import dev.DevUtils; @@ -11,8 +12,10 @@ public class KeyValueBean { // 键 - 提示 + @Keep protected String key = ""; // 值 - 参数值 + @Keep protected String value = ""; /** @@ -25,10 +28,18 @@ public KeyValueBean(String key, String value) { this.value = value; } + /** + * 获取 key + * @return + */ public String getKey() { return key; } + /** + * 获取 value + * @return + */ public String getValue() { return value; } @@ -38,6 +49,12 @@ public String toString() { return key + ": " + value; } + /** + * 通过 resId 设置key, 并且初始化 KeyValueBean + * @param resId + * @param value + * @return + */ 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 index 7138344a7d..6be536ffcc 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/DevLogger.java +++ b/DevLibUtils/src/main/java/dev/utils/app/logger/DevLogger.java @@ -32,7 +32,7 @@ public static LogConfig getLogConfig() { } /** - * 初始化日志配置信息(可以不调用,使用了App默认配置) + * 初始化日志配置信息(可以不调用,使用了 App 默认配置) * @return */ public static LogConfig init() { @@ -51,7 +51,7 @@ public static void init(LogConfig lConfig) { // -- 日志打印方法 -- /** - * Log.DEBUG + * 打印 Log.DEBUG * @param message * @param args */ @@ -60,7 +60,7 @@ public static void d(String message, Object... args) { } /** - * Log.ERROR + * 打印 Log.ERROR * @param message * @param args */ @@ -69,7 +69,7 @@ public static void e(String message, Object... args) { } /** - * Log.ERROR + * 打印 Log.ERROR * @param throwable */ public static void e(Throwable throwable) { @@ -77,7 +77,7 @@ public static void e(Throwable throwable) { } /** - * Log.ERROR + * 打印 Log.ERROR * @param throwable * @param message * @param args @@ -87,7 +87,7 @@ public static void e(Throwable throwable, String message, Object... args) { } /** - * Log.WARN + * 打印 Log.WARN * @param message * @param args */ @@ -96,7 +96,7 @@ public static void w(String message, Object... args) { } /** - * Log.INFO + * 打印 Log.INFO * @param message * @param args */ @@ -105,7 +105,7 @@ public static void i(String message, Object... args) { } /** - * Log.VERBOSE + * 打印 Log.VERBOSE * @param message * @param args */ @@ -114,7 +114,7 @@ public static void v(String message, Object... args) { } /** - * Log.ASSERT + * 打印 Log.ASSERT * @param message * @param args */ @@ -145,7 +145,7 @@ public static void xml(String xml) { // -- 日志打印方法 -- /** - * Log.DEBUG + * 打印 Log.DEBUG * @param tag * @param message * @param args @@ -155,7 +155,7 @@ public static void dTag(String tag, String message, Object... args) { } /** - * Log.ERROR + * 打印 Log.ERROR * @param tag * @param message * @param args @@ -165,7 +165,7 @@ public static void eTag(String tag, String message, Object... args) { } /** - * Log.ERROR + * 打印 Log.ERROR * @param tag * @param throwable * @param message @@ -176,7 +176,7 @@ public static void eTag(String tag, Throwable throwable, String message, Object. } /** - * Log.ERROR + * 打印 Log.ERROR * @param tag * @param throwable */ @@ -185,7 +185,7 @@ public static void eTag(String tag, Throwable throwable) { } /** - * Log.WARN + * 打印 Log.WARN * @param tag * @param message * @param args @@ -195,7 +195,7 @@ public static void wTag(String tag, String message, Object... args) { } /** - * Log.INFO + * 打印 Log.INFO * @param tag * @param message * @param args @@ -205,7 +205,7 @@ public static void iTag(String tag, String message, Object... args) { } /** - * Log.VERBOSE + * 打印 Log.VERBOSE * @param tag * @param message * @param args @@ -215,7 +215,7 @@ public static void vTag(String tag, String message, Object... args) { } /** - * Log.ASSERT + * 打印 Log.ASSERT * @param tag * @param message * @param args diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java b/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java index 1e484bcc81..52121d5f23 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java @@ -13,25 +13,25 @@ */ public final class DevLoggerUtils { - private DevLoggerUtils(){ + private DevLoggerUtils() { } // 日志TAG private static final String TAG = DevLoggerUtils.class.getSimpleName(); /** - * App初始化调用方法(获取版本号) - * @param context 上下文 + * 初始化调用方法(获取版本号) + * @param context */ - public static void appInit(Context context) { - // 保存APP版本信息 - Utils.appInit(context); + public static void init(Context context) { + // 保存 App 版本信息 + Utils.init(context); } // ================= 内部快速初始化LogConfig ================== /** - * 获取发布Log配置(打印线程信息,显示方法总数3,从0开始,不进行排序, 默认属于ERROR级别日志) + * 获取发布 Log 配置(打印线程信息,显示方法总数3,从0开始,不进行排序, 默认属于ERROR级别日志) * @param tag * @return */ @@ -40,7 +40,7 @@ public static LogConfig getReleaseLogConfig(String tag) { } /** - * 获取发布Log配置(打印线程信息,显示方法总数3,从0开始,不进行排序) + * 获取发布 Log 配置(打印线程信息,显示方法总数3,从0开始,不进行排序) * @param tag * @param lLevel 日志级别(分四种) - LogLevel * @return @@ -52,7 +52,7 @@ public static LogConfig getReleaseLogConfig(String tag, LogLevel lLevel) { // -- /** - * 获取调试Log配置(打印线程信息,显示方法总数3,从0开始,不进行排序, 默认属于ERROR级别日志) + * 获取调试 Log 配置(打印线程信息,显示方法总数3,从0开始,不进行排序, 默认属于ERROR级别日志) * @param tag * @return */ @@ -61,9 +61,9 @@ public static LogConfig getDebugLogConfig(String tag) { } /** - * 获取调试Log配置(打印线程信息,显示方法总数3,从0开始,进行排序) + * 获取调试 Log 配置(打印线程信息,显示方法总数3,从0开始,进行排序) * @param tag - * @param lLevel 日志级别(分四种) - LogLevel + * @param lLevel 日志级别(分四种) - {@link LogLevel} * @return */ public static LogConfig getDebugLogConfig(String tag, LogLevel lLevel) { @@ -73,7 +73,7 @@ public static LogConfig getDebugLogConfig(String tag, LogLevel lLevel) { // -- /** - * 获取Log配置(打印线程信息,显示方法总数3,从0开始,并且美化日志信息, 默认属于DEBUG级别日志) + * 获取 Log 配置(打印线程信息,显示方法总数3,从0开始,并且美化日志信息, 默认属于DEBUG级别日志) * @param tag 日志Tag * @return */ @@ -82,7 +82,7 @@ public static LogConfig getSortLogConfig(String tag) { } /** - * 获取Log配置(打印线程信息,显示方法总数3,从0开始,并且美化日志信息) + * 获取 Log 配置(打印线程信息,显示方法总数3,从0开始,并且美化日志信息) * @param tag 日志Tag * @param lLevel 日志级别(分四种) - LogLevel * @return @@ -94,29 +94,29 @@ public static LogConfig getSortLogConfig(String tag, LogLevel lLevel) { // -- /** - * 获取Log配置 + * 获取 Log 配置 * @param tag 日志Tag - * @param mCount 显示的方法总数(推荐3) - * @param mOffset 方法偏移索引(从第几个方法开始打印,默认推荐0) - * @param mAll 是否打印全部方法 + * @param count 显示的方法总数(推荐3) + * @param offset 方法偏移索引(从第几个方法开始打印,默认推荐0) + * @param all 是否打印全部方法 * @param tInfo 是否显示线程信息 - * @param isSortLog 是否排序日志(美化) + * @param sortLog 是否排序日志(美化) * @param lLevel 日志级别(分四种) - LogLevel * @return */ - public static LogConfig getLogConfig(String tag, int mCount, int mOffset, boolean mAll, boolean tInfo, boolean isSortLog, LogLevel lLevel) { + public static LogConfig getLogConfig(String tag, int count, int offset, boolean all, boolean tInfo, boolean sortLog, LogLevel lLevel) { // 生成默认配置信息 LogConfig logConfig = new LogConfig(); // 堆栈方法总数(显示经过的方法) - logConfig.methodCount = mCount; + logConfig.methodCount = count; // 堆栈方法索引偏移(0 = 最新经过调用的方法信息,偏移则往上推,如 1 = 倒数第二条经过调用的方法信息) - logConfig.methodOffset = mOffset; + logConfig.methodOffset = offset; // 是否输出全部方法(在特殊情况下,如想要打印全部经过的方法,但是不知道经过的总数) - logConfig.isOutputMethodAll = mAll; + logConfig.outputMethodAll = all; // 显示日志线程信息(特殊情况,显示经过的线程信息,具体情况如上) - logConfig.isDisplayThreadInfo = tInfo; + logConfig.displayThreadInfo = tInfo; // 是否排序日志(格式化) - logConfig.isSortLog = isSortLog; + logConfig.sortLog = sortLog; // 日志级别 logConfig.logLevel = lLevel; // 设置Tag(特殊情况使用,不使用全部的Tag时,如单独输出在某个Tag下) @@ -128,7 +128,7 @@ public static LogConfig getLogConfig(String tag, int mCount, int mOffset, boolea // =============== 错误日志处理 ================== /** - * 保存app错误日志 + * 保存 App 错误日志 * @param ex 错误信息 * @param fPath 保存路径 + 文件名(含后缀) * @param isNewLines 是否换行 @@ -139,7 +139,7 @@ public static boolean saveErrorLog(Throwable ex, String fPath, boolean isNewLine } /** - * 保存app错误日志 + * 保存 App 错误日志 * @param ex 错误信息 * @param head 顶部标题 * @param bottom 底部内容 @@ -172,7 +172,7 @@ public static boolean saveErrorLog(Throwable ex, String head, String bottom, Str } /** - * 保存app错误日志 + * 保存 App 错误日志 * @param ex 错误信息 * @param head 顶部标题 * @param bottom 底部内容 @@ -192,19 +192,19 @@ public static boolean saveErrorLog(Throwable ex, String head, String bottom, Str } /** - * 保存app日志 + * 保存 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); + public static boolean saveLog(String log, String fPath, String fName, String... eHint) { + return saveLogHeadBottom(log, null, null, fPath, fName, eHint); } /** - * 保存app日志 + * 保存 App 日志 - 包含头部、底部信息 * @param log 日志信息 * @param head 顶部标题 * @param bottom 底部内容 @@ -213,7 +213,7 @@ public static boolean saveLog(String log, String fPath, String fName, String... * @param eHint 错误提示(无设备信息、失败信息获取失败) * @return */ - public static boolean saveLog(String log, String head, String bottom, String fPath, String fName, String... eHint) { + public static boolean saveLogHeadBottom(String log, String head, String bottom, String fPath, String fName, String... eHint) { if(TextUtils.isEmpty(fPath)) { return false; } else if (TextUtils.isEmpty(fName)) { diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/LogConfig.java b/DevLibUtils/src/main/java/dev/utils/app/logger/LogConfig.java index 609ab48fac..21d45f7932 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/LogConfig.java +++ b/DevLibUtils/src/main/java/dev/utils/app/logger/LogConfig.java @@ -13,13 +13,13 @@ public class LogConfig { public int methodOffset = LogConstants.DEFAULT_LOG_METHOD_OFFSET; /** 是否输出全部方法(在特殊情况下,如想要打印全部经过的方法,但是不知道经过的总数) = 默认 false */ - public boolean isOutputMethodAll = LogConstants.JUDGE_OUTPUT_METHOD_ALL; + public boolean outputMethodAll = LogConstants.JUDGE_OUTPUT_METHOD_ALL; /** 显示日志线程信息(特殊情况,显示经过的线程信息,具体情况如上) = 默认 false */ - public boolean isDisplayThreadInfo = LogConstants.JUDGE_DISPLAY_THREAD_LOG; + public boolean displayThreadInfo = LogConstants.JUDGE_DISPLAY_THREAD_LOG; /** 是否排序日志(格式化) = 默认 false */ - public boolean isSortLog = LogConstants.JUDGE_SORT_LOG; + public boolean sortLog = LogConstants.JUDGE_SORT_LOG; /** 日志级别 - 默认异常级别(只有 e,wtf 才进行显示) */ public LogLevel logLevel = LogConstants.DEFAULT_LOG_LEVEL; diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/LogConstants.java b/DevLibUtils/src/main/java/dev/utils/app/logger/LogConstants.java index ebddbc41d0..21f8080b32 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/LogConstants.java +++ b/DevLibUtils/src/main/java/dev/utils/app/logger/LogConstants.java @@ -18,7 +18,7 @@ final class LogConstants { public static final boolean JUDGE_DISPLAY_THREAD_LOG = false; /** 默认的日志Tag */ - public static final String DEFAULT_LOG_TAG = "DevLogger"; + public static final String DEFAULT_LOG_TAG = DevLogger.class.getSimpleName(); /** 默认输出方法数量 */ public static final int DEFAULT_LOG_METHOD_COUNT = 3; diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/LoggerPrinter.java b/DevLibUtils/src/main/java/dev/utils/app/logger/LoggerPrinter.java index c1b3fa57aa..6b4816632e 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/LoggerPrinter.java +++ b/DevLibUtils/src/main/java/dev/utils/app/logger/LoggerPrinter.java @@ -26,7 +26,7 @@ final class LoggerPrinter implements IPrinter { private static LogConfig LOG_CONFIG = null; /** 每个线程的日志配置信息 */ - private static final ThreadLocal LOCAL_LOG_CONFIGS = new ThreadLocal(); + private static final ThreadLocal LOCAL_LOG_CONFIGS = new ThreadLocal<>(); // ================== 实现IPrinter接口,对外公开方法 =================== @@ -164,14 +164,14 @@ public void json(String json) { } } catch (Exception e) { String eHint = "null"; - if (e != null){ + if (e != null) { Throwable throwable = e.getCause(); - if (throwable != null){ + if (throwable != null) { eHint = throwable.getMessage(); } else { try { eHint = e.getMessage(); - } catch (Exception e1){ + } catch (Exception e1) { eHint = e1.getMessage(); } } @@ -209,14 +209,14 @@ public void xml(String xml) { logHandle(lConfig, tag, Log.DEBUG, message); } catch (Exception e) { String eHint = "null"; - if (e != null){ + if (e != null) { Throwable throwable = e.getCause(); - if (throwable != null){ + if (throwable != null) { eHint = throwable.getMessage(); } else { try { eHint = e.getMessage(); - } catch (Exception e1){ + } catch (Exception e1) { eHint = e1.getMessage(); } } @@ -310,14 +310,14 @@ public void jsonTag(String tag, String json) { } } catch (Exception e) { String eHint = "null"; - if (e != null){ + if (e != null) { Throwable throwable = e.getCause(); - if (throwable != null){ + if (throwable != null) { eHint = throwable.getMessage(); } else { try { eHint = e.getMessage(); - } catch (Exception e1){ + } catch (Exception e1) { eHint = e1.getMessage(); } } @@ -352,14 +352,14 @@ public void xmlTag(String tag, String xml) { logHandle(lConfig, tag, Log.DEBUG, message); } catch (Exception e) { String eHint = "null"; - if (e != null){ + if (e != null) { Throwable throwable = e.getCause(); - if (throwable != null){ + if (throwable != null) { eHint = throwable.getMessage(); } else { try { eHint = e.getMessage(); - } catch (Exception e1){ + } catch (Exception e1) { eHint = e1.getMessage(); } } @@ -383,19 +383,14 @@ private boolean isPrintLog(LogConfig lConfig, int logType) { LogLevel lLevel = lConfig.logLevel; // -- switch(lLevel) { - /** 全部不打印 */ - case NONE: + case NONE: // 全部不打印 break; - /** 调试级别 v,d - 全部打印*/ - case DEBUG: + case DEBUG: // 调试级别 v,d - 全部打印 isPrint = true; break; - /** 正常级别 i */ - case INFO: - /** 警告级别 w */ - case WARN: - /** 异常级别 e,wtf */ - case ERROR: + case INFO: // 正常级别 i + case WARN: // 警告级别 w + case ERROR: // 异常级别 e,wtf isPrint = checkLogLevel(lLevel, logType); break; default: @@ -412,20 +407,17 @@ private boolean isPrintLog(LogConfig lConfig, int logType) { */ private boolean checkLogLevel(LogLevel lLevel, int logType) { switch(lLevel) { - /** 正常级别 i */ - case INFO: + case INFO: // 正常级别 i if(logType != Log.VERBOSE && logType != Log.DEBUG) { return true; } break; - /** 警告级别 w */ - case WARN: + case WARN: // 警告级别 w if(logType != Log.VERBOSE && logType != Log.DEBUG && logType != Log.INFO) { return true; } break; - /** 异常级别 e,wtf */ - case ERROR: + case ERROR: // 异常级别 e,wtf if(logType == Log.ERROR || logType == Log.ASSERT) { return true; } @@ -518,7 +510,7 @@ private synchronized void logHandle(LogConfig lConfig, String tag, int logType, } } // 判断是否显示排序后的日志(如果不排序,则显示默认) - if(!lConfig.isSortLog) { + if(!lConfig.sortLog) { fLogPrinter(logType, tag, createMessage(msg, args)); return; } @@ -571,7 +563,7 @@ private synchronized void logHandle(LogConfig lConfig, String tag, int logType, logBottomBorder(logType, tag); } - // ========================== 日志格式拼接 ========================== + // ========================== 日志格式拼接 ========================== /** * 日志线程信息主体部分 * @param lConfig 日志配置 @@ -583,7 +575,7 @@ private synchronized void logHandle(LogConfig lConfig, String tag, int logType, private void logHeaderContent(LogConfig lConfig, int logType, String tag, int methodCount, int methodOffset) { StackTraceElement[] trace = Thread.currentThread().getStackTrace(); // 判断是否显示日志线程信息 - if (lConfig.isDisplayThreadInfo) { + if (lConfig.displayThreadInfo) { // 打印线程信息(线程名) fLogPrinter(logType, tag, LogConstants.HORIZONTAL_DOUBLE_LINE + " Thread: " + Thread.currentThread().getName()); // 进行换行 @@ -604,7 +596,7 @@ private void logHeaderContent(LogConfig lConfig, int logType, String tag, int me methodCount = traceCount - stackOffset - 1; } // 判断是否显示全部方法 - if(lConfig.isOutputMethodAll) { + if(lConfig.outputMethodAll) { // 设置方法总数 methodCount = traceCount; // 设置方法偏移索引为0 @@ -690,7 +682,7 @@ private String createMessage(String message, Object... args) { return ""; } - // =================== 获取堆栈信息 ============================ + // =================== 获取堆栈信息 ============================ /** * 获取类名 * @param name 类名.class @@ -716,7 +708,7 @@ private int getStackOffset(StackTraceElement[] trace) { return -1; } - // ========================== 日志配置获取 ========================== + // ========================== 日志配置获取 ========================== /** * 返回对应线程的日志配置信息 */ diff --git a/DevLibUtils/src/main/java/dev/utils/app/logger/Utils.java b/DevLibUtils/src/main/java/dev/utils/app/logger/Utils.java index b71e4a0afc..7239d69452 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/logger/Utils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/logger/Utils.java @@ -15,6 +15,7 @@ import java.lang.reflect.Field; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; @@ -28,7 +29,7 @@ */ final class Utils { - private Utils(){ + private Utils() { } // 日志TAG @@ -36,31 +37,31 @@ private Utils(){ // =================== 配置信息 ======================= - /** App版本(如1.0.01) 显示给用户看的 */ - static String APP_VERSION_NAME = ""; + /** App 版本(如1.0.01) 显示给用户看的 */ + private static String APP_VERSION_NAME = ""; /** android:versionCode——整数值,代表应用程序代码的相对版本,也就是版本更新过多少次。(不显示给用户看) */ - static String APP_VERSION_CODE = ""; + private static String APP_VERSION_CODE = ""; /** 设备信息 */ - static String DEVICE_INFO_STR = null; + private static String DEVICE_INFO_STR = null; /** 用来存储设备信息 */ - static HashMap DEVICE_INFO_MAPS = new HashMap(); + private static Map DEVICE_INFO_MAPS = new HashMap<>(); /** 换行字符串 */ - static final String NEW_LINE_STR = System.getProperty("line.separator"); + private static final String NEW_LINE_STR = System.getProperty("line.separator"); /** 换行字符串 - 两行 */ - static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; + private static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; - // ================== APP、设备信息处理 =================== + // ================== App、设备信息处理 =================== /** - * 获取app版本信息 - * @param context 上下文 + * 获取 App 版本信息 + * @param context */ - static String[] getAppVersion(Context context) { + private static String[] getAppVersion(Context context) { String[] aVersion = null; try { PackageManager pm = context.getPackageManager(); @@ -81,15 +82,31 @@ static String[] getAppVersion(Context context) { * 获取设备信息 * @param dInfoMaps 传入设备信息传出HashMap */ - static void getDeviceInfo(HashMap dInfoMaps) { + private static void getDeviceInfo(Map dInfoMaps) { // 获取设备信息类的所有申明的字段,即包括public、private和proteced, 但是不包括父类的申明字段。 Field[] fields = Build.class.getDeclaredFields(); // 遍历字段 for (Field field : fields) { try { - // 取消java的权限控制检查 + // 取消 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] + dInfoMaps.put(field.getName(), Arrays.toString((String[]) object)); + } + continue; + } + } catch (Exception e) { + } + } + // 获取类型对应字段的数据, 并保存 dInfoMaps.put(field.getName(), field.get(null).toString()); } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "getDeviceInfo"); @@ -101,7 +118,7 @@ static void getDeviceInfo(HashMap dInfoMaps) { * 处理设备信息 * @param eHint 错误提示,如获取设备信息失败 */ - static String handleDeviceInfo(String eHint) { + private static String handlerDeviceInfo(String eHint) { try { // 如果不为null,则直接返回之前的信息 if(!TextUtils.isEmpty(DEVICE_INFO_STR)) { @@ -114,9 +131,9 @@ static String handleDeviceInfo(String eHint) { // 遍历设备信息 while (mapIter.hasNext()) { // 获取对应的key-value - Map.Entry rnEntry = (Map.Entry) mapIter.next(); - String rnKey = (String) rnEntry.getKey(); // key - String rnValue = (String) rnEntry.getValue(); // value + Map.Entry rnEntry = mapIter.next(); + String rnKey = rnEntry.getKey(); // key + String rnValue = rnEntry.getValue(); // value // 保存设备信息 sBuilder.append(rnKey); sBuilder.append(" = "); @@ -128,7 +145,7 @@ static String handleDeviceInfo(String eHint) { // 返回设备信息 return DEVICE_INFO_STR; } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleDeviceInfo"); + LogPrintUtils.eTag(TAG, e, "handlerDeviceInfo"); } return eHint; } @@ -136,14 +153,14 @@ static String handleDeviceInfo(String eHint) { // ================== 时间格式化 =================== /** 日期格式类型 */ - static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; + private static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss"; /** * 获取当前日期的字符串 * @return 字符串 */ @SuppressLint("SimpleDateFormat") - static String getDateNow() { + private static String getDateNow() { try { Calendar cld = Calendar.getInstance(); DateFormat df = new SimpleDateFormat(yyyyMMddHHmmss); @@ -160,7 +177,7 @@ static String getDateNow() { * 判断某个文件夹是否创建,未创建则创建(不能加入文件名) * @param fPath 文件夹路径 */ - static File createFile(String fPath) { + private static File createFile(String fPath) { try { File file = new File(fPath); // 当这个文件夹不存在的时候则创建文件夹 @@ -181,7 +198,7 @@ static File createFile(String fPath) { * @param fUrl 保存路径(包含文件名.后缀) * @return 是否保存成功 */ - static boolean saveFile(String txt, String fUrl) { + private static boolean saveFile(String txt, String fUrl) { try { // 保存内容到一个文件 FileOutputStream fos = new FileOutputStream(fUrl); @@ -203,7 +220,7 @@ static boolean saveFile(String txt, String fUrl) { * @param ex 错误信息 * @return */ - static String getThrowableMsg(String eHint, Throwable ex) { + private static String getThrowableMsg(String eHint, Throwable ex) { PrintWriter printWriter = null; try { if(ex != null) { @@ -232,7 +249,7 @@ static String getThrowableMsg(String eHint, Throwable ex) { * @param ex 错误信息 * @return */ - static String getThrowableNewLinesMsg(String eHint, Throwable ex) { + private static String getThrowableNewLinesMsg(String eHint, Throwable ex) { PrintWriter printWriter = null; try { if(ex != null) { @@ -267,15 +284,15 @@ static String getThrowableNewLinesMsg(String eHint, Throwable ex) { // ================== 对外公开方法 =================== /** - * App初始化调用方法 - * @param context 上下文 + * 初始化调用方法 + * @param context */ - public static void appInit(Context context) { + public static void init(Context context) { // 如果版本信息为null,才进行处理 if (TextUtils.isEmpty(APP_VERSION_CODE) || TextUtils.isEmpty(APP_VERSION_NAME)) { - // 获取app版本信息 + // 获取 App 版本信息 String[] aVersion = getAppVersion(context); - // 保存app版本信息 + // 保存 App 版本信息 APP_VERSION_NAME = aVersion[0]; APP_VERSION_CODE = aVersion[1]; } @@ -284,14 +301,14 @@ public static void appInit(Context context) { // 获取设备信息 getDeviceInfo(DEVICE_INFO_MAPS); // 转换字符串 - handleDeviceInfo(""); + handlerDeviceInfo(""); } } // ========= 保存错误日志信息 ========== /** - * 保存app错误日志 + * 保存 App 错误日志 * @param ex 错误信息 * @param fPath 保存路径 * @param fName 文件名(含后缀) @@ -304,7 +321,7 @@ public static boolean saveErrorLog(Throwable ex, String fPath, String fName, boo } /** - * 保存app错误日志 + * 保存 App 错误日志 * @param ex 错误信息 * @param head 顶部标题 * @param bottom 底部内容 @@ -316,13 +333,13 @@ public static boolean saveErrorLog(Throwable ex, String fPath, String fName, boo */ public static boolean saveErrorLog(Throwable ex, String head, String bottom, String fPath, String fName, boolean isNewLines, String... eHint) { // 处理可变参数(错误提示) - eHint = handleVariable(2, eHint); + eHint = handlerVariable(2, eHint); // 日志拼接 StringBuilder sBuilder = new StringBuilder(); // 防止文件夹不存在 createFile(fPath); // 设备信息 - String dInfo = handleDeviceInfo(eHint[0]); + String dInfo = handlerDeviceInfo(eHint[0]); // 如果存在顶部内容,则进行添加 if(!TextUtils.isEmpty(head)) { sBuilder.append(head); @@ -331,7 +348,7 @@ public static boolean saveErrorLog(Throwable ex, String head, String bottom, Str sBuilder.append(NEW_LINE_STR_X2); } // ============ - // 保存App信息 + // 保存 App 信息 sBuilder.append("date: " + getDateNow()); sBuilder.append(NEW_LINE_STR); sBuilder.append("versionName: " + APP_VERSION_NAME); @@ -368,19 +385,19 @@ public static boolean saveErrorLog(Throwable ex, String head, String bottom, Str } /** - * 保存app日志 + * 保存 App 日志 * @param log 日志信息 * @param fPath 保存路径 * @param fName 文件名(含后缀) * @param eHint 错误提示(无设备信息、失败信息获取失败) * @return */ - public static boolean saveLog(String log, String fPath, String fName, String... eHint){ + public static boolean saveLog(String log, String fPath, String fName, String... eHint) { return saveLog(log, null, null, fPath, fName, eHint); } /** - * 保存app日志 + * 保存 App 日志 * @param log 日志信息 * @param head 顶部标题 * @param bottom 底部内容 @@ -389,15 +406,15 @@ public static boolean saveLog(String log, String fPath, String fName, String... * @param eHint 错误提示(无设备信息、失败信息获取失败) * @return */ - public static boolean saveLog(String log, String head, String bottom, String fPath, String fName, String... eHint){ + public static boolean saveLog(String log, String head, String bottom, String fPath, String fName, String... eHint) { // 处理可变参数(错误提示) - eHint = handleVariable(2, eHint); + eHint = handlerVariable(2, eHint); // 日志拼接 StringBuilder sBuilder = new StringBuilder(); // 防止文件夹不存在 createFile(fPath); // 设备信息 - String dInfo = handleDeviceInfo(eHint[0]); + String dInfo = handlerDeviceInfo(eHint[0]); // 如果存在顶部内容,则进行添加 if(!TextUtils.isEmpty(head)) { sBuilder.append(head); @@ -406,7 +423,7 @@ public static boolean saveLog(String log, String head, String bottom, String fPa sBuilder.append(NEW_LINE_STR_X2); } // ============ - // 保存App信息 + // 保存 App 信息 sBuilder.append("date: " + getDateNow()); sBuilder.append(NEW_LINE_STR); sBuilder.append("versionName: " + APP_VERSION_NAME); @@ -442,7 +459,7 @@ public static boolean saveLog(String log, String head, String bottom, String fPa * @param vArrays 可变参数数组 * @return */ - public static String[] handleVariable(int length, String[] vArrays) { + public static String[] handlerVariable(int length, String[] vArrays) { // 处理后的数据, String[] hArrays = new String[length]; // 是否统一处理 @@ -481,7 +498,7 @@ public static String[] handleVariable(int length, String[] vArrays) { } } } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "handleVariable"); + LogPrintUtils.eTag(TAG, e, "handlerVariable"); } return hArrays; } diff --git a/DevLibUtils/src/main/java/dev/utils/app/player/DevMediaManager.java b/DevLibUtils/src/main/java/dev/utils/app/player/DevMediaManager.java new file mode 100644 index 0000000000..24c0237b8f --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/player/DevMediaManager.java @@ -0,0 +1,643 @@ +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 统一管理类 + * Created by 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 instance; + + private DevMediaManager() { + } + + /** 获取 DevMediaManager 实例 ,单例模式 */ + public static DevMediaManager getInstance() { + if (instance == null) { + instance = new DevMediaManager(); + } + return instance; + } + + // =============== 内部处理方法 ================= + + /** + * 创建 MediaPlayer + */ + private void createMedia() { + // 销毁MediaPlayer + destroyMedia(); + // 初始化MediaPlayer + mMediaPlayer = new MediaPlayer(); + mMediaPlayer.reset(); + // 绑定事件 + bindListener(); + // 设置默认流类型 + setAudioStreamType(); + } + + /** + * 销毁 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; + // 清空播放信息 + clearMPlayerData(); + } + + /** + * 绑定事件 + * @return true: 绑定成功, 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 + */ + public void setAudioStreamType(int streamtype) { + if(mMediaPlayer != null) { + try { + // 播放流类型 + mMediaPlayer.setAudioStreamType(streamtype); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "setAudioStreamType"); + } + } + } + + /** + * 设置流类型(音量键可控制) + */ + private void setAudioStreamType() { + setAudioStreamType(AudioManager.STREAM_MUSIC); + } + + // == 播放操作 == + + /** + * 播放 Raw 资源 + * @param rawId 播放资源 + * @return true: 执行成功, false: 执行中断(失败) + */ + public boolean playPrepareRaw(@RawRes int rawId) { + return playPrepareRaw(rawId, false); + } + + /** + * 播放 Raw 资源 + * @param rawId 播放资源 + * @param isLooping 是否循环播放 + * @return true: 执行成功, false: 执行中断(失败) + */ + public boolean playPrepareRaw(final @RawRes 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 { + file.close(); + } + } + + @Override + public boolean isLooping() { + return isLooping; + } + }); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "playPrepareRaw"); + // 销毁资源 + destroyMedia(); + } + return false; + } + + // = + + /** + * 播放 Assets 资源 + * @param playUri 播放地址 + * @return true: 执行成功, false: 执行中断(失败) + */ + public boolean playPrepareAssets(final String playUri) { + return playPrepareAssets(playUri, false); + } + + /** + * 播放 Assets 资源 + * @param playUri 播放地址 + * @param isLooping 是否循环播放 + * @return true: 执行成功, false: 执行中断(失败) + */ + public boolean playPrepareAssets(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 { + file.close(); + } + } + + @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 true: 执行成功, false: 执行中断(失败) + */ + public boolean playPrepare(final String playUri) { + return playPrepare(playUri, false); + } + + /** + * 预加载播放 - (file-path or http/rtsp URL) http资源, 本地资源 + * @param playUri 播放地址 + * @param isLooping 是否循环播放 + * @return true: 执行成功, 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 true: 执行成功, false: 执行中断(失败) + */ + public boolean playPrepare(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操作 ================= + + /** + * 是否播放中(判断null) + * @return + */ + public boolean isPlaying() { + if(mMediaPlayer != null) { + return mMediaPlayer.isPlaying(); + } + return false; + } + + /** + * 暂停操作(判断null) + */ + public void pause() { + if(mMediaPlayer != null) { + mMediaPlayer.pause(); + } + } + + /** + * 停止操作(判断null) - 销毁MediaPlayer + */ + public void stop() { + // 销毁MediaPlayer + destroyMedia(); + } + + // =============== 快捷操作 ================ + + /** + * 是否忽略错误类型 + * @param eWhat + * @return + */ + public static boolean isIgnoreWhat(int eWhat) { + // 是否忽略 + boolean isIgnore = false; + switch(eWhat) { + case -38: + case 1: + case 100: + case 700: + case 701: + case 800: + isIgnore = true; + break; + } + return isIgnore; + } + + // =============== 回调事件 ================= + + /** 播放出错回调 */ + @Override + public boolean onError(MediaPlayer mp, int what, int extra) { + LogPrintUtils.dTag(TAG, "onError - what: " + what + ", extra: " + extra); + // 触发回调 + if(mMeidaListener != null) { + mMeidaListener.onError(what, extra); + } + return false; + } + + /** 视频大小改变回调 */ + @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(); 异步播放准备成功回调 */ + @Override + public void onPrepared(MediaPlayer mp) { + LogPrintUtils.dTag(TAG, "onPrepared"); + // 触发回调 + if(mMeidaListener != null) { + mMeidaListener.onPrepared(); + } + } + + /** 视频播放结束回调 */ + @Override + public void onCompletion(MediaPlayer mp) { + LogPrintUtils.dTag(TAG, "onCompletion"); + // 触发回调 + if(mMeidaListener != null) { + mMeidaListener.onCompletion(); + } + } + + /** MediaPlayer 缓冲更新回调 */ + @Override + public void onBufferingUpdate(MediaPlayer mp, int percent) { + LogPrintUtils.dTag(TAG, "onBufferingUpdate - percent: " + percent); + // 触发回调 + if(mMeidaListener != null) { + mMeidaListener.onBufferingUpdate(percent); + } + } + + /** 滑动加载完成回调 */ + @Override + public void onSeekComplete(MediaPlayer mp) { + LogPrintUtils.dTag(TAG, "onSeekComplete"); + // 触发回调 + if(mMeidaListener != null) { + mMeidaListener.onSeekComplete(); + } + } + + // =============== 封装回调事件 ================= + /** MediaPlayer回调事件 */ + private MediaListener mMeidaListener; + + /** + * detail: MediaPlayer回调接口 + * Created by Ttt + */ + public interface MediaListener { + + /** 使用 mMediaPlayer.prepareAsync(); 异步播放准备成功回调 */ + void onPrepared(); + + /** 视频播放结束回调 */ + void onCompletion(); + + /** + * MediaPlayer 缓冲更新回调 + * @param percent + */ + void onBufferingUpdate(int percent); + + /** 滑动加载完成回调 */ + void onSeekComplete(); + + /** + * 播放出错回调 + * @param what + * @param extra + */ + void onError(int what, int extra); + + /** + * 视频大小改变回调 + * @param width + * @param height + */ + void onVideoSizeChanged(int width, int height); + } + + /** + * 设置MediaPlayer回调 + * @param mMeidaListener + */ + public void setMeidaListener(MediaListener mMeidaListener) { + this.mMeidaListener = mMeidaListener; + } + + /** + * detail: 播放 设置 + * Created by Ttt + */ + public static abstract class MediaSet { + + /** + * 是否循环播放 - 默认不循环 + * @return + */ + public boolean isLooping() { + return false; + } + + /** + * 获取播放音量(设置) - 默认使用全局统一音量 + * @return + */ + public float getVolume() { + return DevMediaManager.getInstance().getVolume(); + } + + /** + * 设置播放配置 uri等 + * @param mediaPlayer + */ + public abstract void setMediaConfig(MediaPlayer mediaPlayer) throws Exception; + } + + // =============== get/set方法 ================= + + /** + * 判断 MediaPlayer 是否为null + * @return + */ + public boolean isNullMediaPlayer() { + return mMediaPlayer == null; + } + + /** + * 判断 MediaPlayer 是否不为null + * @return + */ + public boolean isNotNullMediaPlayer() { + return mMediaPlayer != null; + } + + /** + * 获取 MediaPlayer 对象 + * @return + */ + public MediaPlayer getMediaPlayer() { + return mMediaPlayer; + } + + /** + * 设置 MediaPlayer 对象 + * @param mMediaPlayer + */ + public void setMediaPlayer(MediaPlayer mMediaPlayer) { + this.mMediaPlayer = mMediaPlayer; + } + + /** + * 设置Tag打印 + * @param tag + */ + public void setTAG(String tag) { + TAG = tag; + } + + /** + * 获取播放音量 + * @return + */ + public float getVolume() { + return mVolume; + } + + /** + * 设置播放音量 + * @param volume + */ + public void setVolume(float volume) { + this.mVolume = volume; + } + + // =============== 内部属性 ================= + + /** 本地资源 */ + private @RawRes int mPlayRawId = -1; + /** 播放路径/地址 */ + private String mPlayUri = null; + /** 视频宽度 */ + private int mVideoWidth = 0; + /** 视频高度 */ + private int mVideoHeight = 0; + /** 播放音量 */ + private float mVolume = -1f; + + /** 清空播放信息 */ + private void clearMPlayerData() { + mPlayRawId = -1; + mPlayUri = null; + mVideoWidth = 0; + mVideoHeight = 0; + } + + /** + * 获取播放的资源id + * @return + */ + 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; + } +} diff --git a/DevLibUtils/src/main/java/dev/utils/app/player/DevVideoPlayerControl.java b/DevLibUtils/src/main/java/dev/utils/app/player/DevVideoPlayerControl.java new file mode 100644 index 0000000000..715a65c136 --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/player/DevVideoPlayerControl.java @@ -0,0 +1,354 @@ +package dev.utils.app.player; + +import android.media.MediaPlayer; +import android.text.TextUtils; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +import dev.utils.LogPrintUtils; + +/** + * detail: 视频播放控制器 + * Created by 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 autoPlay = false; + + /** + * 初始化构造函数 + * @param surfaceview + */ + public DevVideoPlayerControl(SurfaceView surfaceview) { + this(surfaceview, false); + } + + /** + * 初始化构造函数 + * @param surfaceview + * @param autoPlay + */ + public DevVideoPlayerControl(SurfaceView surfaceview, boolean autoPlay) { + this.mSurfaceview = surfaceview; + this.autoPlay = 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 改变通知 */ + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + LogPrintUtils.dTag(TAG, "surfaceChanged -> format: " + format + ", width: " + width + ", height: " + height); + } + + /** surface 创建 */ + @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 销毁 */ + @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 (autoPlay) { + 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(); + } + } + + /** 缓存进度 */ + @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(); + } + } + + /** 异常回调 */ + @Override + public void onError(int what, int extra) { + LogPrintUtils.dTag(TAG, "onError -> what: " + what + ", extra: " + extra); + // 触发回调 + if (mMediaListener != null) { + mMediaListener.onError(what, extra); + } + } + + /** 视频大小改变通知 */ + @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 + */ + public void setMediaListener(DevMediaManager.MediaListener mediaListener) { + this.mMediaListener = mediaListener; + } + + // ================== 播放快捷操作 ====================== + + /** 暂停播放 */ + public void pausePlayer() { + DevMediaManager.getInstance().pause(); + } + + /** 停止播放 */ + public void stopPlayer() { + DevMediaManager.getInstance().stop(); + } + + /** + * 开始播放 + * @param playUri 播放地址 + * @return + */ + public void startPlayer(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(DevMediaManager.MediaSet mediaSet) { + // 设置播放信息 + this.mMediaSet = mediaSet; + // 重置操作 + resetOperate(); + } + + /** + * 获取显示的SurfaceView + * @return + */ + public SurfaceView getSurfaceview() { + return mSurfaceview; + } + + // == + + /** + * 是否播放中 + * @return + */ + public boolean isPlaying() { + return DevMediaManager.getInstance().isPlaying(); + } + + /** + * 是否播放中 + * @param uri 播放地址 + * @return + */ + public boolean isPlaying(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 + */ + public boolean isAutoPlay() { + return autoPlay; + } + + /** + * 设置自动播放 + * @param autoPlay + */ + public void setAutoPlay(boolean autoPlay) { + this.autoPlay = autoPlay; + } + + /** + * 获取当前播放的地址 + * @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(); + } +} diff --git a/DevLibUtils/src/main/java/dev/utils/app/share/PreferenceImpl.java b/DevLibUtils/src/main/java/dev/utils/app/share/PreferenceImpl.java index 0f7b6cd2f8..952eff4c03 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/share/PreferenceImpl.java +++ b/DevLibUtils/src/main/java/dev/utils/app/share/PreferenceImpl.java @@ -171,7 +171,7 @@ public T get(String key, DataType type) { @Override public List getAll(String key) { - List list = new ArrayList(); + List list = new ArrayList<>(); Set set = get(key, DataType.STRING_SET); for(String value : set) { list.add(value); diff --git a/DevLibUtils/src/main/java/dev/utils/app/share/SPUtils.java b/DevLibUtils/src/main/java/dev/utils/app/share/SPUtils.java index 7293f102a6..a2cece767b 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/share/SPUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/share/SPUtils.java @@ -6,6 +6,6 @@ */ public final class SPUtils extends IPreferenceHolder { - private SPUtils(){ + private SPUtils() { } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/share/SharedUtils.java b/DevLibUtils/src/main/java/dev/utils/app/share/SharedUtils.java index ab95be05a0..20df22363d 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/share/SharedUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/share/SharedUtils.java @@ -13,10 +13,10 @@ */ public final class SharedUtils { - private SharedUtils(){ + private SharedUtils() { } - /** 全局上下文 */ + /** 全局 Context */ private static Context mContext; /** @@ -25,81 +25,165 @@ private SharedUtils(){ */ public static void init(Context context) { if (mContext == null && context != null) { - // 初始化全局上下文 + // 初始化全局 Context mContext = context.getApplicationContext(); } } - + + /** + * 保存一个数据 + * @param key + * @param value + */ public static void put(String key, T value) { SPUtils.getPreference(mContext).put(key, value); } + /** + * 保存一个Map集合(只能是 Integer,Long,Boolean,Float,String,Set) + * @param map + */ public static void putAll(Map map) { SPUtils.getPreference(mContext).putAll(map); } + /** + * 保存一个List集合 + * @param key + * @param list + */ public static void putAll(String key, List list) { SPUtils.getPreference(mContext).putAll(key, list); } + /** + * 保存一个List集合,并且自定义保存顺序 + * @param key + * @param list + * @param comparator + */ public static void putAll(String key, List list, Comparator comparator) { SPUtils.getPreference(mContext).putAll(key, list, comparator); } - + + /** + * 根据key取出一个数据 + * @param key + */ public static T get(String key, IPreference.DataType type) { return SPUtils.getPreference(mContext).get(key, type); } - + + /** + * 取出全部数据 + * @return + */ public static Map getAll() { return SPUtils.getPreference(mContext).getAll(); } - + + /** + * 取出一个List集合 + * @param key + * @return + */ public static List getAll(String key) { return SPUtils.getPreference(mContext).getAll(key); } - + + /** + * 移除一个数据 + * @param key + * @return + */ public static void remove(String key) { SPUtils.getPreference(mContext).remove(key); } - + + /** + * 移除一个集合的数据 + * @param keys + * @return + */ public static void removeAll(List keys) { SPUtils.getPreference(mContext).removeAll(keys); } - + + /** + * 移除一个数组的数据 + * @param keys + * @return + */ public static void removeAll(String[] keys) { SPUtils.getPreference(mContext).removeAll(keys); } - + + /** + * 是否存在key + * @return + */ public static boolean contains(String key) { return SPUtils.getPreference(mContext).contains(key); } - + + /** + * 清除全部数据 + */ public static void clear() { SPUtils.getPreference(mContext).clear(); } // == - + + /** + * 获取int类型的数据 + * @return + */ public static int getInt(String key) { return SPUtils.getPreference(mContext).getInt(key); } - + + /** + * 获取Float类型的数据 + * @param key + * @return + */ public static float getFloat(String key) { return SPUtils.getPreference(mContext).getFloat(key); } - + + /** + * 获取long类型的数据 + * @param key + * @return + */ public static long getLong(String key) { return SPUtils.getPreference(mContext).getLong(key); } - + + /** + * 获取boolean类型的数据 + * @param key + * @return + */ public static boolean getBoolean(String key) { return SPUtils.getPreference(mContext).getBoolean(key); } - + + /** + * 获取String类型的数据 + * @param key + * @return + */ public static String getString(String key) { return SPUtils.getPreference(mContext).getString(key); } - + + /** + * 获取Set类型的数据 + * @param key + * @return + */ public static Set getSet(String key) { return SPUtils.getPreference(mContext).getSet(key); } diff --git a/DevLibUtils/src/main/java/dev/utils/app/toast/ToastTintUtils.java b/DevLibUtils/src/main/java/dev/utils/app/toast/ToastTintUtils.java new file mode 100644 index 0000000000..6e7bf96e89 --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/toast/ToastTintUtils.java @@ -0,0 +1,1288 @@ +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 工具类 + * Created by Ttt + * tips: + * 支持子线程弹出 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 mToast = null; + // 判断是否使用 Handler + private static boolean mIsHandler = true; + // 内部 Handler + private static final Handler sHandler = new Handler(Looper.getMainLooper()); + // Null 值 + private static String mNullText = "text is null"; + // == 部分配置 == + // 判断是否使用配置 + private static boolean mUseConfig = true; + // Toast 的重心、X、Y 轴偏移 + private static int mGravity, mX, mY; + // 水平边距、垂直边距 + private static float mHorizontalMargin, mVerticalMargin; + // == 样式相关 == + // 默认样式 + private static ToastTintUtils.Style defaultStyle = new DefaultStyle(); + // Normal 样式 + private static ToastTintUtils.Style normalStyle = new NormalStyle(); + // Info 样式 + private static ToastTintUtils.Style infoStyle = new InfoStyle(); + // Warning 样式 + private static ToastTintUtils.Style warningStyle = new WarningStyle(); + // Error 样式 + private static ToastTintUtils.Style errorStyle = new ErrorStyle(); + // Success 样式 + private static ToastTintUtils.Style successStyle = new SuccessStyle(); + // = + // info icon + private static Drawable infoDrawable = null; + // warning icon + private static Drawable warningDrawable = null; + // error icon + private static Drawable errorDrawable = null; + // Success icon + private static Drawable successDrawable = null; + + /** + * 重置默认参数 + */ + public static void reset() { + mIsHandler = true; + mUseConfig = true; + mNullText = "text is null"; + mGravity = mX = mY = 0; + mHorizontalMargin = mVerticalMargin = 0.0f; + } + + /** + * 设置 Toast 过滤器 + * @param toastFilter + */ + public static void setToastFilter(ToastTintUtils.Filter toastFilter) { + ToastTintUtils.sToastFilter = toastFilter; + } + + /** + * 设置是否使用 Handler 显示 Toast + * @param isHandler + */ + public static void setIsHandler(boolean isHandler) { + ToastTintUtils.mIsHandler = isHandler; + } + + /** + * 设置 Text 为 null 的文本 + * @param nullText + */ + public static void setNullText(String nullText) { + ToastTintUtils.mNullText = nullText; + } + + /** + * 判断是否使用配置 + * @param useConfig + */ + public static void setUseConfig(boolean useConfig) { + ToastTintUtils.mUseConfig = useConfig; + } + + /** + * 设置 Toast 显示在屏幕上的位置。 + * @param gravity + * @param xOffset + * @param yOffset + */ + public static void setGravity(int gravity, int xOffset, int yOffset) { + ToastTintUtils.mGravity = gravity; + ToastTintUtils.mX = xOffset; + ToastTintUtils.mY = yOffset; + } + + /** + * 设置边距 + * @param horizontalMargin + * @param verticalMargin + */ + public static void setMargin(float horizontalMargin, float verticalMargin) { + ToastTintUtils.mHorizontalMargin = horizontalMargin; + ToastTintUtils.mVerticalMargin = verticalMargin; + } + + // = + + /** + * 获取默认样式 + * @return + */ + public static Style getDefaultStyle() { + return defaultStyle; + } + + /** + * 获取 Normal 样式 + * @return + */ + public static Style getNormalStyle() { + return normalStyle; + } + + /** + * 获取 Info 样式 + * @return + */ + public static Style getInfoStyle() { + return infoStyle; + } + + /** + * 获取 Warning 样式 + * @return + */ + public static Style getWarningStyle() { + return warningStyle; + } + + /** + * 获取 Error 样式 + * @return + */ + public static Style getErrorStyle() { + return errorStyle; + } + + /** + * 获取 Success 样式 + * @return + */ + public static Style getSuccessStyle() { + return successStyle; + } + + // = + + /** + * 设置 Normal 样式 + * @param normalStyle + */ + public static void setNormalStyle(Style normalStyle) { + ToastTintUtils.normalStyle = normalStyle; + } + + /** + * 设置 Info 样式 + * @param infoStyle + */ + public static void setInfoStyle(Style infoStyle) { + ToastTintUtils.infoStyle = infoStyle; + } + + /** + * 设置 Warning 样式 + * @param warningStyle + */ + public static void setWarningStyle(Style warningStyle) { + ToastTintUtils.warningStyle = warningStyle; + } + + /** + * 设置 Error 样式 + * @param errorStyle + */ + public static void setErrorStyle(Style errorStyle) { + ToastTintUtils.errorStyle = errorStyle; + } + + /** + * 设置 Success 样式 + * @param successStyle + */ + public static void setSuccessStyle(Style successStyle) { + ToastTintUtils.successStyle = successStyle; + } + + /** + * 获取 Info 样式 icon + * @return + */ + public static Drawable getInfoDrawable() { + if (infoDrawable != null) { + return infoDrawable; + } + infoDrawable = getDrawable(DevUtils.getContext(), R.drawable.dev_toast_icon_info_white); + return infoDrawable; + } + + /** + * 获取 Warning 样式 icon + * @return + */ + public static Drawable getWarningDrawable() { + if (warningDrawable != null) { + return warningDrawable; + } + warningDrawable = getDrawable(DevUtils.getContext(), R.drawable.dev_toast_icon_warning_white); + return warningDrawable; + } + + /** + * 获取 Error 样式 icon + * @return + */ + public static Drawable getErrorDrawable() { + if (errorDrawable != null) { + return errorDrawable; + } + errorDrawable = getDrawable(DevUtils.getContext(), R.drawable.dev_toast_icon_error_white); + return errorDrawable; + } + + /** + * 获取 Success 样式 icon + * @return + */ + public static Drawable getSuccessDrawable() { + if (successDrawable != null) { + return successDrawable; + } + successDrawable = getDrawable(DevUtils.getContext(), R.drawable.dev_toast_icon_success_white); + return successDrawable; + } + + // ==================== + // ==== 显示 Toast ==== + // ==================== + + // === normal === + + /** normal 样式 Toast */ + public static void normal(String text) { + custom(true, null, normalStyle, text, Toast.LENGTH_SHORT, null); + } + + /** normal 样式 Toast */ + public static void normal(String text, int duration) { + custom(true, null, normalStyle, text, duration, null); + } + + /** normal 样式 Toast */ + public static void normal(String text, Drawable icon) { + custom(true, null, normalStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** normal 样式 Toast */ + public static void normal(String text, int duration, Drawable icon) { + custom(true, null, normalStyle, text, duration, icon); + } + + // = + + /** normal 样式 Toast */ + public static void normal(boolean isSingle, String text) { + custom(isSingle, null, normalStyle, text, Toast.LENGTH_SHORT, null); + } + + /** normal 样式 Toast */ + public static void normal(boolean isSingle, String text, int duration) { + custom(isSingle, null, normalStyle, text, duration, null); + } + + /** normal 样式 Toast */ + public static void normal(boolean isSingle, String text, Drawable icon) { + custom(isSingle, null, normalStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** normal 样式 Toast */ + public static void normal(boolean isSingle, String text, int duration, Drawable icon) { + custom(isSingle, null, normalStyle, text, duration, icon); + } + +// // = +// +// public static void normal(Context context, String text) { +// custom(true, context, normalStyle, text, Toast.LENGTH_SHORT, null); +// } +// +// public static void normal(Context context, String text, int duration) { +// custom(true, context, normalStyle, text, duration, null); +// } +// +// public static void normal(Context context, String text, Drawable icon) { +// custom(true, context, normalStyle, text, Toast.LENGTH_SHORT, icon); +// } +// +// public static void normal(Context context, String text, int duration, Drawable icon) { +// custom(true, context, normalStyle, text, duration, icon); +// } +// +// // = +// +// public static void normal(boolean isSingle, Context context, String text) { +// custom(isSingle, context, normalStyle, text, Toast.LENGTH_SHORT, null); +// } +// +// public static void normal(boolean isSingle, Context context, String text, int duration) { +// custom(isSingle, context, normalStyle, text, duration, null); +// } +// +// public static void normal(boolean isSingle, Context context, String text, Drawable icon) { +// custom(isSingle, context, normalStyle, text, Toast.LENGTH_SHORT, icon); +// } +// +// public static void normal(boolean isSingle, Context context, String text, int duration, Drawable icon) { +// custom(isSingle, context, normalStyle, text, Toast.LENGTH_SHORT, icon); +// } + + // === info === + + /** info 样式 Toast */ + public static void info(String text) { + custom(true, null, infoStyle, text, Toast.LENGTH_SHORT, getInfoDrawable()); + } + + /** info 样式 Toast */ + public static void info(String text, int duration) { + custom(true, null, infoStyle, text, duration, getInfoDrawable()); + } + + /** info 样式 Toast */ + public static void info(String text, Drawable icon) { + custom(true, null, infoStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** info 样式 Toast */ + public static void info(String text, int duration, Drawable icon) { + custom(true, null, infoStyle, text, duration, icon); + } + + // = + + /** info 样式 Toast */ + public static void info(boolean isSingle, String text) { + custom(isSingle, null, infoStyle, text, Toast.LENGTH_SHORT, getInfoDrawable()); + } + + /** info 样式 Toast */ + public static void info(boolean isSingle, String text, int duration) { + custom(isSingle, null, infoStyle, text, duration, getInfoDrawable()); + } + + /** info 样式 Toast */ + public static void info(boolean isSingle, String text, Drawable icon) { + custom(isSingle, null, infoStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** info 样式 Toast */ + public static void info(boolean isSingle, String text, int duration, Drawable icon) { + custom(isSingle, null, infoStyle, text, duration, icon); + } + + // === warning === + + /** warning 样式 Toast */ + public static void warning(String text) { + custom(true, null, warningStyle, text, Toast.LENGTH_SHORT, getWarningDrawable()); + } + + /** warning 样式 Toast */ + public static void warning(String text, int duration) { + custom(true, null, warningStyle, text, duration, getWarningDrawable()); + } + + /** warning 样式 Toast */ + public static void warning(String text, Drawable icon) { + custom(true, null, warningStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** warning 样式 Toast */ + public static void warning(String text, int duration, Drawable icon) { + custom(true, null, warningStyle, text, duration, icon); + } + + // = + + /** warning 样式 Toast */ + public static void warning(boolean isSingle, String text) { + custom(isSingle, null, warningStyle, text, Toast.LENGTH_SHORT, getWarningDrawable()); + } + + /** warning 样式 Toast */ + public static void warning(boolean isSingle, String text, int duration) { + custom(isSingle, null, warningStyle, text, duration, getWarningDrawable()); + } + + /** warning 样式 Toast */ + public static void warning(boolean isSingle, String text, Drawable icon) { + custom(isSingle, null, warningStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** warning 样式 Toast */ + public static void warning(boolean isSingle, String text, int duration, Drawable icon) { + custom(isSingle, null, warningStyle, text, duration, icon); + } + + // === error === + + /** error 样式 Toast */ + public static void error(String text) { + custom(true, null, errorStyle, text, Toast.LENGTH_SHORT, getErrorDrawable()); + } + + /** error 样式 Toast */ + public static void error(String text, int duration) { + custom(true, null, errorStyle, text, duration, getErrorDrawable()); + } + + /** error 样式 Toast */ + public static void error(String text, Drawable icon) { + custom(true, null, errorStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** error 样式 Toast */ + public static void error(String text, int duration, Drawable icon) { + custom(true, null, errorStyle, text, duration, icon); + } + + // = + + /** error 样式 Toast */ + public static void error(boolean isSingle, String text) { + custom(isSingle, null, errorStyle, text, Toast.LENGTH_SHORT, getErrorDrawable()); + } + + /** error 样式 Toast */ + public static void error(boolean isSingle, String text, int duration) { + custom(isSingle, null, errorStyle, text, duration, getErrorDrawable()); + } + + /** error 样式 Toast */ + public static void error(boolean isSingle, String text, Drawable icon) { + custom(isSingle, null, errorStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** error 样式 Toast */ + public static void error(boolean isSingle, String text, int duration, Drawable icon) { + custom(isSingle, null, errorStyle, text, duration, icon); + } + + // === success === + + /** success 样式 Toast */ + public static void success(String text) { + custom(true, null, successStyle, text, Toast.LENGTH_SHORT, getSuccessDrawable()); + } + + /** success 样式 Toast */ + public static void success(String text, int duration) { + custom(true, null, successStyle, text, duration, getSuccessDrawable()); + } + + /** success 样式 Toast */ + public static void success(String text, Drawable icon) { + custom(true, null, successStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** success 样式 Toast */ + public static void success(String text, int duration, Drawable icon) { + custom(true, null, successStyle, text, duration, icon); + } + + // = + + /** success 样式 Toast */ + public static void success(boolean isSingle, String text) { + custom(isSingle, null, successStyle, text, Toast.LENGTH_SHORT, getSuccessDrawable()); + } + + /** success 样式 Toast */ + public static void success(boolean isSingle, String text, int duration) { + custom(isSingle, null, successStyle, text, duration, getSuccessDrawable()); + } + + /** success 样式 Toast */ + public static void success(boolean isSingle, String text, Drawable icon) { + custom(isSingle, null, successStyle, text, Toast.LENGTH_SHORT, icon); + } + + /** success 样式 Toast */ + public static void success(boolean isSingle, String text, int duration, Drawable icon) { + custom(isSingle, null, successStyle, text, duration, icon); + } + + // === custom === + + /** custom Toast */ + public static void custom(ToastTintUtils.Style style, String text) { + custom(true, null, style, text, Toast.LENGTH_SHORT, null); + } + + /** custom Toast */ + public static void custom(ToastTintUtils.Style style, String text, int duration) { + custom(true, null, style, text, duration, null); + } + + /** custom Toast */ + public static void custom(ToastTintUtils.Style style, String text, Drawable icon) { + custom(true, null, style, text, Toast.LENGTH_SHORT, icon); + } + + /** custom Toast */ + public static void custom(ToastTintUtils.Style style, String text, int duration, Drawable icon) { + custom(true, null, style, text, duration, icon); + } + + // = + + /** custom Toast */ + public static void custom(boolean isSingle, ToastTintUtils.Style style, String text) { + custom(isSingle, null, style, text, Toast.LENGTH_SHORT, null); + } + + /** custom Toast */ + public static void custom(boolean isSingle, ToastTintUtils.Style style, String text, int duration) { + custom(isSingle, null, style, text, duration, null); + } + + /** custom Toast */ + public static void custom(boolean isSingle, ToastTintUtils.Style style, String text, Drawable icon) { + custom(isSingle, null, style, text, Toast.LENGTH_SHORT, icon); + } + + /** custom Toast */ + public static void custom(boolean isSingle, ToastTintUtils.Style style, String text, int duration, Drawable icon) { + custom(isSingle, null, style, text, duration, icon); + } + + // = + + /** custom Toast */ + public static void custom(Context context, ToastTintUtils.Style style, String text) { + custom(true, context, style, text, Toast.LENGTH_SHORT, null); + } + + /** custom Toast */ + public static void custom(Context context, ToastTintUtils.Style style, String text, int duration) { + custom(true, context, style, text, duration, null); + } + + /** custom Toast */ + public static void custom(Context context, ToastTintUtils.Style style, String text, Drawable icon) { + custom(true, context, style, text, Toast.LENGTH_SHORT, icon); + } + + /** custom Toast */ + public static void custom(Context context, ToastTintUtils.Style style, String text, int duration, Drawable icon) { + custom(true, context, style, text, duration, icon); + } + + // = + + /** custom Toast */ + public static void custom(boolean isSingle, Context context, ToastTintUtils.Style style, String text) { + custom(isSingle, context, style, text, Toast.LENGTH_SHORT, null); + } + + /** custom Toast */ + public static void custom(boolean isSingle, Context context, ToastTintUtils.Style style, String text, int duration) { + custom(isSingle, context, style, text, duration, null); + } + + /** custom Toast */ + public static void custom(boolean isSingle, Context context, ToastTintUtils.Style style, String text, Drawable icon) { + custom(isSingle, context, style, text, Toast.LENGTH_SHORT, icon); + } + + /** + * custom Toast + * @param isSingle 是否单独显示 + * @param context + * @param style Toast 样式 + * @param text 文案 + * @param duration 显示时长 + * @param icon 图标 + */ + public static void custom(boolean isSingle, Context context, ToastTintUtils.Style style, String text, int duration, Drawable icon) { + // 获取View + View view = inflaterView(context, style, text, icon); + // 显示Toast + showToastView(isSingle, context, view, duration); + } + + // ==================== + // ==== 内部 Toast ==== + // ==================== + + /** + * 显示 View Toast 方法 + * @param isSingle + * @param context + * @param view + * @param duration + */ + private static void showToastView(final boolean isSingle, final Context context, final View view, final int duration) { + if (view == null) return; + if (mIsHandler) { + 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 + * @param context + * @param view + * @param duration + * @return + */ + private static Toast newToastView(boolean isSingle, Context context, View view, 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 (mToast != null) { + mToast.cancel(); + mToast = null; + } + // 解决 MIUI 会显示应用名称问题 + mToast = new Toast(context); + mToast.setView(view); + mToast.setDuration(duration); + // 判断是否使用配置 + if (mUseConfig) { + // 设置属性配置 + if (mGravity != 0) { + mToast.setGravity(mGravity, mX, mY); + } + mToast.setMargin(mHorizontalMargin, mVerticalMargin); + } + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(mToast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastView"); + } + return mToast; + } else { + Toast toast = null; + try { + // 解决 MIUI 会显示应用名称问题 + toast = new Toast(context); + toast.setView(view); + toast.setDuration(duration); + // 判断是否使用配置 + if (mUseConfig) { + // 设置属性配置 + if (mGravity != 0) { + toast.setGravity(mGravity, mX, mY); + } + toast.setMargin(mHorizontalMargin, mVerticalMargin); + } + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(toast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastView"); + } + return toast; + } + } + + /** + * 实例化 Layout View + * @param context + * @param style + * @param text + * @param icon + * @return + */ + private static View inflaterView(Context context, ToastTintUtils.Style style, String text, Drawable icon) { + if (context == null) { + context = DevUtils.getContext(); + } + // 如果样式为 null, 则不处理 + if (style == null) { + return null; + } + // 判断是否过滤 + if (!sPriToastFilter.filter(text)){ + return null; + } + // 处理内容 + text = sPriToastFilter.handlerContent(text); + // 设置为null, 便于提示排查 + if (TextUtils.isEmpty(text)) { + text = mNullText; + // 如果还是为null, 则不处理 + if (TextUtils.isEmpty(text)) { + return null; + } + } + if (context != null) { + try { + // 引入View + final View toastLayout = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).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(text); + // 设置字体颜色 + 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"); + } + } + // 默认返回null + return null; + } + + // ============== + // == 内部方法 == + // ============== + + /** + * 设置背景 + * @param view + * @param drawable + */ + private static void setBackground(View view, 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 + * @param id + * @return + */ + private static Drawable getDrawable(Context context, @DrawableRes 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 + * @param tintColor + * @return + */ + private static Drawable tintIcon(Drawable drawable, @ColorInt 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 + * @param tintColor + * @return + */ + private static Drawable tint9PatchDrawableFrame(Context context, @ColorInt 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 + */ + private static void reflectToastHandler(Toast toast) { + if (toast == null) return; + // 反射设置 Toat Handler 解决 Android7.1.1Toast 崩溃 问题 + 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 + * Created by 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 着色等相关 样式配置 + * Created by Ttt + */ + public interface Style { + + /** + * 文本颜色 + * @return + */ + int getTextColor(); + + /** + * 字体大小 + * @return + */ + float getTextSize(); + + /** + * 背景着色颜色 + * @return + */ + int getBackgroundTintColor(); + + /** + * 背景图片 + * @return + */ + Drawable getBackground(); + + /** + * 最大行数 + * @return + */ + int getMaxLines(); + + /** + * Ellipsize 效果 + * @return + */ + TextUtils.TruncateAt getEllipsize(); + + /** + * 字体样式 + * @return + */ + Typeface getTypeface(); + + /** + * 获取图标着色颜色 + * @return + */ + int getTintIconColor(); + + /** + * 是否渲染图标 - getTintIconColor() 着色渲染 + * @return + */ + boolean isTintIcon(); + } + + // = + + /** + * detail: 默认样式 + * Created by 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 + */ + @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; + } + } + + /** + * detail: Normal 样式 - 灰色 + * Created by Ttt + */ + public static class NormalStyle extends DefaultStyle { + + /** 背景着色颜色 */ + @Override + public int getBackgroundTintColor() { + return Color.parseColor("#353A3E"); + } + + /** 是否渲染图标 - getTintIconColor() 着色渲染 */ + @Override + public boolean isTintIcon() { + return true; + } + } + + /** + * detail: Info 样式 - 海洋蓝 + * Created by Ttt + */ + public static class InfoStyle extends DefaultStyle { + + /** 背景着色颜色 */ + @Override + public int getBackgroundTintColor() { + return Color.parseColor("#3F51B5"); + } + + /** 是否渲染图标 - getTintIconColor() 着色渲染 */ + @Override + public boolean isTintIcon() { + return true; + } + } + + /** + * detail: Warning 样式 - 橙色 + * Created by Ttt + */ + public static class WarningStyle extends DefaultStyle { + + /** 背景着色颜色 */ + @Override + public int getBackgroundTintColor() { + return Color.parseColor("#FFA900"); + } + + /** 是否渲染图标 - getTintIconColor() 着色渲染 */ + @Override + public boolean isTintIcon() { + return true; + } + } + + /** + * detail: Error 样式 - 红色 + * Created by Ttt + */ + public static class ErrorStyle extends DefaultStyle { + + /** 背景着色颜色 */ + @Override + public int getBackgroundTintColor() { + return Color.parseColor("#D50000"); + } + + /** 是否渲染图标 - getTintIconColor() 着色渲染 */ + @Override + public boolean isTintIcon() { + return true; + } + } + + /** + * detail: Success 样式 - 绿色 + * Created by Ttt + */ + public static class SuccessStyle extends DefaultStyle { + + /** 背景着色颜色 */ + @Override + public int getBackgroundTintColor() { + return Color.parseColor("#388E3C"); + } + + /** 是否渲染图标 - getTintIconColor() 着色渲染 */ + @Override + public boolean isTintIcon() { + return true; + } + } + + + // == 其他接口 == + + /** + * detail: Toast 过滤器 + * Created by Ttt + */ + public interface Filter { + + /** + * 判断是否显示 + * @param content + * @return true: 接着执行, false: 过滤不处理 + */ + boolean filter(String content); + + /** + * 获取 Toast 显示的文案 + * @param content + * @return 处理后的内容 + */ + String handlerContent(String content); + } + + // === ToastTintUtils.Filter 实现方法 === + + /** + * 内部 Toast Filter 实现对象 + */ + private final static ToastTintUtils.Filter sPriToastFilter = new ToastTintUtils.Filter() { + + /** + * 判断是否显示 + * @param content + * @return true: 接着执行, false: 过滤不处理 + */ + @Override + public boolean filter(String content) { + if (sToastFilter != null) { + return sToastFilter.filter(content); + } + return true; + } + + /** + * 获取 Toast 显示的文案 + * @param content + * @return 处理后的内容 + */ + @Override + public String handlerContent(String content) { + if (sToastFilter != null) { + return sToastFilter.handlerContent(content); + } + return content; + } + }; +} diff --git a/DevLibUtils/src/main/java/dev/utils/app/toast/ToastUtils.java b/DevLibUtils/src/main/java/dev/utils/app/toast/ToastUtils.java index 39f61355b6..56e26c1ccd 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/toast/ToastUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/toast/ToastUtils.java @@ -1,14 +1,26 @@ 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.text.TextUtils; +import android.view.View; import android.widget.Toast; +import java.lang.reflect.Field; + import dev.DevUtils; import dev.utils.LogPrintUtils; /** - * detail: 自定义Toast工具类 + * detail: Simple Toast 工具类(简单的 Toast 工具类, 支持子线程弹出 Toast) * Created by Ttt + * tips: + * 支持子线程弹出 Toast, 可通过开关配置 + * 内部解决 Android 7.1.1 崩溃问题 + * 但无处理 部分ROM 如魅族、小米、三星等关闭应用通知,无法显示 Toast 问题 */ public final class ToastUtils { @@ -17,29 +29,87 @@ private ToastUtils() { // 日志TAG private static final String TAG = ToastUtils.class.getSimpleName(); - - /** 内部持有唯一 */ + // Toast 判断过滤 + private static ToastUtils.Filter sToastFilter = null; + // 内部持有单个Toast private static Toast mToast = null; + // 判断是否使用 Handler + private static boolean mIsHandler = true; + // 内部 Handler + private static final Handler sHandler = new Handler(Looper.getMainLooper()); + // Null 值 + private static String mNullText = "text is null"; + // == 部分配置 == + // 判断是否使用配置 + private static boolean mUseConfig = true; + // Toast 的重心、X、Y 轴偏移 + private static int mGravity, mX, mY; + // 水平边距、垂直边距 + private static float mHorizontalMargin, mVerticalMargin; /** - * 内部处理防止Context 为null奔溃问题 - * @return + * 重置默认参数 */ - private static Context getContext(Context context){ - if (context != null){ - return context; - } else { - // 设置全局Context - return DevUtils.getContext(); - } + public static void reset() { + mIsHandler = true; + mUseConfig = true; + mNullText = "text is null"; + mGravity = mX = mY = 0; + mHorizontalMargin = mVerticalMargin = 0.0f; } /** - * 获取内部唯一Toast对象 - * @return + * 设置 Toast 过滤器 + * @param toastFilter + */ + public static void setToastFilter(ToastUtils.Filter toastFilter) { + ToastUtils.sToastFilter = toastFilter; + } + + /** + * 设置是否使用 Handler 显示 Toast + * @param isHandler */ - public static Toast getSignleToast(){ - return mToast; + public static void setIsHandler(boolean isHandler) { + ToastUtils.mIsHandler = isHandler; + } + + /** + * 设置 Text 为 null 的文本 + * @param nullText + */ + public static void setNullText(String nullText) { + ToastUtils.mNullText = nullText; + } + + /** + * 判断是否使用配置 + * @param useConfig + */ + public static void setUseConfig(boolean useConfig) { + ToastUtils.mUseConfig = useConfig; + } + + /** + * 设置 Toast 显示在屏幕上的位置。 + * @param gravity + * @param xOffset + * @param yOffset + */ + public static void setGravity(int gravity, int xOffset, int yOffset) { + ToastUtils.mGravity = gravity; + ToastUtils.mX = xOffset; + ToastUtils.mY = yOffset; + } + + /** + * 设置边距 + * @param horizontalMargin + * @param verticalMargin + */ + public static void setMargin(float horizontalMargin, float verticalMargin) { + ToastUtils.mHorizontalMargin = horizontalMargin; + ToastUtils.mVerticalMargin = verticalMargin; } // ===================== @@ -50,113 +120,527 @@ public static Toast getSignleToast(){ // == Toast.LENGTH_SHORT == // ======================== - public static Toast showShort(Context context, String text) { - return handlerToastStr(true, context, text, Toast.LENGTH_SHORT); + /** + * 显示 LENGTH_SHORT Toast + * @param text + * @param objs + */ + public static void showShort(String text, Object... objs) { + showShort(null, text, objs); } - public static Toast showShort(Context context, String text, Object... objs) { - return handlerToastStr(true, context, text, Toast.LENGTH_SHORT, objs); + /** + * 显示 LENGTH_SHORT Toast + * @param context + * @param text + * @param objs + */ + public static void showShort(Context context, String text, Object... objs) { + handlerToastStr(true, context, text, Toast.LENGTH_SHORT, objs); } - public static Toast showShort(Context context, int resId) { - return handlerToastRes(true, context, resId, Toast.LENGTH_SHORT); + // = + + /** + * * 显示 LENGTH_SHORT Toast + * @param resId + * @param objs + */ + public static void showShort(int resId, Object... objs) { + showShort(null, resId, objs); } - public static Toast showShort(Context context, int resId, Object... objs) { - return handlerToastRes(true, context, resId, Toast.LENGTH_SHORT, objs); + /** + * * 显示 LENGTH_SHORT Toast + * @param context + * @param resId + * @param objs + */ + public static void showShort(Context context, int resId, Object... objs) { + 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); + /** + * 显示 LENGTH_LONG Toast + * @param text + * @param objs + */ + public static void showLong(String text, Object... objs) { + showLong(null, text, objs); + } + + /** + * 显示 LENGTH_LONG Toast + * @param context + * @param text + * @param objs + */ + public static void showLong(Context context, String text, Object... objs) { + handlerToastStr(true, context, text, Toast.LENGTH_LONG, objs); + } + + // = + + /** + * 显示 LENGTH_LONG Toast + * @param resId + * @param objs + */ + public static void showLong(int resId, Object... objs) { + showLong(null, resId, objs); } - public static Toast showLong(Context context, String text, Object... objs) { - return handlerToastStr(true, context, text, Toast.LENGTH_LONG, objs); + /** + * 显示 LENGTH_LONG Toast + * @param context + * @param resId + * @param objs + */ + public static void showLong(Context context, int resId, Object... objs) { + handlerToastRes(true, context, resId, Toast.LENGTH_LONG, objs); } - public static Toast showLong(Context context, int resId) { - return handlerToastRes(true, context, resId, Toast.LENGTH_LONG); + // =================== + // ==== Toast方法 ==== + // =================== + + /** + * 显示 Toast + * @param resId + * @param duration + */ + public static void showToast(int resId, int duration) { + showToast(null, resId, duration); } - public static Toast showLong(Context context, int resId, Object...objs) { - return handlerToastRes(true, context, resId, Toast.LENGTH_LONG, objs); + /** + * 显示 Toast + * @param context + * @param resId + * @param duration + */ + public static void showToast(Context context, int resId, int duration) { + handlerToastRes(true, context, resId, duration); + } + + /** + * 显示 Toast + * @param text + * @param duration + */ + public static void showToast(String text, int duration) { + priShowToastText(true, null, text, duration); + } + + /** + * 显示 Toast + * @param context + * @param text + * @param duration + */ + public static void showToast(Context context, String text, int duration) { + priShowToastText(true, context, text, duration); + } + + // ===================== + // == 非统一显示Toast == + // ===================== + + // ======================== + // == Toast.LENGTH_SHORT == + // ======================== + + /** + * 显示 new LENGTH_SHORT Toast + * @param text + * @param objs + */ + public static void showShortNew(String text, Object... objs) { + showShortNew(null, text, objs); + } + + /** + * 显示 new LENGTH_SHORT Toast + * @param context + * @param text + * @param objs + */ + public static void showShortNew(Context context, String text, Object... objs) { + handlerToastStr(false, context, text, Toast.LENGTH_SHORT, objs); + } + + // = + + /** + * 显示 new LENGTH_SHORT Toast + * @param resId + * @param objs + */ + public static void showShortNew(int resId, Object... objs) { + showShortNew(null, resId, objs); + } + + /** + * 显示 new LENGTH_SHORT Toast + * @param context + * @param resId + * @param objs + */ + public static void showShortNew(Context context, int resId, Object... objs) { + handlerToastRes(false, context, resId, Toast.LENGTH_SHORT, objs); + } + + // ======================== + // == Toast.LENGTH_LONG === + // ======================== + + /** + * 显示 new LENGTH_LONG Toast + * @param text + * @param objs + */ + public static void showLongNew(String text, Object... objs) { + showLongNew(null, text, objs); + } + + /** + * 显示 new LENGTH_LONG Toast + * @param context + * @param text + * @param objs + */ + public static void showLongNew(Context context, String text, Object... objs) { + handlerToastStr(false, context, text, Toast.LENGTH_LONG, objs); + } + + // = + + /** + * 显示 new LENGTH_LONG Toast + * @param resId + * @param objs + */ + public static void showLongNew(int resId, Object... objs) { + showLongNew(null, resId, objs); + } + + /** + * 显示 new LENGTH_LONG Toast + * @param context + * @param resId + * @param objs + */ + public static void showLongNew(Context context, int resId, Object... objs) { + handlerToastRes(false, context, resId, Toast.LENGTH_LONG, objs); } - // ======================= - // ==== 最终Toast方法 ==== - // ======================= + // =================== + // ==== Toast方法 ==== + // =================== + + /** + * 显示新的 Toast + * @param resId + * @param duration + */ + public static void showToastNew(int resId, int duration) { + showToastNew(null, resId, duration); + } /** - * 显示Toast + * 显示新的 Toast * @param context * @param resId * @param duration */ - public static Toast showToast(Context context, int resId, int duration) { - return handlerToastRes(true, context, resId, duration); + public static void showToastNew(Context context, int resId, int duration) { + handlerToastRes(false, context, resId, duration); } /** - * 显示Toast + * 显示新的 Toast + * @param text + * @param duration + */ + public static void showToastNew(String text, int duration) { + priShowToastText(false, null, text, duration); + } + + /** + * 显示新的 Toast * @param context * @param text * @param duration */ - public static Toast showToast(Context context, String text, int duration) { - return showToast(true, context, text, duration); + public static void showToastNew(Context context, String text, int duration) { + priShowToastText(false, context, text, duration); } - // == + // ==================== + // ==== 显示 Toast ==== + // ==================== /** - * 最终显示的Toast方法 + * 内部私有方法, 最终显示 Toast * @param isSingle * @param context * @param text * @param duration - * @return Toast + * @Toast */ - private static Toast showToast(boolean isSingle, Context context, String text, int duration) { - // 尽心设置为null, 便于提示排查 - if (text == null) { - text = "null"; + private static void priShowToastText(final boolean isSingle, final Context context, final String text, final int duration) { + if (mIsHandler) { + 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 + * @param context + * @param text + * @param duration + * @return + */ + public static Toast newToastText(boolean isSingle, Context context, String text, int duration) { + if (context == null) { + context = DevUtils.getContext(); + } + // 判断是否过滤 + if (!sPriToastFilter.filter(text)){ + return null; + } + // 处理内容 + text = sPriToastFilter.handlerContent(text); + // 设置为null, 便于提示排查 + if (TextUtils.isEmpty(text)) { + text = mNullText; + // 如果还是为null, 则不处理 + if (TextUtils.isEmpty(text)) { + return null; + } } // 判断是否显示唯一, 单独共用一个 if (isSingle) { try { + // 关闭旧的 Toast if (mToast != null) { - mToast.setDuration(duration); - mToast.setText(text); - } else { - if (context != null) { - mToast = Toast.makeText(context, text, duration); + mToast.cancel(); + mToast = null; + } + // 解决 MIUI 会显示应用名称问题 + mToast = Toast.makeText(context, null, duration); + mToast.setText(text); + // 判断是否使用配置 + if (mUseConfig) { + // 设置属性配置 + if (mGravity != 0) { + mToast.setGravity(mGravity, mX, mY); } + mToast.setMargin(mHorizontalMargin, mVerticalMargin); } - // 处理后,不为null,才进行处理 + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(mToast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastText"); + } + return mToast; + } else { + Toast toast = null; + try { + // 解决 MIUI 会显示应用名称问题 + toast = Toast.makeText(context, null, duration); + toast.setText(text); + // 判断是否使用配置 + if (mUseConfig) { + // 设置属性配置 + if (mGravity != 0) { + toast.setGravity(mGravity, mX, mY); + } + toast.setMargin(mHorizontalMargin, mVerticalMargin); + } + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(toast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastText"); + } + return toast; + } + } + + // = 显示 View Toast = + + /** + * 显示 View Toast 方法 + * @param view + */ + public static void showToastView(View view) { + showToastView(true,null, view, Toast.LENGTH_SHORT); + } + + /** + * 显示 View Toast 方法 + * @param view + * @param duration + */ + public static void showToastView(View view, int duration) { + showToastView(true,null, view, duration); + } + + /** + * 显示 View Toast 方法 + * @param isSingle + * @param view + */ + public static void showToastView(boolean isSingle, View view) { + showToastView(isSingle,null, view, Toast.LENGTH_SHORT); + } + + /** + * 显示 View Toast 方法 + * @param isSingle + * @param view + * @param duration + */ + public static void showToastView(boolean isSingle, View view, int duration) { + showToastView(isSingle,null, view, duration); + } + + /** + * 显示 View Toast 方法 + * @param isSingle + * @param context + * @param view + * @param duration + */ + public static void showToastView(final boolean isSingle, final Context context, final View view, final int duration) { + if (view == null) return; + if (mIsHandler) { + 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 + * @param context + * @param view + * @param duration + * @return + */ + public static Toast newToastView(boolean isSingle, Context context, View view, 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 (mToast != null) { - mToast.show(); + mToast.cancel(); + mToast = null; + } + // 解决 MIUI 会显示应用名称问题 + mToast = new Toast(context); + mToast.setView(view); + mToast.setDuration(duration); + // 判断是否使用配置 + if (mUseConfig) { + // 设置属性配置 + if (mGravity != 0) { + mToast.setGravity(mGravity, mX, mY); + } + mToast.setMargin(mHorizontalMargin, mVerticalMargin); } - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "showToast"); + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(mToast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastView"); } return mToast; } else { Toast toast = null; try { - toast = Toast.makeText(context, text, duration); - toast.show(); - } catch (Exception e){ - LogPrintUtils.eTag(TAG, e, "showToast"); + // 解决 MIUI 会显示应用名称问题 + toast = new Toast(context); + toast.setView(view); + toast.setDuration(duration); + // 判断是否使用配置 + if (mUseConfig) { + // 设置属性配置 + if (mGravity != 0) { + toast.setGravity(mGravity, mX, mY); + } + toast.setMargin(mHorizontalMargin, mVerticalMargin); + } + // 反射 Hook Toast 解决 Android 7.1.1 崩溃问题 + reflectToastHandler(toast); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastView"); } return toast; } } - // ===== + // ==================== + // === 内部处理方法 === + // ==================== /** * 处理 R.string 资源Toast的格式化 @@ -166,23 +650,24 @@ private static Toast showToast(boolean isSingle, Context context, String text, i * @param duration * @param objs */ - private static Toast handlerToastRes(boolean isSingle, Context context, int resId, int duration, Object... objs) { - if (getContext(context) != null) { - String text; + private static void handlerToastRes(boolean isSingle, Context context, int resId, int duration, Object... objs) { + if (context == null) { + context = DevUtils.getContext(); + } + if (context != null) { + String text = null; try { // 获取字符串并且进行格式化 if (objs != null && objs.length != 0) { - text = getContext(context).getString(resId, objs); + text = context.getString(resId, objs); } else { - text = getContext(context).getString(resId); + text = context.getString(resId); } } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "handlerToastRes"); - text = e.getMessage(); } - return showToast(isSingle, context, text, duration); + priShowToastText(isSingle, context, text, duration); } - return null; } /** @@ -192,93 +677,157 @@ private static Toast handlerToastRes(boolean isSingle, Context context, int resI * @param duration * @param objs */ - private static Toast handlerToastStr(boolean isSingle, Context context, String text, int duration, Object... objs) { - // 防止上下文为null + private static void handlerToastStr(boolean isSingle, Context context, String text, int duration, 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 { - return showToast(isSingle, context, String.format(text, objs), duration); - } catch (Exception e){ + priShowToastText(isSingle, context, String.format(text, objs), duration); + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "handlerToastStr"); - return showToast(isSingle, context, e.getMessage(), duration); + priShowToastText(isSingle, context, e.getMessage(), duration); } } else { - return showToast(isSingle, context, text, duration); + priShowToastText(isSingle, context, text, duration); } } else { - return showToast(isSingle, context, text, duration); + priShowToastText(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); - } + // =============================== + // = 解决 Android 7.1.1 崩溃问题 = + // =============================== - public static Toast showShortNew(Context context, String text, Object... objs) { - return handlerToastStr(false, context, text, Toast.LENGTH_SHORT, objs); - } + /** + * 反射 Hook Toast 设置 Handler + * @param toast + */ + private static void reflectToastHandler(Toast toast) { + if (toast == null) return; + // 反射设置 Toat Handler 解决 Android7.1.1Toast 崩溃 问题 + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) { + try { + Field field_tn = Toast.class.getDeclaredField("mTN"); + field_tn.setAccessible(true); - public static Toast showShortNew(Context context, int resId) { - return handlerToastRes(false, context, resId, Toast.LENGTH_SHORT); - } + Object mTN = field_tn.get(toast); + Field field_handler = field_tn.getType().getDeclaredField("mHandler"); + field_handler.setAccessible(true); - public static Toast showShortNew(Context context, int resId, Object... objs) { - return handlerToastRes(false, context, resId, Toast.LENGTH_SHORT, objs); + Handler handler = (Handler) field_handler.get(mTN); + field_handler.set(mTN, new SafeHandler(handler)); + } catch (Exception ignored) { + } + } } - // ======================== - // == Toast.LENGTH_LONG === - // ======================== + /** + * detail: Toast 安全显示 Handler + * Created by Ttt + */ + private static final class SafeHandler extends Handler { - public static Toast showLongNew(Context context, String text) { - return handlerToastStr(false, context, text, Toast.LENGTH_LONG); - } + private Handler mHandler; - public static Toast showLongNew(Context context, String text, Object... objs) { - return handlerToastStr(false, context, text, Toast.LENGTH_LONG, objs); - } + SafeHandler(Handler handler) { + mHandler = handler; + } - public static Toast showLongNew(Context context, int resId) { - return handlerToastRes(false, context, resId, Toast.LENGTH_LONG); - } + @Override + public void handleMessage(Message msg) { + mHandler.handleMessage(msg); + } - public static Toast showLongNew(Context context, int resId, Object...objs) { - return handlerToastRes(false, context, resId, Toast.LENGTH_LONG, objs); + @Override + public void dispatchMessage(Message msg) { + try { + mHandler.dispatchMessage(msg); + } catch (Exception ignored) { + } + } } - // ======================= - // ==== 最终Toast方法 ==== - // ======================= + // == 其他接口 == /** - * 显示Toast - * @param context - * @param resId - * @param duration + * detail: Toast 过滤器 + * Created by Ttt */ - public static Toast showToastNew(Context context, int resId, int duration) { - return handlerToastRes(false, context, resId, duration); + public interface Filter { + + /** + * 判断是否显示 + * @param view + * @return true: 接着执行, false: 过滤不处理 + */ + boolean filter(View view); + + /** + * 判断是否显示 + * @param content + * @return true: 接着执行, false: 过滤不处理 + */ + boolean filter(String content); + + /** + * 获取 Toast 显示的文案 + * @param content + * @return 处理后的内容 + */ + String handlerContent(String content); } + // === ToastUtils.Filter 实现方法 === + /** - * 显示Toast - * @param context - * @param text - * @param duration + * 内部 Toast Filter 实现对象 */ - public static Toast showToastNew(Context context, String text, int duration) { - return showToast(false, context, text, duration); - } + private final static ToastUtils.Filter sPriToastFilter = new Filter() { + + /** + * 判断是否显示 + * @param view + * @return true: 接着执行, false: 过滤不处理 + */ + @Override + public boolean filter(View view) { + if (sToastFilter != null) { + return sToastFilter.filter(view); + } + return (view != null); + } + + /** + * 判断是否显示 + * @param content + * @return true: 接着执行, false: 过滤不处理 + */ + @Override + public boolean filter(String content) { + if (sToastFilter != null) { + return sToastFilter.filter(content); + } + return true; + } + + /** + * 获取 Toast 显示的文案 + * @param content + * @return 处理后的内容 + */ + @Override + public String handlerContent(String content) { + if (sToastFilter != null) { + return sToastFilter.handlerContent(content); + } + return content; + } + }; } 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/toast/toaster/DefaultToastStyle.java b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/DefaultToastStyle.java new file mode 100644 index 0000000000..967a3ccf58 --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/DefaultToastStyle.java @@ -0,0 +1,151 @@ +package dev.utils.app.toast.toaster; + +import android.graphics.Color; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; + +/** + * detail: Toast 默认样式 + * Created by Ttt + */ +public class DefaultToastStyle 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/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/DevToast.java b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/DevToast.java new file mode 100644 index 0000000000..b7179c65da --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/DevToast.java @@ -0,0 +1,171 @@ +package dev.utils.app.toast.toaster; + +import android.app.Application; +import android.view.View; + +/** + * detail: Toast 工具类 (支持子线程弹出 Toast, 处理无通知权限) + * Created by Ttt + * tips: + * 支持子线程弹出 Toast, 可通过开关配置 + * 内部解决 Android 7.1.1 崩溃问题 + * 已处理 部分ROM 如魅族、小米、三星等关闭应用通知,无法显示 Toast 问题 + * == + * 缺点: 同时间只能显示一个 Toast + */ +public final class DevToast { + + private DevToast() { + } + + /** + * 包下 IToastImpl 类持有对象 + */ + private static final IToast.Operate toast = new IToastImpl(); + + /** + * 重置默认参数 + */ + public static void reset() { + toast.reset(); + } + + /** + * 设置是否使用 Handler 显示 Toast + * @param isHandler + */ + public static void setIsHandler(boolean isHandler) { + toast.setIsHandler(isHandler); + } + + /** + * 设置 Text 为 null 的文本 + * @param nullText + */ + public static void setNullText(String nullText) { + toast.setNullText(nullText); + } + + /** + * 设置 Toast 文案长度转换 显示时间 + * @param textLengthConvertDuration + */ + public static void setTextLength(int textLengthConvertDuration){ + toast.setTextLength(textLengthConvertDuration); + } + + // = + + /** + * Application 初始化调用 (必须调用 -> 内部已经调用) + * @param application + */ + public static void init(Application application) { + toast.init(application); + } + + // -- 配置方法 -- + + /** + * 使用单次 Toast 样式配置 + * @param toastStyle Toast 样式 + * @return + */ + public static IToast.Operate style(IToast.Style toastStyle) { + return toast.style(toastStyle); + } + + /** + * 使用默认 Toast 样式 + * @return + */ + public static IToast.Operate defaultStyle() { + return toast.defaultStyle(); + } + + /** + * 获取 Toast 样式配置 + * @return Toast 样式配置 + */ + public static IToast.Style getToastStyle() { + return toast.getToastStyle(); + } + + /** + * 初始化 Toast 样式配置(非单次,一直持续) + * @param toastStyle Toast 样式配置 + */ + public static void initStyle(IToast.Style toastStyle) { + toast.initStyle(toastStyle); + } + + /** + * 初始化 Toast 过滤器 + * @param toastFilter + */ + public static void initToastFilter(IToast.Filter toastFilter) { + toast.initToastFilter(toastFilter); + } + + /** + * 设置 Toast 显示的View + * @param view + */ + public static void setView(View view) { + toast.setView(view); + } + + /** + * 设置 Toast 显示的View + * @param layoutId + */ + public static void setView(int layoutId) { + toast.setView(layoutId); + } + + // ========= 操作方法 ========= + + /** + * 显示 Toast + * @param content + * @param args + */ + public static void show(String content, Object... args) { + toast.show(content, args); + } + + /** + * 显示 R.string.resId Toast + * @param resId + * @param args + */ + public static void show(int resId, Object... args) { + toast.show(resId, args); + } + + /** + * 通过 View 显示 Toast + * @param view + */ + public static void show(View view) { + toast.show(view); + } + + /** + * 通过 View 显示 Toast + * @param view + * @param duration + */ + public static void show(View view, int duration) { + toast.show(view, duration); + } + + // = + + /** + * 取消当前显示的 Toast + */ + public static void cancel() { + toast.cancel(); + } +} diff --git a/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/IToast.java b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/IToast.java new file mode 100644 index 0000000000..3abdf857fa --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/IToast.java @@ -0,0 +1,273 @@ +package dev.utils.app.toast.toaster; + +import android.app.Application; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.View; + +/** + * detail: Toast 对外提供接口方法 + * Created by Ttt + */ +public final class IToast { + + private IToast() { + } + + /** + * detail: Toast 对外公开操作方法 + * Created by Ttt + */ + public interface Operate { + + /** + * 重置默认参数 + */ + void reset(); + + /** + * 设置是否使用 Handler 显示 Toast + * @param isHandler + */ + void setIsHandler(boolean isHandler); + + /** + * 设置 Text 为 null 的文本 + * @param nullText + */ + void setNullText(String nullText); + + /** + * 设置 Toast 文案长度转换 显示时间 + * @param textLengthConvertDuration + */ + void setTextLength(int textLengthConvertDuration); + + // = + + /** + * Application 初始化调用 + * @param application + */ + void init(Application application); + + // ==================== + // ----- 配置方法 ----- + // ==================== + + /** + * 使用单次 Toast 样式配置 + * @param toastStyle Toast 样式 + * @return + */ + IToast.Operate style(IToast.Style toastStyle); + + /** + * 使用默认 Toast 样式 + * @return + */ + IToast.Operate defaultStyle(); + + /** + * 获取 Toast 样式配置 + * @return Toast 样式配置 + */ + IToast.Style getToastStyle(); + + /** + * 初始化 Toast 样式配置(非单次,一直持续) + * @param toastStyle Toast 样式配置 + */ + void initStyle(IToast.Style toastStyle); + + /** + * 初始化 Toast 过滤器 + * @param toastFilter + */ + void initToastFilter(IToast.Filter toastFilter); + + /** + * 设置 Toast 显示的View + * @param view + */ + void setView(View view); + + /** + * 设置 Toast 显示的View + * @param layoutId + */ + void setView(int layoutId); + + // ========= 操作方法 ========= + + // = 显示文本的, 通过内容长度来控制, 显示时长 = + + /** + * 显示 Toast + * @param content + * @param args + */ + void show(String content, Object... args); + + /** + * 显示 R.string.resId Toast + * @param resId + * @param args + */ + void show(int resId, Object... args); + + // = 直接显示View的, 通过配置时间来控制 = + + /** + * 通过 View 显示 Toast + * @param view + */ + void show(View view); + + /** + * 通过 View 显示 Toast + * @param view + * @param duration + */ + void show(View view, int duration); + + // = + + /** + * 取消当前显示的 Toast + */ + void cancel(); + } + + // ====================== + // ====== 其他接口 ====== + // ====================== + + /** + * detail: Toast 样式配置 + * Created by Ttt + */ + public interface Style { + + /** + * Toast 的重心 + * @return + */ + int getGravity(); + + /** + * X轴偏移 + * @return + */ + int getXOffset(); + + /** + * Y轴偏移 + * @return + */ + int getYOffset(); + + /** + * 获取水平边距 + * @return + */ + int getHorizontalMargin(); + + /** + * 获取垂直边距 + * @return + */ + int getVerticalMargin(); + + /** + * Toast Z轴坐标阴影 + * @return + */ + int getZ(); + + /** + * 圆角大小 + * @return + */ + float getCornerRadius(); + + /** + * 背景着色颜色 + * @return + */ + int getBackgroundTintColor(); + + /** + * 背景图片 + * @return + */ + Drawable getBackground(); + + // == TextView 相关 == + + /** + * 文本颜色 + * @return + */ + int getTextColor(); + + /** + * 字体大小 + * @return + */ + float getTextSize(); + + /** + * 最大行数 + * @return + */ + int getMaxLines(); + + /** + * Ellipsize 效果 + * @return + */ + TextUtils.TruncateAt getEllipsize(); + + /** + * 字体样式 + * @return + */ + Typeface getTypeface(); + + /** + * TextView padding 边距 - new int[] { left, top, right, bottom } + * @return + */ + int[] getPadding(); + } + + /** + * detail: Toast 过滤器 + * Created by Ttt + */ + public interface Filter { + + /** + * 判断是否显示 + * @param view + * @return true: 接着执行, false: 过滤不处理 + */ + boolean filter(View view); + + /** + * 判断是否显示 + * @param content + * @return true: 接着执行, false: 过滤不处理 + */ + boolean filter(String content); + + /** + * 获取 Toast 显示的文案 + * @param content + * @return 处理后的内容 + */ + String handlerContent(String content); + } +} diff --git a/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/IToastImpl.java b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/IToastImpl.java new file mode 100644 index 0000000000..c87ef3e128 --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/IToastImpl.java @@ -0,0 +1,601 @@ +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.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 接口实现方法 (处理方法) + * Created by 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 sConfigToast = null; + // 当前显示的 Toast + private ToastFactory.BaseToast mToast = null; + // Toast 样式信息 + private IToast.Style sToastStyle = null; + // Toast 判断过滤 + private IToast.Filter sToastFilter = null; + // Toast 默认样式 + private final IToast.Style sDefaultStyle = new DefaultToastStyle(); + // 每个线程的 Toast 样式 + private final ThreadLocal LOCAL_TOAST_STYLES = new ThreadLocal<>(); + // 判断是否使用 Handler + private boolean mIsHandler = true; + // 内部 Handler + private final Handler sHandler = new Handler(Looper.getMainLooper()); + // Null 值 + private String mNullText = "text is null"; + // Toast 文案长度转换 显示时间 + private int mTextLengthConvertDuration = 15; + + /** + * 重置默认参数 + */ + @Override + public void reset() { + // 重新初始化 + init(mApplication); + } + + /** + * 设置是否使用 Handler 显示 Toast + * @param isHandler + */ + @Override + public void setIsHandler(boolean isHandler) { + this.mIsHandler = isHandler; + } + + /** + * 设置 Text 为 null 的文本 + * @param nullText + */ + @Override + public void setNullText(String nullText) { + this.mNullText = nullText; + } + + /** + * 设置 Toast 文案长度转换 显示时间 + * @param textLengthConvertDuration + */ + @Override + public void setTextLength(int textLengthConvertDuration) { + this.mTextLengthConvertDuration = textLengthConvertDuration; + } + + // === Application 中初始化 === + + /** + * Application 初始化调用 + * @param application + */ + @Override + public void init(Application application) { + if (application != null) { + this.mApplication = application; +// try { +// // 解除注册 Activity 生命周期监听 +// application.unregisterActivityLifecycleCallbacks(this); +// } catch (Exception e){ +// } +// // 注册 Activity 生命周期监听 +// application.registerActivityLifecycleCallbacks(this); + // 初始化默认参数 + mIsHandler = true; + mNullText = "text is null"; + // 初始化 Toast + sConfigToast = new ToastFactory.BaseToast(mApplication); + sConfigToast.setView(createView()); + // 初始化默认样式 + getToastStyle(); + } + } + + // === 实现IToast接口,对外公开方法 === + + /** + * 使用单次 Toast 样式配置 + * @param toastStyle Toast 样式 + * @return + */ + @Override + public IToast.Operate style(IToast.Style toastStyle) { + if(toastStyle != null) { + LOCAL_TOAST_STYLES.set(toastStyle); + } + return this; + } + + /** + * 使用默认 Toast 样式 + * @return + */ + @Override + public IToast.Operate defaultStyle() { + return style(sDefaultStyle); + } + + /** + * 获取 Toast 样式配置 + * @return Toast 样式配置 + */ + @Override + public IToast.Style getToastStyle() { + if(sToastStyle == null) { + sToastStyle = sDefaultStyle; + } + return sToastStyle; + } + + /** + * 初始化 Toast 样式配置(非单次,一直持续) + * @param toastStyle Toast 样式配置 + */ + @Override + public void initStyle(IToast.Style toastStyle) { + sToastStyle = toastStyle; + // 防止样式为null + getToastStyle(); + } + + /** + * 初始化 Toast 过滤器 + * @param toastFilter + */ + @Override + public void initToastFilter(IToast.Filter toastFilter) { + sToastFilter = toastFilter; + } + + /** + * 设置 Toast 显示的View + * @param view + */ + @Override + public void setView(View view) { + if (sConfigToast != null && view != null) { + sConfigToast.setView(view); + // 如果是 null, 则抛出异常 + if (sConfigToast.isEmptyMessageView()){ + // 如果设置的布局没有包含一个 TextView 则抛出异常,必须要包含一个 TextView 作为 Message View + throw new IllegalArgumentException("The layout must contain a TextView"); + } + } + } + + /** + * 设置 Toast 显示的View + * @param layoutId + */ + @Override + public void setView(int layoutId) { + if (sConfigToast != null) { + try { + setView(View.inflate(sConfigToast.getView().getContext().getApplicationContext(), layoutId, null)); + } catch (Exception e) { + } + // 如果是 null, 则抛出异常 + if (sConfigToast.isEmptyMessageView()){ + // 如果设置的布局没有包含一个 TextView 则抛出异常,必须要包含一个 TextView 作为 Message View + throw new IllegalArgumentException("The layout must contain a TextView"); + } + } + } + + // === 操作方法 === + + /** + * 显示 Toast + * @param content + * @param args + */ + @Override + public void show(String content, Object... args) { + String text = Utils.getFormatString(content, args); + if (filter(text)) { + priShowToastText(handlerContent(text)); + } + } + + /** + * 显示 R.string.resId Toast + * @param resId + * @param args + */ + @Override + public void show(int resId, Object... args) { + String text = Utils.getFormatRes(resId, args); + if (filter(text)) { + // 获取处理的内容 + priShowToastText(handlerContent(text)); + } + } + + /** + * 通过 View 显示 Toast + * @param view + */ + @Override + public void show(View view) { + if (filter(view)) { + priShowToastView(view, Toast.LENGTH_SHORT); + } + } + + /** + * 通过 View 显示 Toast + * @param view + * @param duration + */ + @Override + public void show(View view, int duration) { + if (filter(view)) { + priShowToastView(view, duration); + } + } + + // = + + @Override + public void cancel() { + if (mToast != null){ + try { + mToast.cancel(); + } catch (Exception e){ + } + } + } + + // === IToast.Filter 实现方法 === + + /** + * 判断是否显示 + * @param view + * @return true: 接着执行, false: 过滤不处理 + */ + @Override + public boolean filter(View view) { + if (sToastFilter != null) { + return sToastFilter.filter(view); + } + return true; + } + + /** + * 判断是否显示 + * @param content + * @return true: 接着执行, false: 过滤不处理 + */ + @Override + public boolean filter(String content) { + if (sToastFilter != null) { + return sToastFilter.filter(content); + } + return true; + } + + /** + * 获取 Toast 显示的文案 + * @param content + * @return 处理后的内容 + */ + @Override + public String handlerContent(String content) { + if (sToastFilter != null) { + return sToastFilter.handlerContent(content); + } + return content; + } + + // ================ + // === 内部处理 === + // ================ + + /** + * 返回对应线程的 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 + */ + 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) { + sHandler.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 + * @param text + * @return + */ + private Toast newToastText(IToast.Style style, String text) { + if (style == null) return null; + // 设置为null, 便于提示排查 + if (TextUtils.isEmpty(text)) { + text = mNullText; + // 如果还是为null, 则不处理 + if (TextUtils.isEmpty(text)) { + return null; + } + } + try { + // 关闭旧的 Toast + if (mToast != null) { + mToast.cancel(); + mToast = null; + } + // 如果不存在 TextView, 直接跳过 + if (sConfigToast.isEmptyMessageView()){ + return null; + } + View view = sConfigToast.getView(); + // 获取 Toast TextView + TextView toastTextView = sConfigToast.getMessageView(); + // 设置文案 + toastTextView.setText(text); + // 设置字体颜色 + 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((text.length() < mTextLengthConvertDuration) ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG); + } catch (Exception e) { + LogPrintUtils.eTag(TAG, e, "newToastText"); + } + return mToast; + } + + /** + * 显示 View Toast 方法 + * @param view + * @param duration + */ + private void priShowToastView(final View view, final int duration) { + if (view == null) return; + // 获取样式 + final IToast.Style style = getThreadToastStyle(); + // = + if (mIsHandler) { + sHandler.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 + * @param view + * @param duration + * @return + */ + private Toast newToastView(IToast.Style style, View view, 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; + } + + +// Application.ActivityLifecycleCallbacks +// // ================================ +// // == 监听 Activity 生命周期处理 == +// // ================================ +// +// @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) { +// // A 跳转 B 页面的生命周期方法执行顺序 +// // onPause(A) -> onCreate(B) -> onStart(B) -> onResume(B) -> onStop(A) -> onDestroyed(A) +// // = +// // 不能放在 onStop 或者 onDestroyed 方法中,因为此时新的 Activity 已经创建完成,必须在这个新的 Activity 未创建之前关闭这个 WindowManager +// // 调用取消显示会直接新的 Activity 的 onCreate 调用显示 Toast 可能显示不出来的问题(立马显示然后立马消失的效果) +//// if (mToast != null){ +//// if (mToast instanceof ToastFactory.NotificationToast){ +//// mToast.cancel(); +//// } +//// } +// } +// +// @Override +// public void onActivityStopped(Activity activity) {} +// +// @Override +// public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} +// +// @Override +// public void onActivityDestroyed(Activity activity) {} +} diff --git a/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/ToastFactory.java b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/ToastFactory.java new file mode 100644 index 0000000000..e182e5de0f --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/ToastFactory.java @@ -0,0 +1,343 @@ +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 工厂模式 + * Created by Ttt + */ +final class ToastFactory { + + private ToastFactory() { + } + + // 日志 TAG + private static final String TAG = ToastFactory.class.getSimpleName(); + + /** + * detail: Toast 基类 + * Created by Ttt + */ + static class BaseToast extends Toast { + + // Toast 消息 View + private TextView mMessageView; + + /** + * 构造函数 + * @param 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 + * @return + */ + private TextView findTextView(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 + */ + public final boolean isEmptyMessageView() { + return mMessageView == null; + } + + /** + * 获取TextView + * @return + */ + public TextView getMessageView() { + return mMessageView; + } + +// // = +// +// /** +// * 获取系统 Toast View +// * @param context +// * @return +// */ +// public static final View getToastSystemView(Context context) { +// return Toast.makeText(context, "", Toast.LENGTH_SHORT).getView(); +// } +// +// /** +// * 设置系统 Toast View +// * @param context +// */ +// public final BaseToast setToastSystemView(Context context) { +// setView(getToastSystemView(context)); +// return this; +// } + } + + /** + * detail: 解决 Android 7.1 Toast 崩溃问题 + * Created by Ttt + */ + static final class SafeToast extends BaseToast { + + public SafeToast(Context context) { + super(context); + // 反射设置 Toat Handler 解决 Android7.1.1Toast 崩溃 问题 + 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 + * Created by 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 + * Created by Ttt + */ + static final class NotificationToast extends BaseToast { + + // Toast Window 显示辅助类 + private final ToastHelper mToastHelper; + + 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 + * @return + */ + public static BaseToast create(Context context) { + if (Utils.isNotificationEnabled(context)) { + return new SafeToast(context); + } + return new NotificationToast(context); + } + + // = + + /** + * detail: Toast Window 显示辅助类 + * Created by Ttt + * tips: 参考 Toast.TN 实现方式 + */ + static final class ToastHelper extends Handler { + + // 当前 Toast 对象 + private final Toast mToast; + // 判断是否显示中 + private boolean show; + + ToastHelper(Toast toast) { + super(Looper.getMainLooper()); + mToast = toast; + } + + @Override + public void handleMessage(Message msg) { + cancel(); + } + + /*** + * 显示 Toast 弹窗 + */ + void show() { + if (!show) { + 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); + // 当前已经显示 + show = 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 (show) { + try { + Utils.getWindowManager(DevUtils.getTopActivity()).removeView(mToast.getView()); + } catch (Exception ignored) { + } + // 当前没有显示 + show = false; + } + } + } +} diff --git a/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/Utils.java b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/Utils.java new file mode 100644 index 0000000000..bd8a9abb8e --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/Utils.java @@ -0,0 +1,211 @@ +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.NonNull; +import android.view.View; +import android.view.WindowManager; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import dev.DevUtils; +import dev.utils.LogPrintUtils; + +/** + * detail: Toast 内部工具类 + * Created by Ttt + */ +final class Utils { + + private Utils() { + } + + // 日志 TAG + private static final String TAG = Utils.class.getSimpleName(); + + // = + + /** + * 获取格式化字符串 + * @param format + * @param args + * @return + */ + public static String getFormatString(String format, 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 + * @param objs + */ + public static String getFormatRes(int resId, 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 + */ + 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 + * @return + */ + public static boolean isNotificationEnabled(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) { + AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + ApplicationInfo appInfo = context.getApplicationInfo(); + String pkg = context.getApplicationContext().getPackageName(); + int uid = appInfo.uid; + + try { + 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 (NoSuchMethodException | NoSuchFieldException | InvocationTargetException | IllegalAccessException | RuntimeException | ClassNotFoundException ignored) { + return true; + } + } else { + return true; + } + } + + /** + * 获取一个对象的独一无二的标记 + * @param object + * @return + */ + public static String getObjectTag(Object object) { + if (object == null) return null; + // 对象所在的包名 + 对象的内存地址 + return object.getClass().getName() + Integer.toHexString(object.hashCode()); + } + + /** + * 获取一个WindowManager对象 + * @param activity + * @return + */ + public static WindowManager getWindowManager(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 + * @param drawable + */ + public static void setBackground(@NonNull View view, 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 + * @param id + * @return + */ + public static Drawable getDrawable(Context context, @DrawableRes int id) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + return context.getDrawable(id); + else + return context.getResources().getDrawable(id); + } + + /** + * 图片着色 + * @param drawable + * @param tintColor + * @return + */ + public static Drawable tintIcon(@NonNull Drawable drawable, @ColorInt 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 + * @param tintColor + * @return + */ + public static Drawable tint9PatchDrawableFrame(Context context, @ColorInt 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; + } +} diff --git a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiHotUtils.java b/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiHotUtils.java index f0385382ff..b32f0b9bd7 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiHotUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiHotUtils.java @@ -14,6 +14,7 @@ import java.io.FileReader; import java.lang.reflect.Method; +import dev.DevUtils; import dev.utils.LogPrintUtils; /** @@ -33,15 +34,25 @@ public class WifiHotUtils { // 日志TAG - private final static String TAG = WifiHotUtils.class.getSimpleName(); - // 对应的上下文 + private static final String TAG = WifiHotUtils.class.getSimpleName(); + // Context private Context mContext; /** 定义WifiManager对象 */ private WifiManager mWifiManager; // 热点 Wifi 配置 private WifiConfiguration apWifiConfig; - /** 构造器(只能进行初始化WifiManager操作,其他靠方法定义) */ + /** + * 构造器(只能进行初始化WifiManager操作,其他靠方法定义) + */ + public WifiHotUtils() { + this(DevUtils.getContext()); + } + + /** + * 构造器(只能进行初始化WifiManager操作,其他靠方法定义) + * @param context + */ public WifiHotUtils(Context context) { this.mContext = context; // 初始化WifiManager对象 @@ -51,7 +62,7 @@ public WifiHotUtils(Context context) { // == Wifi 操作 == /** - * 创建Wifi配置信息(无其他操作,单独返回WifiConfig) => Wifi热点 (就支持 无密码/WPA2 PSK) + * 创建Wifi配置信息(无其他操作,单独返回WifiConfig) => Wifi热点 (就支持 无密码/WPA2 PSK) * @param ssid * @param pwd 密码需要大于等于8位 * @return @@ -85,7 +96,7 @@ 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; @@ -98,9 +109,9 @@ public static WifiConfiguration createWifiConfigToAp(String ssid, String pwd) { public void stratWifiAp(WifiConfiguration wifiConfig) { this.apWifiConfig = 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(); } // 清空信息 @@ -120,7 +131,7 @@ public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) { // 打印信息 LogPrintUtils.dTag(TAG, "Android 8.0 wifi Ap ssid: " + apWifiSSID + ", pwd: " + apWifiPwd); // 触发回调 - if (wifiAPListener != null){ + if (wifiAPListener != null) { wifiAPListener.onStarted(wifiConfiguration); } } @@ -131,7 +142,7 @@ public void onStopped() { // 打印信息 LogPrintUtils.dTag(TAG, "Android 8.0 onStopped wifiAp"); // 触发回调 - if (wifiAPListener != null){ + if (wifiAPListener != null) { wifiAPListener.onStopped(); } } @@ -142,12 +153,12 @@ public void onFailed(int reason) { // 打印信息 LogPrintUtils.eTag(TAG, "Android 8.0 onFailed wifiAp, reason : " + reason); // 触发回调 - if (wifiAPListener != null){ + if (wifiAPListener != null) { wifiAPListener.onFailed(reason); } } }, null); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1){ // android 7.1 系统以上不支持自动开启热点,需要手动开启热点 + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { // android 7.1 系统以上不支持自动开启热点,需要手动开启热点 // 先设置wifi热点信息, 这样跳转前保存热点信息, 开启热点则是对应设置的信息 boolean setResult = setWifiApConfiguration(wifiConfig); // 打印日志 @@ -155,6 +166,7 @@ public void onFailed(int reason) { // https://www.cnblogs.com/bluestorm/p/3665555.html 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 { @@ -172,11 +184,11 @@ public void onFailed(int reason) { /** * 关闭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(); } // 清空信息 @@ -249,7 +261,7 @@ public int getWifiApState() { * 获取Wifi热点配置信息 * @return */ - public WifiConfiguration getWifiApConfiguration(){ + public WifiConfiguration getWifiApConfiguration() { try { // 获取Wifi热点方法 Method method = mWifiManager.getClass().getMethod("getWifiApConfiguration"); @@ -268,13 +280,13 @@ public WifiConfiguration getWifiApConfiguration(){ * @param apWifiConfig * @return 是否成功 */ - public boolean setWifiApConfiguration(WifiConfiguration apWifiConfig){ + public boolean setWifiApConfiguration(WifiConfiguration apWifiConfig) { try { // 获取设置Wifi热点方法 Method method = mWifiManager.getClass().getMethod("setWifiApConfiguration", WifiConfiguration.class); // 开启Wifi热点 return (boolean) method.invoke(mWifiManager, apWifiConfig); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "setWifiApConfiguration"); } return false; @@ -286,12 +298,12 @@ public boolean setWifiApConfiguration(WifiConfiguration apWifiConfig){ * 判断是否打开Wifi热点 * @return */ - public boolean isOpenWifiAp(){ + public boolean isOpenWifiAp() { // 判断是否开启热点(默认未打开) boolean isOpen = false; // 获取当前Wifi热点状态 int wifiApState = getWifiApState(); - switch(wifiApState){ + switch(wifiApState) { case WIFI_AP_STATE_DISABLING: // Wifi热点正在关闭 break; case WIFI_AP_STATE_DISABLED: // Wifi热点已关闭 @@ -312,12 +324,12 @@ public boolean isOpenWifiAp(){ * @param isExecute 是否执行关闭 * @return 之前是否打开热点 */ - public boolean closeWifiApCheck(boolean isExecute){ + public boolean closeWifiApCheck(boolean isExecute) { // 判断是否开启热点(默认是) boolean isOpen = true; // 获取当前Wifi热点状态 int wifiApState = getWifiApState(); - switch(wifiApState){ + switch(wifiApState) { case WIFI_AP_STATE_DISABLING: // Wifi热点正在关闭 isExecute = false; break; @@ -332,7 +344,7 @@ public boolean closeWifiApCheck(boolean isExecute){ break; } // 如果属于开启,则进行关闭 - if(isOpen && isExecute){ + if(isOpen && isExecute) { closeWifiAp(); } return isOpen; @@ -342,7 +354,7 @@ public boolean closeWifiApCheck(boolean isExecute){ * 是否有连接热点 * @return */ - public boolean isConnectHot(){ + public boolean isConnectHot() { try { BufferedReader br = new BufferedReader(new FileReader("/proc/net/arp")); String line; @@ -351,7 +363,7 @@ public boolean isConnectHot(){ if (splitted != null && splitted.length >= 4) { String ipAdr = splitted[0]; // Ip地址 // 防止地址为null,并且需要以.拆分 存在4个长度 255.255.255.255 - if(ipAdr != null && ipAdr.split("\\.").length >= 3){ + if(ipAdr != null && ipAdr.split("\\.").length >= 3) { return true; } } @@ -366,7 +378,7 @@ public boolean isConnectHot(){ * 获取热点主机ip地址 * @return */ - public String getHotspotServiceIp(){ + public String getHotspotServiceIp() { try { // 获取网关信息 DhcpInfo dhcpinfo = mWifiManager.getDhcpInfo(); @@ -382,7 +394,7 @@ public String getHotspotServiceIp(){ * 获取连接上的子网关热点IP(一个) * @return */ - public String getHotspotAllotIp(){ + public String getHotspotAllotIp() { try { BufferedReader br = new BufferedReader(new FileReader("/proc/net/arp")); String line; @@ -391,7 +403,7 @@ public String getHotspotAllotIp(){ if (splitted != null && splitted.length >= 4) { String ipAdr = splitted[0]; // Ip地址 // 防止地址为null,并且需要以.拆分 存在4个长度 255.255.255.255 - if(ipAdr != null && ipAdr.split("\\.").length >= 3){ + if(ipAdr != null && ipAdr.split("\\.").length >= 3) { return ipAdr; } } @@ -408,11 +420,11 @@ public String getHotspotAllotIp(){ * @param ipAdr ip地址 * @return */ - public String getHotspotSplitIpMask(String df, String ipAdr){ + public String getHotspotSplitIpMask(String df, String ipAdr) { // 网关掩码 String hsMask = df; // 获取网关掩码 - if(ipAdr != null){ + if(ipAdr != null) { try { int length = ipAdr.lastIndexOf("."); // 进行裁剪 @@ -477,10 +489,10 @@ private String intToString(int data) { */ public String getApWifiSSID() { // 大于 8.0 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { return apWifiSSID; } else { - if (apWifiConfig != null){ + if (apWifiConfig != null) { return apWifiConfig.SSID; } } @@ -493,10 +505,10 @@ public String getApWifiSSID() { */ public String getApWifiPwd() { // 大于 8.0 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { return apWifiPwd; } else { - if (apWifiConfig != null){ + if (apWifiConfig != null) { return apWifiConfig.preSharedKey; } } @@ -507,6 +519,10 @@ public String getApWifiPwd() { private onWifiAPListener wifiAPListener; + /** + * 设置 Wifi 热点监听 + * @param wifiAPListener + */ @RequiresApi(api = Build.VERSION_CODES.O) public void setOnWifiAPListener(onWifiAPListener wifiAPListener) { this.wifiAPListener = wifiAPListener; @@ -518,10 +534,21 @@ public void setOnWifiAPListener(onWifiAPListener wifiAPListener) { */ public interface onWifiAPListener { + /** + * 开启热点触发 + * @param wifiConfig + */ void onStarted(WifiConfiguration wifiConfig); + /** + * 关闭热点回调 + */ void onStopped(); + /** + * 失败回调 + * @param reason + */ void onFailed(int reason); } } diff --git a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiUtils.java b/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiUtils.java index 3ffb795904..d84d75f170 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiUtils.java @@ -1,5 +1,6 @@ package dev.utils.app.wifi; +import android.Manifest; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -9,6 +10,7 @@ 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; @@ -17,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import dev.DevUtils; import dev.utils.LogPrintUtils; /** @@ -29,8 +32,7 @@ public final class WifiUtils { // 日志TAG - private final static String TAG = WifiUtils.class.getSimpleName(); - + private static final String TAG = WifiUtils.class.getSimpleName(); // ======= wifi管理类对象 ======= /** 定义WifiManager对象 */ private WifiManager mWifiManager; @@ -41,9 +43,19 @@ public final class WifiUtils { public static final int WEP = 1; /** wpa加密方式 */ public static final int WPA = 2; - // ==================================================== + // ============================== + + /** + * 构造器(只能进行初始化WifiManager操作,其他靠方法定义) + */ + public WifiUtils() { + this(DevUtils.getContext()); + } - /** 构造器(只能进行初始化WifiManager操作,其他靠方法定义) */ + /** + * 构造器(只能进行初始化WifiManager操作,其他靠方法定义) + * @param context + */ public WifiUtils(Context context) { // 初始化WifiManager对象 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); @@ -53,14 +65,14 @@ public WifiUtils(Context context) { * 获取wifi管理对象 * @return */ - public WifiManager getWifiManager(){ + public WifiManager getWifiManager() { return this.mWifiManager; } // ================= wifi 开关、连接状态获取 ================= /** 判断是否打开wifi */ - public boolean isOpenWifi(){ + public boolean isOpenWifi() { return mWifiManager.isWifiEnabled(); } @@ -81,7 +93,7 @@ public void closeWifi() { } /** 自动切换wifi开关状态 */ - public void toggleWifiEnabled(){ + public void toggleWifiEnabled() { // 如果打开了,则关闭 // 如果关闭了,则打开 // ================= @@ -101,7 +113,7 @@ public int getWifiState() { // ================= GET 操作 ================= /** 开始扫描wifi */ - public boolean startScan(){ + public boolean startScan() { // 开始扫描 return mWifiManager.startScan(); } @@ -117,26 +129,42 @@ public List getWifiList() { } /** 获取WifiInfo对象 */ - public WifiInfo getWifiInfo(){ + public WifiInfo getWifiInfo() { return mWifiManager.getConnectionInfo(); } - /** 获取MAC地址 */ + /** + * 获取MAC地址 + * @param wifiInfo + * @return + */ public String getMacAddress(WifiInfo wifiInfo) { return wifiInfo.getMacAddress(); } - /** 获取接入点的BSSID */ + /** + * 获取接入点的BSSID + * @param wifiInfo + * @return + */ public String getBSSID(WifiInfo wifiInfo) { return wifiInfo.getBSSID(); } - /** 获取IP地址 */ + /** + * 获取IP地址 + * @param wifiInfo + * @return + */ public int getIPAddress(WifiInfo wifiInfo) { return wifiInfo.getIpAddress(); } - /** 获取连接的ID */ + /** + * 获取连接的ID + * @param wifiInfo + * @return + */ public int getNetworkId(WifiInfo wifiInfo) { return wifiInfo.getNetworkId(); } @@ -148,7 +176,7 @@ public int getNetworkId(WifiInfo wifiInfo) { * @param wifiInfo wifi信息 * @return */ - public static String getSSID(WifiInfo wifiInfo){ + public static String getSSID(WifiInfo wifiInfo) { try { // 获取SSID,并进行处理 return formatSSID(wifiInfo.getSSID(), false); @@ -159,13 +187,12 @@ public static String getSSID(WifiInfo wifiInfo){ } /** - * 通过上下文获取当前连接的ssid - * @param context + * 通过 Context 获取当前连接的ssid */ - public static String getSSID(Context context){ + public static String getSSID() { try { // 初始化WifiManager对象 - WifiManager mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + WifiManager mWifiManager = (WifiManager) DevUtils.getContext().getApplicationContext().getSystemService(Context.WIFI_SERVICE); // 获取当前连接的wifi WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); // 获取wifi - SSID @@ -180,10 +207,11 @@ public static String getSSID(Context context){ /** * 判断是否存在\"ssid\",存在则裁剪返回 + * @param ssid */ - public static String formatSSID(String ssid){ + public static String formatSSID(String ssid) { // 自动去掉SSID - if(ssid != null && ssid.startsWith("\"") && ssid.endsWith("\"")){ + if(ssid != null && ssid.startsWith("\"") && ssid.endsWith("\"")) { try { // 裁剪连接的ssid,并返回 return ssid.substring(1, ssid.length() - 1); @@ -200,8 +228,8 @@ public static String formatSSID(String ssid){ * @param isHandler true = 添加引号,false = 删除引号 * @return */ - public static String formatSSID(String ssid, boolean isHandler){ - if(isHandler){ + public static String formatSSID(String ssid, boolean isHandler) { + if(isHandler) { return "\"" + ssid + "\""; } else { return formatSSID(ssid); @@ -209,12 +237,12 @@ public static String formatSSID(String ssid, boolean isHandler){ } /** - * 获取密码(经过处理) + * 获取密码(经过处理) * @param pwd 需要处理的密码 * @param isJudge 是否需要判断 * @return */ - public String getPassword(String pwd, boolean isJudge){ + public String getPassword(String pwd, boolean isJudge) { if (isJudge && isHexWepKey(pwd)) { return pwd; } else { @@ -222,7 +250,12 @@ public String getPassword(String pwd, boolean isJudge){ } } - protected static boolean isHexWepKey(String wepKey) { + /** + * 判断是否 wep 加密 + * @param wepKey + * @return + */ + public 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) { @@ -231,7 +264,12 @@ protected static boolean isHexWepKey(String wepKey) { return isHex(wepKey); } - protected static boolean isHex(String key) { + /** + * 判断是否 十六进制 + * @param key + * @return + */ + private 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')) { @@ -262,7 +300,7 @@ public static int getWifiType(String type) { * 获取加密类型(int常量) - 判断int(String) * @param type */ - public static int getWifiTypeInt(String type){ + public static int getWifiTypeInt(String type) { // WPA 是本机的用法 if (type.equals("2")) { return WPA; @@ -277,8 +315,8 @@ public static int getWifiTypeInt(String type){ * 获取加密类型(int常量) * @param type */ - public static String getWifiType(int type){ - switch(type){ + public static String getWifiType(int type) { + switch(type) { case WPA: return "2"; case WEP: @@ -295,7 +333,7 @@ public static String getWifiType(int type){ * @return */ public static String getWifiTypeStr(int type) { - switch(type){ + switch(type) { case WPA: return "WPA"; case WEP: @@ -306,14 +344,14 @@ public static String getWifiTypeStr(int type) { } /** - * 判断是否连接为null - + * 判断是否连接为null - unknown ssid * @param ssid * @return */ - public static boolean isConnNull(String ssid){ - if(ssid == null){ + public static boolean isConnNull(String ssid) { + if(ssid == null) { return true; - } else if (ssid.indexOf("unknown") != -1){ // + } else if (ssid.indexOf("unknown") != -1) { // return true; } return false; @@ -321,17 +359,19 @@ public static boolean isConnNull(String ssid){ /** * 判断是否连接上Wifi(非连接中) + * * @return 返回ssid */ - public static String isConnectAphot(Context context){ + @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) + public static String isConnectAphot() { try { // 连接管理 - ConnectivityManager cManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cManager = (ConnectivityManager) DevUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); // 连接状态 NetworkInfo.State nState = cManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState(); - if((nState == NetworkInfo.State.CONNECTED)){ + if((nState == NetworkInfo.State.CONNECTED)) { // 获取连接的ssid - return getSSID(context); + return getSSID(); } } catch (Exception e) { LogPrintUtils.eTag(TAG, e, "isConnectAphot"); @@ -343,13 +383,13 @@ public static String isConnectAphot(Context context){ // ================= Wifi配置操作 ================= /** 默认没有密码 */ - static final int SECURITY_NONE = 0; + private static final int SECURITY_NONE = 0; /** WEP加密方式 */ - static final int SECURITY_WEP = 1; + private static final int SECURITY_WEP = 1; /** PSK加密方式 */ - static final int SECURITY_PSK = 2; + private static final int SECURITY_PSK = 2; /** EAP加密方式 */ - static final int SECURITY_EAP = 3; + private static final int SECURITY_EAP = 3; /** * 获取Wifi配置,加密类型 @@ -372,7 +412,7 @@ public static int getSecurity(WifiConfiguration wifiConfig) { * @param wifiConfig * @return */ - public static boolean isExsitsPwd(WifiConfiguration wifiConfig){ + public static boolean isExsitsPwd(WifiConfiguration wifiConfig) { int wifiSecurity = getSecurity(wifiConfig); // 判断是否加密 return (wifiSecurity != SECURITY_NONE); @@ -382,18 +422,18 @@ public static boolean isExsitsPwd(WifiConfiguration wifiConfig){ * 查看以前是否也配置过这个网络 * @param ssid 需要判断的wifi SSID */ - public WifiConfiguration isExsits(String ssid){ + public WifiConfiguration isExsits(String ssid) { // 获取wifi 连接过的配置信息 List listWifiConfigs = getConfiguration(); // 防止为null - if(listWifiConfigs == null){ + if(listWifiConfigs == null) { return null; } // 遍历判断是否存在 - for(int i = 0, len = listWifiConfigs.size();i < len ;i++){ + for(int i = 0, len = listWifiConfigs.size();i < len ;i++) { WifiConfiguration wConfig = listWifiConfigs.get(i); - if(wConfig != null){ - if (wConfig.SSID.equals("\"" + ssid + "\"")){ + if(wConfig != null) { + if (wConfig.SSID.equals("\"" + ssid + "\"")) { return wConfig; } } @@ -405,18 +445,18 @@ public WifiConfiguration isExsits(String ssid){ * 查看以前是否也配置过这个网络 * @param networkId 网络id */ - public WifiConfiguration isExsits(int networkId){ + public WifiConfiguration isExsits(int networkId) { // 获取wifi 连接过的配置信息 List listWifiConfigs = getConfiguration(); // 防止为null - if(listWifiConfigs == null){ + if(listWifiConfigs == null) { return null; } // 遍历判断是否存在 - for(int i = 0, len = listWifiConfigs.size();i < len ;i++){ + for(int i = 0, len = listWifiConfigs.size();i < len ;i++) { WifiConfiguration wConfig = listWifiConfigs.get(i); - if(wConfig != null){ - if (wConfig.networkId == networkId){ + if(wConfig != null) { + if (wConfig.networkId == networkId) { return wConfig; } } @@ -427,23 +467,22 @@ public WifiConfiguration isExsits(int networkId){ // ================= 配置操作 ================= /** * 删除指定的 Wifi(SSID) 配置信息 - * @param context * @param ssid * @return 删除结果 */ - public static boolean delWifiConfig(Context context, String ssid){ + public static boolean delWifiConfig(String ssid) { try { // 初始化WifiManager对象 - WifiManager mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + WifiManager mWifiManager = (WifiManager) DevUtils.getContext().getApplicationContext().getSystemService(Context.WIFI_SERVICE); // 获取wifi 连接过的配置信息 List listWifiConfigs = mWifiManager.getConfiguredNetworks(); // 防止为null - if(listWifiConfigs != null){ + if(listWifiConfigs != null) { // 遍历判断是否存在 - for(int i = 0, len = listWifiConfigs.size();i < len ;i++){ + for(int i = 0, len = listWifiConfigs.size();i < len ;i++) { WifiConfiguration wConfig = listWifiConfigs.get(i); - if(wConfig != null){ - if (wConfig.SSID.equals("\"" + ssid + "\"")){ + if(wConfig != null) { + if (wConfig.SSID.equals("\"" + ssid + "\"")) { // 删除操作 mWifiManager.removeNetwork(wConfig.networkId); } @@ -465,7 +504,7 @@ public static boolean delWifiConfig(Context context, String ssid){ * @param pwd wifi密码 * @param wType 加密类型 */ - public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType){ + public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType) { return quickConnWifi(ssid, pwd, wType, false, null); } @@ -478,7 +517,7 @@ public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType){ * @param ip 静态IP地址 * @return 连接成功的 WifiConfiguration */ - public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType, boolean isStatic, String ip){ + public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType, boolean isStatic, String ip) { // 步骤 // 1.创建Wifi静态Ip连接配置 // 2.创建正常Wifi连接配置 @@ -493,11 +532,11 @@ public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType, boole // 正常的Wifi连接配置 WifiConfiguration connWifiConfig = null; // 如果需要通过静态ip方式连接,则进行设置 - if(isStatic && !TextUtils.isEmpty(ip)){ + if(isStatic && !TextUtils.isEmpty(ip)) { // 创建Wifi静态IP连接配置 WifiConfiguration staticWifiConfig = setStaticWifiConfig(createWifiConfig(ssid, pwd, wType, true), ip); // 如果静态ip方式,配置失败,则初始化正常连接的Wifi配置 - if(staticWifiConfig == null){ + if(staticWifiConfig == null) { // 创建正常的配置信息 connWifiConfig = createWifiConfig(ssid, pwd, wType, true); // -- @@ -517,7 +556,7 @@ public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType, boole // 判断当前准备连接的wifi,是否存在配置文件 WifiConfiguration preWifiConfig = this.isExsits(ssid); // -- - if(preWifiConfig != null){ + if(preWifiConfig != null) { // 存在则删除 boolean isRemove = mWifiManager.removeNetwork(preWifiConfig.networkId); // 打印结果 @@ -528,7 +567,7 @@ public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType, boole // -- // 连接网络 int _nId = mWifiManager.addNetwork(connWifiConfig); - if(_nId != -1){ + if(_nId != -1) { try { // 获取当前连接的Wifi对象 WifiInfo wifiInfo = getWifiInfo(); @@ -546,7 +585,7 @@ public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType, boole // 开始连接 boolean isResult = mWifiManager.enableNetwork(_nId, true); // -- - if(!isResult){ + if(!isResult) { isResult = mWifiManager.enableNetwork(_nId, true); } // 打印结果 @@ -556,7 +595,7 @@ public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType, boole connWifiConfig.SSID = formatSSID(connWifiConfig.SSID, false); // 连接网络 _nId = mWifiManager.addNetwork(connWifiConfig); - if(_nId !=-1){ + if(_nId !=-1) { try { // 获取当前连接的Wifi对象 WifiInfo wifiInfo = getWifiInfo(); @@ -574,7 +613,7 @@ public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType, boole // 开始连接 boolean isResult = mWifiManager.enableNetwork(_nId, true); // -- - if(!isResult){ + if(!isResult) { isResult = mWifiManager.enableNetwork(_nId, true); } // 打印结果 @@ -587,14 +626,14 @@ public WifiConfiguration quickConnWifi(String ssid, String pwd, int wType, boole LogPrintUtils.dTag(TAG, "连接的SSID - networkId : " + _nId); // 返回连接的信息 return connWifiConfig; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, "quickConnWifi", e); } return null; } /** - * 创建Wifi配置信息(无其他操作,单独返回WifiConfig) + * 创建Wifi配置信息(无其他操作,单独返回WifiConfig) * @param ssid * @param pwd * @param wType @@ -616,7 +655,7 @@ public static WifiConfiguration createWifiConfig(String ssid, String pwd, int wT } else { wifiConfig.SSID = ssid; } - switch(wType){ + switch(wType) { case WifiUtils.NOPWD: // 不存在密码 wifiConfig.hiddenSSID = true; wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE); @@ -671,7 +710,7 @@ public static WifiConfiguration createWifiConfig(String ssid, String pwd, int wT * @param wcg * @return */ - public boolean removeWifiConfig(WifiConfiguration wcg){ + public boolean removeWifiConfig(WifiConfiguration wcg) { // 如果等于null则直接返回 if(wcg == null) return false; @@ -682,7 +721,7 @@ public boolean removeWifiConfig(WifiConfiguration wcg){ mWifiManager.saveConfiguration(); // 返回删除结果 return isResult; - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, "removeWifiConfig", e); } return false; @@ -696,7 +735,7 @@ public void disconnectWifi(int netId) { try { mWifiManager.disableNetwork(netId); mWifiManager.disconnect(); - } catch (Exception e){ + } catch (Exception e) { LogPrintUtils.eTag(TAG, "disconnectWifi", e); } } @@ -709,10 +748,10 @@ public void disconnectWifi(int netId) { * @param ip 静态ip * @return */ - private WifiConfiguration setStaticWifiConfig(WifiConfiguration wifiConfig, String ip){ + private WifiConfiguration setStaticWifiConfig(WifiConfiguration wifiConfig, String ip) { String gateway = null; String dns = null; - if(ip != null){ + if(ip != null) { try { InetAddress intetAddress = InetAddress.getByName(ip); int intIp = inetAddressToInt(intetAddress); @@ -736,14 +775,14 @@ private WifiConfiguration setStaticWifiConfig(WifiConfiguration wifiConfig, Stri * @param networkPrefixLength 网络前缀长度 * @return */ - private WifiConfiguration setStaticWifiConfig(WifiConfiguration wifiConfig, String ip, String gateway, String dns, int networkPrefixLength){ + private WifiConfiguration setStaticWifiConfig(WifiConfiguration wifiConfig, String ip, String gateway, String dns, int networkPrefixLength) { try { - if(ip == null || gateway == null){ + if(ip == null || gateway == null) { return null; } // 设置Inet地址 InetAddress intetAddress = InetAddress.getByName(ip); - if(Build.VERSION.SDK_INT <= 20){ // 旧的版本,5.0之前 + if(Build.VERSION.SDK_INT <= 20) { // 旧的版本,5.0之前 // 设置IP分配方式,静态ip setEnumField(wifiConfig, "STATIC", "ipAssignment"); // 设置不用代理 @@ -752,7 +791,7 @@ private WifiConfiguration setStaticWifiConfig(WifiConfiguration wifiConfig, Stri setIpAddress(intetAddress, networkPrefixLength, wifiConfig); // 设置网关 setGateway(InetAddress.getByName(gateway), wifiConfig); - if(dns != null){ // 判断是否需要设置域名 + if(dns != null) { // 判断是否需要设置域名 // 设置DNS setDNS(InetAddress.getByName(dns), wifiConfig); } @@ -791,7 +830,7 @@ private void setDNS(InetAddress dns, WifiConfiguration wifiConfig) throws Except if (linkProperties == null) throw new NullPointerException(); - ArrayList mDnses = (ArrayList) getDeclaredField(linkProperties, "mDnses"); + List mDnses = (ArrayList) getDeclaredField(linkProperties, "mDnses"); mDnses.clear(); // or add a new dns address , here I just want to replace DNS1 mDnses.add(dns); } @@ -848,7 +887,7 @@ private void setStaticIpConfig(String ip, String gateway, String dns, int prefix // 从WifiConfig -> mIpConfiguration 获取staticIpConfiguration // 获取 staticIpConfiguration 变量 Object staticIpConfigClass = getField(obj, "staticIpConfiguration"); - if(staticIpConfigClass == null){ + if(staticIpConfigClass == null) { // 创建静态ip配置类 staticIpConfigClass = Class.forName("android.net.StaticIpConfiguration").newInstance(); } @@ -860,9 +899,9 @@ private void setStaticIpConfig(String ip, String gateway, String dns, int prefix setValueField(staticIpConfigClass, linkAddress, "ipAddress"); // 设置网关 gateway setValueField(staticIpConfigClass, InetAddress.getByName(gateway), "gateway"); - if(dns != null){ // 判断是否需要设置域名 + if(dns != null) { // 判断是否需要设置域名 // 设置DNS - ArrayList mDnses = (ArrayList) getDeclaredField(staticIpConfigClass, "dnsServers"); + 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)); } diff --git a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiVo.java b/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiVo.java index 1c37c7f259..e529452fda 100644 --- a/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiVo.java +++ b/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiVo.java @@ -3,8 +3,8 @@ import android.net.wifi.ScanResult; import android.os.Parcel; import android.os.Parcelable; +import android.support.annotation.Keep; -import java.util.ArrayList; import java.util.List; import dev.utils.LogPrintUtils; @@ -18,28 +18,32 @@ public class WifiVo implements Parcelable { // 日志TAG private static final String TAG = WifiVo.class.getSimpleName(); /** wifi SSID */ + @Keep public String wSSID = null; /** wifi 密码 */ + @Keep public String wPwd = null; /** wifi 加密类型 */ + @Keep public int wType = WifiUtils.NOPWD; /** wifi 信号等级 */ + @Keep public int wLevel = 0; // -- - public WifiVo(){ + public WifiVo() { } /** * 获取Wifi信息 * @param sResult 扫描的Wifi信息 */ - public static WifiVo createWifiVo(ScanResult sResult){ - if (sResult != null){ + public static WifiVo createWifiVo(ScanResult sResult) { + if (sResult != null) { try { // 防止wifi名长度为0 - if(sResult.SSID.length() == 0){ + if(sResult.SSID.length() == 0) { return null; } // 初始化wifi信息实体类 @@ -63,17 +67,17 @@ public static WifiVo createWifiVo(ScanResult sResult){ * @param listWifiVos 处理后数据源 * @param listScanResults 扫描返回的数据 */ - public static void scanWifiVos(ArrayList listWifiVos, List listScanResults){ + public static void scanWifiVos(List listWifiVos, List listScanResults) { // 清空旧数据 listWifiVos.clear(); // 遍历wifi列表数据 - for(int i = 0, len = listScanResults.size();i < len;i++){ + 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){ + if(sResult.SSID.length() == 0) { continue; } // 保存wifi信息 diff --git a/DevLibUtils/src/main/java/dev/utils/common/AssistUtils.java b/DevLibUtils/src/main/java/dev/utils/common/AssistUtils.java index 4eca7a3b20..0c13f8d3f9 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/AssistUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/AssistUtils.java @@ -11,14 +11,14 @@ */ public final class AssistUtils { - private AssistUtils(){ + private AssistUtils() { } /** * 获取随机唯一数 * @return */ - public static UUID randomUUID(){ + public static UUID randomUUID() { return UUID.randomUUID(); } @@ -26,7 +26,7 @@ public static UUID randomUUID(){ * 获取随机唯一数 HashCode * @return */ - public static int randomUUIDToHashCode(){ + public static int randomUUIDToHashCode() { return UUID.randomUUID().hashCode(); } @@ -35,18 +35,18 @@ public static int randomUUIDToHashCode(){ * @param uuid * @return */ - public static int randomUUIDToHashCode(UUID uuid){ - if (uuid != null){ + public static int randomUUIDToHashCode(UUID uuid) { + if (uuid != null) { return uuid.hashCode(); } - return -1; + return 0; } /** * 获取随机数 唯一id * @return */ - public static String getRandomUUID(){ + public static String getRandomUUID() { // 获取随机数 String random1 = (900000 + new Random().nextInt(10000)) + ""; // 获取随机数 @@ -69,16 +69,16 @@ public static String getRandomUUID(){ * @param salts * @return */ - public static String whileMD5(String data, int number, boolean isUppercase, String... salts){ - if (data != null && number >= 1){ + 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){ + if (isUppercase) { // 循环加密 - for (int i = 0; i < number; i++){ - if (saltLen >= i){ + for (int i = 0; i < number; i++) { + if (saltLen >= i) { String salt = salts[i]; - if (salt != null){ + if (salt != null) { data = MD5Utils.md5Upper(data + salt); } else { data = MD5Utils.md5Upper(data); @@ -89,10 +89,10 @@ public static String whileMD5(String data, int number, boolean isUppercase, Stri } } else { // 循环加密 - for (int i = 0; i < number; i++){ - if (saltLen >= i){ + for (int i = 0; i < number; i++) { + if (saltLen >= i) { String salt = salts[i]; - if (salt != null){ + if (salt != null) { data = MD5Utils.md5(data + salt); } else { data = MD5Utils.md5(data); diff --git a/DevLibUtils/src/main/java/dev/utils/common/BigDecimalUtils.java b/DevLibUtils/src/main/java/dev/utils/common/BigDecimalUtils.java index 86c7c37bdd..e27884d399 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/BigDecimalUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/BigDecimalUtils.java @@ -10,7 +10,7 @@ */ public final class BigDecimalUtils { - private BigDecimalUtils(){ + private BigDecimalUtils() { } // 日志TAG @@ -44,7 +44,7 @@ public static BigDecimal add(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.add(b2); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "add"); } return new BigDecimal(0); @@ -65,7 +65,7 @@ public static String add(String v1, String v2, int scale) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "add"); } return "0"; @@ -94,7 +94,7 @@ public static BigDecimal substract(String v1, String v2) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.subtract(b2); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "substract"); } return new BigDecimal(0); @@ -115,7 +115,7 @@ public static String substract(String v1, String v2, int scale) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "substract"); } return "0"; @@ -164,14 +164,14 @@ public static String multiply(String v1, String v2, int scale) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "multiply"); } return "0"; } /** - * 提供(相对)精确的除法运算,当发生除不尽的情况时, + * 提供(相对)精确的除法运算,当发生除不尽的情况时, * 精确到小数点以后10位,以后的数字四舍五入. * @param v1 被除数 * @param v2 除数 @@ -183,7 +183,7 @@ public static double divide(double v1, double v2) { /** - * 提供(相对)精确的除法运算. + * 提供(相对)精确的除法运算. * 当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入. * @param v1 被除数 * @param v2 除数 @@ -200,7 +200,7 @@ public static double divide(double v1, double v2, int scale) { } /** - * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 * 定精度,以后的数字四舍五入 * @param v1 被除数 * @param v2 除数 @@ -215,7 +215,7 @@ public static String divide(String v1, String v2, int scale) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v1); return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "divide"); } return "0"; @@ -249,7 +249,7 @@ public static String round(String v, int scale) { try { BigDecimal b = new BigDecimal(v); return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "round"); } return "0"; @@ -288,7 +288,7 @@ public static String remainder(String v1, String v2, int scale) { BigDecimal b1 = new BigDecimal(v1); BigDecimal b2 = new BigDecimal(v2); return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString(); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "remainder"); } return "0"; @@ -307,7 +307,7 @@ public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) { } try { return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "remainder"); } return new BigDecimal(0); @@ -435,7 +435,7 @@ public static Boolean compareBigDecimal(String amount, double compare) { if (lenth.compareTo(BigDecimal.valueOf(compare)) == -1) { return false; } - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "compareBigDecimal"); } return true; @@ -449,7 +449,7 @@ public static Boolean compareBigDecimal(String amount, double compare) { * @return 处理过的数据 */ public static String adjustDouble(String s, int numOfIntPart, int numOfDecimalPart) { - if (s == null){ + if (s == null) { return null; } // 按小数点的位置分割成整数部分和小数部分 diff --git a/DevLibUtils/src/main/java/dev/utils/common/ByteUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ByteUtils.java index 6141a597a3..f69da9921f 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/ByteUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/ByteUtils.java @@ -9,14 +9,14 @@ /** * detail: 字节工具类,提供一些有关字节的便捷方法 + * Created by Ttt * (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(){ + private ByteUtils() { } // 日志TAG @@ -33,7 +33,7 @@ private ByteUtils(){ * @param bytes */ public static void byteJiaMi(byte[] bytes) { - if (bytes == null){ + if (bytes == null) { return; } for (int w = 0; w < bytes.length; w++) { diff --git a/DevLibUtils/src/main/java/dev/utils/common/ClassUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ClassUtils.java index 16e8da28e9..225cbdc8af 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/ClassUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/ClassUtils.java @@ -8,11 +8,11 @@ /** * detail: 类工具 - * @author mty + * Created by Ttt */ public final class ClassUtils { - private ClassUtils(){ + private ClassUtils() { } // 日志TAG @@ -37,7 +37,7 @@ public static boolean isBaseDataType(Class clazz) { * @param claxx * @return */ - public static T newInstance(Class claxx){ + public static T newInstance(Class claxx) { try { Constructor[] cons = claxx.getDeclaredConstructors(); for (Constructor c : cons) { @@ -54,14 +54,14 @@ public static T newInstance(Class claxx){ return (T) c.newInstance(objs); } } - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "newInstance"); } return null; } /** - * 判断 Class 是否为原始类型(boolean、char、byte、short、int、long、float、double) + * 判断 Class 是否为原始类型(boolean、char、byte、short、int、long、float、double) * @param clazz * @return */ diff --git a/DevLibUtils/src/main/java/dev/utils/common/CloneUtils.java b/DevLibUtils/src/main/java/dev/utils/common/CloneUtils.java index 956a67c602..fc326fe37b 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/CloneUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/CloneUtils.java @@ -15,7 +15,7 @@ */ public final class CloneUtils { - private CloneUtils(){ + private CloneUtils() { } // 日志TAG diff --git a/DevLibUtils/src/main/java/dev/utils/common/CloseUtils.java b/DevLibUtils/src/main/java/dev/utils/common/CloseUtils.java index 7d764f519e..f7091fcc53 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/CloseUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/CloseUtils.java @@ -5,10 +5,8 @@ import dev.utils.JCLogUtils; /** - * detail: 关闭工具类 - (关闭IO流等) - * Created by Blankj - * http://blankj.com - * Update to Ttt + * detail: 关闭工具类 - (关闭IO流等) + * Created by Ttt */ public final class CloseUtils { diff --git a/DevLibUtils/src/main/java/dev/utils/common/ColorUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ColorUtils.java new file mode 100644 index 0000000000..97e528d263 --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/common/ColorUtils.java @@ -0,0 +1,658 @@ +package dev.utils.common; + +import java.util.HashMap; +import java.util.Locale; + +import dev.utils.JCLogUtils; + +/** + * detail: 颜色工具类 包括常用的色值 + * Created by Ttt + */ +public final class ColorUtils { + + private ColorUtils() { + } + + // 日志TAG + private static final String TAG = ColorUtils.class.getSimpleName(); + + /** + * 0-255 十进值转换成十六进制,如255 就是 ff + * 255 * 0.x = 十进制 -> 十六进制 + * ============ + * 透明度0-100 + * 00、19、33、4C、66、7F、99、B2、CC、E5、FF + */ + + /** 透明 */ + 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; + + // = + + /** + * 计算百分比值 + * @param value + * @param max + * @return + */ + public static float percent(float value, float max){ + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + if (value >= max) return 1.0f; + return value / max; + } + + /** + * 计算百分比值 + * @param value + * @param max + * @return + */ + public static float percent(int value, 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 + */ + public static int clamp(int value, int max, 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 + */ + public static float clamp(float value, float max, float min) { + return value > max ? max : value < min ? min : value; + } + + // = + + /** + * 返回一个颜色中的透明度值(返回10进制) + * @param color + * @return + */ + public static int alpha(int color) { + return color >>> 24; + } + + /** + * 返回一个颜色中的透明度百分比值 + * @param color + * @return + */ + public static float alphaPercent(int color){ + return percent(alpha(color), 255); + } + + // = + + /** + * 返回一个颜色中红色的色值(返回10进制) + * @param color + * @return + */ + public static int red(int color) { + return (color >> 16) & 0xFF; + } + + /** + * 返回一个颜色中红色的百分比值 + * @param color + * @return + */ + public static float redPercent(int color){ + return percent(red(color), 255); + } + + // = + + /** + * 返回一个颜色中绿色的色值(返回10进制) + * @param color + * @return + */ + public static int green(int color) { + return (color >> 8) & 0xFF; + } + + /** + * 返回一个颜色中绿色的百分比值 + * @param color + * @return + */ + public static float greenPercent(int color){ + return percent(green(color), 255); + } + + // = + + /** + * 返回一个颜色中蓝色的色值(返回10进制) + * @param color + * @return + */ + public static int blue(int color) { + return color & 0xFF; + } + + /** + * 返回一个颜色中蓝色的百分比值 + * @param color + * @return + */ + public static float bluePercent(int color){ + return percent(blue(color), 255); + } + + // = + + /** + * 根据对应的 red、green、blue 生成一个颜色值 + * @param red [0-255] + * @param green [0-255] + * @param blue [0-255] + */ + public static int rgb(int red, int green, int blue) { + return 0xff000000 | (red << 16) | (green << 8) | blue; + } + + /** + * 根据对应的 red、green、blue 生成一个颜色值 + * @param red [0-255] + * @param green [0-255] + * @param blue [0-255] + */ + public static int rgb(float red, float green, 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] + */ + public static int argb(int alpha, int red, int green, 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] + */ + public static int argb(float alpha, float red, float green, 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 + * @return + */ + public static boolean isRGB(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 + * @return + */ + public static boolean isARGB(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 + * @param alpha [0-255] + * @return + */ + public static int setAlpha(int color, int alpha) { + return (color & 0x00ffffff) | (alpha << 24); + } + + /** + * 设置透明度 + * @param color + * @param alpha [0-255] + * @return + */ + public static int setAlpha(int color, float alpha) { + return (color & 0x00ffffff) | ((int) (alpha * 255.0f + 0.5f) << 24); + } + + /** + * 改变颜色值中的红色色值 + * @param color + * @param red [0-255] + * @return + */ + public static int setRed(int color, int red) { + return (color & 0xff00ffff) | (red << 16); + } + + /** + * 改变颜色值中的红色色值 + * @param color + * @param red [0-255] + * @return + */ + public static int setRed(int color, float red) { + return (color & 0xff00ffff) | ((int) (red * 255.0f + 0.5f) << 16); + } + + /** + * 改变颜色值中的绿色色值 + * @param color + * @param green [0-255] + * @return + */ + public static int setGreen(int color, int green) { + return (color & 0xffff00ff) | (green << 8); + } + + /** + * 改变颜色值中的绿色色值 + * @param color + * @param green [0-255] + * @return + */ + public static int setGreen(int color, float green) { + return (color & 0xffff00ff) | ((int) (green * 255.0f + 0.5f) << 8); + } + + /** + * 改变颜色值中的蓝色色值 + * @param color + * @param blue [0-255] + * @return + */ + public static int setBlue(int color, int blue) { + return (color & 0xffffff00) | blue; + } + + /** + * 改变颜色值中的蓝色色值 + * @param color + * @param blue [0-255] + * @return + */ + public static int setBlue(int color, float blue) { + return (color & 0xffffff00) | (int) (blue * 255.0f + 0.5f); + } + + // == + + /** + * 解析颜色字符串, 返回对应的颜色值 + * @param colorString + * @return + */ + private static int priParseColor(String colorString) { + if (colorString.charAt(0) == '#') { + // Use a long to avoid rollovers on #ffXXXXXX + long color = Long.parseLong(colorString.substring(1), 16); + if (colorString.length() == 7) { + // Set the alpha value + color |= 0x00000000ff000000; + } else if (colorString.length() != 9) { + throw new IllegalArgumentException("Unknown color"); + } + return (int)color; + } else { + Integer color = sColorNameMap.get(colorString.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' + */ + public static int parseColor(String colorString) { + try { + return priParseColor(colorString); + } catch (Exception e){ + JCLogUtils.eTag(TAG, e, "parseColor"); + } + return -1; + } + + /** + * 颜色值 转换 RGB颜色 字符串 + * @param colorInt + * @return + */ + public static String int2RgbString(int colorInt) { + colorInt = colorInt & 0x00ffffff; + String colorStr = Integer.toHexString(colorInt); + while (colorStr.length() < 6) { + colorStr = "0" + colorStr; + } + return "#" + colorStr; + } + + /** + * 颜色值 转换 ARGB颜色 字符串 + * @param colorInt + * @return + */ + public static String int2ArgbString(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 + */ + public static int getRandomColor(boolean supportAlpha) { + int high = supportAlpha ? (int) (Math.random() * 0x100) << 24 : 0xFF000000; + return high | (int) (Math.random() * 0x1000000); + } + + /** + * 判断是否为ARGB格式的十六进制颜色,例如:FF990587 + * @param str + * @return + */ + public static boolean judgeColorString(String str) { + if (str.length() == 8) { + for (int i = 0; i < str.length(); i++) { + char cc = str.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 + * @param darkValue + * @return + */ + public static int setDark(String colorStr, int darkValue){ + int color = parseColor(colorStr); + if (color == -1) return -1; + return setDark(color, darkValue); + } + + /** + * 颜色加深(单独修改 RGB值, 不变动透明度) + * @param color + * @param darkValue + * @return + */ + public static int setDark(int color, int darkValue){ + int red = red(color); + int green = green(color); + int blue = blue(color); + // 进行加深(累减) + red -= darkValue; + green -= darkValue; + blue -= darkValue; + // 进行设置 + color = setRed(color, clamp(red, 255, 0)); + color = setGreen(color, clamp(green, 255, 0)); + color = setBlue(color, clamp(blue, 255, 0)); + return color; + } + + /** + * 颜色变浅, 变亮(单独修改 RGB值, 不变动透明度) + * @param colorStr + * @param lightValue + * @return + */ + public static int setLight(String colorStr, int lightValue){ + int color = parseColor(colorStr); + if (color == -1) return -1; + return setLight(color, lightValue); + } + + /** + * 颜色变浅, 变亮(单独修改 RGB值, 不变动透明度) + * @param color + * @param lightValue + * @return + */ + public static int setLight(int color, int lightValue){ + int red = red(color); + int green = green(color); + int blue = blue(color); + // 进行变浅, 变亮(累加) + red += lightValue; + green += lightValue; + blue += lightValue; + // 进行设置 + color = setRed(color, clamp(red, 255, 0)); + color = setGreen(color, clamp(green, 255, 0)); + color = setBlue(color, clamp(blue, 255, 0)); + return color; + } + + /** + * 设置透明度加深 + * @param colorStr + * @param darkValue + * @return + */ + public static int setAlphaDark(String colorStr, int darkValue){ + int color = parseColor(colorStr); + if (color == -1) return -1; + return setAlphaDark(color, darkValue); + } + + /** + * 设置透明度加深 + * @param color + * @param darkValue + * @return + */ + public static int setAlphaDark(int color, int darkValue){ + int alpha = alpha(color); + // 透明度加深 + alpha += darkValue; + // 进行设置 + color = setAlpha(color, clamp(alpha, 255, 0)); + return color; + } + + /** + * 设置透明度变浅 + * @param colorStr + * @param lightValue + * @return + */ + public static int setAlphaLight(String colorStr, int lightValue){ + int color = parseColor(colorStr); + if (color == -1) return -1; + return setAlphaLight(color, lightValue); + } + + /** + * 设置透明度变浅 + * @param color + * @param lightValue + * @return + */ + public static int setAlphaLight(int color, int lightValue){ + int alpha = alpha(color); + // 透明度变浅 + alpha -= lightValue; + // 进行设置 + color = setAlpha(color, clamp(alpha, 255, 0)); + return color; + } + + // = + + private static final HashMap sColorNameMap; + static { + sColorNameMap = new HashMap<>(); + sColorNameMap.put("transparent", TRANSPARENT); + sColorNameMap.put("white", WHITE); + sColorNameMap.put("black", BLACK); + sColorNameMap.put("red", RED); + sColorNameMap.put("green", GREEN); + sColorNameMap.put("blue", BLUE); + sColorNameMap.put("gray", GRAY); + sColorNameMap.put("grey", GRAY); + sColorNameMap.put("skyblue", SKYBLUE); + sColorNameMap.put("orange", ORANGE); + sColorNameMap.put("gold", GOLD); + sColorNameMap.put("pink", PINK); + sColorNameMap.put("fuchsia", FUCHSIA); + sColorNameMap.put("graywhite", GRAYWHITE); + sColorNameMap.put("purple", PURPLE); + sColorNameMap.put("cyan", CYAN); + sColorNameMap.put("yellow", YELLOW); + sColorNameMap.put("chocolate", CHOCOLATE); + sColorNameMap.put("tomato", TOMATO); + sColorNameMap.put("orangered", ORANGERED); + sColorNameMap.put("silver", SILVER); + sColorNameMap.put("darkgray", DKGRAY); + sColorNameMap.put("lightgray", LTGRAY); + sColorNameMap.put("lightgrey", LTGRAY); + sColorNameMap.put("magenta", MAGENTA); + sColorNameMap.put("highlight", HIGHLIGHT); + sColorNameMap.put("lowlight", LOWLIGHT); + sColorNameMap.put("aqua", 0xFF00FFFF); + sColorNameMap.put("lime", 0xFF00FF00); + sColorNameMap.put("maroon", 0xFF800000); + sColorNameMap.put("navy", 0xFF000080); + sColorNameMap.put("olive", 0xFF808000); + sColorNameMap.put("teal", 0xFF008080); + } +} 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 index c0995772dc..e1cb244fe7 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/ConverUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/ConverUtils.java @@ -10,14 +10,14 @@ */ public final class ConverUtils { - private ConverUtils(){ + private ConverUtils() { } // 日志TAG private static final String TAG = ConverUtils.class.getSimpleName(); // byte是字节数据类型、有符号型的、占1个字节、大小范围为-128——127 - // char是字符数据类型、无符号型的、占2个字节(unicode码)、大小范围为0-65535 + // char是字符数据类型、无符号型的、占2个字节(unicode码)、大小范围为0-65535 // byte[] (-128) - 127 // 当大于127时则开始缩进 127 = 127, 128 = -128 , 129 = -127 @@ -28,11 +28,11 @@ private ConverUtils(){ * @param dfStr * @return */ - public static String toString(char[] chars, String dfStr){ - if (chars != null){ + public static String toString(char[] chars, String dfStr) { + if (chars != null) { try { return new String(chars); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "toString"); } } @@ -45,11 +45,11 @@ public static String toString(char[] chars, String dfStr){ * @param dfStr * @return */ - public static String toString(byte[] bytes, String dfStr){ - if (bytes != null){ + public static String toString(byte[] bytes, String dfStr) { + if (bytes != null) { try { return new String(bytes); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "toString"); } } @@ -64,7 +64,7 @@ public static String toString(byte[] bytes, String dfStr){ * 97 - 122 = a-z, 48-57 = 0-9 * toString((char) 97); = a */ - public static String toString(char val){ + public static String toString(char val) { return Character.toString(val); } @@ -74,55 +74,55 @@ public static String toString(char val){ * @return */ public static String toString(Object obj, String dfStr) { - if (obj != null){ + if (obj != null) { // return (obj instanceof String ? (String) obj : obj.toString()); try { - if (obj instanceof String){ + if (obj instanceof String) { return (String) obj; } else { Class cla = obj.getClass(); // 判断是否数组类型 - if (cla.isArray()){ + if (cla.isArray()) { // == 基本数据类型 == - if (cla.isAssignableFrom(int[].class)){ + if (cla.isAssignableFrom(int[].class)) { return Arrays.toString((int[]) obj); - } else if (cla.isAssignableFrom(boolean[].class)){ + } else if (cla.isAssignableFrom(boolean[].class)) { return Arrays.toString((boolean[]) obj); - } else if (cla.isAssignableFrom(long[].class)){ + } else if (cla.isAssignableFrom(long[].class)) { return Arrays.toString((long[]) obj); - } else if (cla.isAssignableFrom(double[].class)){ + } else if (cla.isAssignableFrom(double[].class)) { return Arrays.toString((double[]) obj); - } else if (cla.isAssignableFrom(float[].class)){ + } else if (cla.isAssignableFrom(float[].class)) { return Arrays.toString((float[]) obj); - } else if (cla.isAssignableFrom(byte[].class)){ + } else if (cla.isAssignableFrom(byte[].class)) { return Arrays.toString((byte[]) obj); - } else if (cla.isAssignableFrom(char[].class)){ + } else if (cla.isAssignableFrom(char[].class)) { return Arrays.toString((char[]) obj); - } else if (cla.isAssignableFrom(short[].class)){ + } else if (cla.isAssignableFrom(short[].class)) { return Arrays.toString((short[]) obj); } // == 基本类型封装 == - if (cla.isAssignableFrom(Integer[].class)){ + if (cla.isAssignableFrom(Integer[].class)) { return Arrays.toString((Integer[]) obj); - } else if (cla.isAssignableFrom(Boolean[].class)){ + } else if (cla.isAssignableFrom(Boolean[].class)) { return Arrays.toString((Boolean[]) obj); - } else if (cla.isAssignableFrom(Long[].class)){ + } else if (cla.isAssignableFrom(Long[].class)) { return Arrays.toString((Long[]) obj); - } else if (cla.isAssignableFrom(Double[].class)){ + } else if (cla.isAssignableFrom(Double[].class)) { return Arrays.toString((Double[]) obj); - } else if (cla.isAssignableFrom(Float[].class)){ + } else if (cla.isAssignableFrom(Float[].class)) { return Arrays.toString((Float[]) obj); - } else if (cla.isAssignableFrom(Byte[].class)){ + } else if (cla.isAssignableFrom(Byte[].class)) { return Arrays.toString((Byte[]) obj); - } else if (cla.isAssignableFrom(Character[].class)){ + } else if (cla.isAssignableFrom(Character[].class)) { return Arrays.toString((Character[]) obj); - } else if (cla.isAssignableFrom(Short[].class)){ + } else if (cla.isAssignableFrom(Short[].class)) { return Arrays.toString((Short[]) obj); } } return obj.toString(); } - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "toString"); } } @@ -150,10 +150,10 @@ public static int toInt(String str, int dfValue) { * @param dfValue * @return */ - public static boolean toBoolean(String str, boolean dfValue){ + public static boolean toBoolean(String str, boolean dfValue) { try { // 判断是否0 - if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("1")){ + if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("1")) { return true; } else { return false; @@ -170,7 +170,7 @@ public static boolean toBoolean(String str, boolean dfValue){ * @param dfValue * @return */ - public static float toFloat(String str, float dfValue){ + public static float toFloat(String str, float dfValue) { try { return Float.parseFloat(str); } catch (Exception e) { @@ -185,7 +185,7 @@ public static float toFloat(String str, float dfValue){ * @param dfValue * @return */ - public static double toDouble(String str, double dfValue){ + public static double toDouble(String str, double dfValue) { try { return Double.parseDouble(str); } catch (Exception e) { @@ -200,7 +200,7 @@ public static double toDouble(String str, double dfValue){ * @param dfValue * @return */ - public static long toLong(String str, long dfValue){ + public static long toLong(String str, long dfValue) { try { return Long.parseLong(str); } catch (Exception e) { @@ -231,7 +231,7 @@ public static int toInt(Integer val, int dfValue) { * @param val * @return */ - public static boolean toBoolean(Boolean val){ + public static boolean toBoolean(Boolean val) { return (val != null && val); } @@ -241,7 +241,7 @@ public static boolean toBoolean(Boolean val){ * @param dfValue * @return */ - public static float toFloat(Float val, float dfValue){ + public static float toFloat(Float val, float dfValue) { try { return val; } catch (Exception e) { @@ -256,7 +256,7 @@ public static float toFloat(Float val, float dfValue){ * @param dfValue * @return */ - public static double toDouble(Double val, double dfValue){ + public static double toDouble(Double val, double dfValue) { try { return val; } catch (Exception e) { @@ -271,7 +271,7 @@ public static double toDouble(Double val, double dfValue){ * @param dfValue * @return */ - public static long toLong(Long val, long dfValue){ + public static long toLong(Long val, long dfValue) { try { return val; } catch (Exception e) { @@ -287,7 +287,7 @@ public static long toLong(Long val, long dfValue){ * @param val * @return */ - public static int toInt(char val){ + public static int toInt(char val) { return (int) val; } @@ -297,7 +297,7 @@ public static int toInt(char val){ * @param dfValue * @return */ - public static char toChar(String str, char dfValue){ + public static char toChar(String str, char dfValue) { return toChar(str, 0, dfValue); } @@ -308,10 +308,10 @@ public static char toChar(String str, char dfValue){ * @param dfValue * @return */ - public static char toChar(String str, int pos, char dfValue){ + public static char toChar(String str, int pos, char dfValue) { try { return str.charAt(pos); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "toChar"); } return dfValue; @@ -324,7 +324,7 @@ public static char toChar(String str, int pos, char dfValue){ * -- * toCharInt('a') = 97 */ - public static int toCharInt(char val){ + public static int toCharInt(char val) { return (int) val; } @@ -333,10 +333,10 @@ public static int toCharInt(char val){ * @param str * @return */ - public static char[] toCharArys(String str){ + public static char[] toCharArys(String str) { try { return str.toCharArray(); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "toCharArys"); } return null; @@ -347,7 +347,7 @@ public static char[] toCharArys(String str){ * @param str * @return */ - public static byte[] toByteArys(String str){ + public static byte[] toByteArys(String str) { if (str != null) { try { return str.getBytes(); @@ -359,29 +359,50 @@ public static byte[] toByteArys(String str){ } // ====================================================================== +// Binary(二进制) toBinaryString +// Oct(八进制) +// Dec(十进制) +// Hex(十六进制) +// +// https://baike.baidu.com/item/ASCII/309296?fr=aladdin#3 // 以0x开始的数据表示16进制 /** - * 一个整数参数的字符串表示形式在基数为16的无符号整数 + * 一个 int 参数的字符串表示形式在基数为16的无符号 int * @param val * @return * -- * 例如 -> 传入 0x1f603 => toHexString(0x1f603); 返回: 1f603 */ - public static String toHexString(int val){ + public static String toHexString(int val) { return Integer.toHexString(val); } - public static String toHexString(long val){ + /** + * 一个 long 参数的字符串表示形式在基数为16的无符号 long + * @param val + * @return + */ + public static String toHexString(long val) { return Long.toHexString(val); } - public static String toHexString(double val){ + /** + * 一个 double 参数的字符串表示形式在基数为16的无符号 double + * @param val + * @return + */ + public static String toHexString(double val) { return Double.toHexString(val); } - public static String toHexString(float val){ + /** + * 一个 float 参数的字符串表示形式在基数为16的无符号 float + * @param val + * @return + */ + public static String toHexString(float val) { return Float.toHexString(val); } @@ -395,7 +416,7 @@ public static String toHexString(float val){ * -- * 如: parseInt("1f603", 16) = 128515 */ - public static int parseInt(String s, int radix){ + public static int parseInt(String s, int radix) { return Integer.parseInt(s, radix); } @@ -421,7 +442,7 @@ public static int parseInt(String s, int radix){ * @return */ public static String toHexString(byte[] bData, char[] hexDigits) { - if (bData == null || hexDigits == null){ + if (bData == null || hexDigits == null) { return null; } StringBuilder sBuilder = new StringBuilder(bData.length * 2); @@ -537,6 +558,23 @@ public static byte[] chars2Bytes(final char[] chars) { return bytes; } + /** + * byte 数组转换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); + } + /** * 判断字符串是否为 null 或全为空白字符 * @param str 待校验字符串 diff --git a/DevLibUtils/src/main/java/dev/utils/common/CoordinateUtils.java b/DevLibUtils/src/main/java/dev/utils/common/CoordinateUtils.java index a8bf0ce0c9..177897c049 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/CoordinateUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/CoordinateUtils.java @@ -3,17 +3,32 @@ import static java.lang.Math.PI; /** - * detail: 坐标相关工具类 - * @author Blankj + * detail: 坐标相关工具类 - GPS 纠偏 + * Created by Ttt + * =================== + * 地球坐标系 (WGS-84) + * 火星坐标系 (GCJ-02) + * 百度坐标系 (BD09) + * =================== + * https://github.com/hujiulong/gcoord + * =================== + * 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 坐标 diff --git a/DevLibUtils/src/main/java/dev/utils/common/DateUtils.java b/DevLibUtils/src/main/java/dev/utils/common/DateUtils.java index df2e8ca147..1b1eae314b 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/DateUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/DateUtils.java @@ -18,15 +18,22 @@ 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 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 MMdd = "MM月dd日"; - // -- + public static final String hhmmssMMDDyyyy = "hh:mm:ss M月d日 yyyy"; + /** 一分钟 60秒 */ public static final int MINUTE_S = 60; /** 一小时 60 * 60秒 */ @@ -53,7 +60,7 @@ private DateUtils() { * 获取当前日期的字符串 - "yyyy-MM-dd HH:mm:ss" * @return 字符串 */ - public static String getDateNow(){ + public static String getDateNow() { return getDateNow(yyyyMMddHHmmss); } @@ -83,7 +90,7 @@ public static String getDateNow(String format) { * @param format 日期格式 * @return 按照需求格式的日期字符串 */ - public static String formatTime(long time, String format){ + public static String formatTime(long time, String format) { try { return new SimpleDateFormat(format).format(time); } catch (Exception e) { @@ -112,7 +119,7 @@ public static String formatDate(Date date, String format) { * @param time * @return */ - public static Date parseDate(long time){ + public static Date parseDate(long time) { try { return new Date(time); } catch (Exception e) { @@ -126,7 +133,7 @@ public static Date parseDate(long time){ * @param time * @return */ - public static Date parseDate(String time){ + public static Date parseDate(String time) { return parseDate(time, yyyyMMddHHmmss); } @@ -155,7 +162,7 @@ public static Date parseDate(String time, String format) { * @param time * @return */ - public static long parseLong(String time){ + public static long parseLong(String time) { return parseLong(time, yyyyMMddHHmmss); } @@ -188,7 +195,7 @@ public static long parseLong(String time, String format) { * @param time(毫秒) * @return */ - public static int getTimeDiffMinute(long time){ + public static int getTimeDiffMinute(long time) { return (int) (time / 60000); // 60 * 1000 } @@ -197,7 +204,7 @@ public static int getTimeDiffMinute(long time){ * @param time(毫秒) * @return */ - public static int getTimeDiffHour(long time){ + public static int getTimeDiffHour(long time) { return (int) (time / 3600000); // 60 * 1000 * 60 } @@ -206,7 +213,7 @@ public static int getTimeDiffHour(long time){ * @param time(毫秒) * @return */ - public static int getTimeDiffDay(long time){ + public static int getTimeDiffDay(long time) { return (int) (time / 86400000); // 60 * 1000 * 60 * 24 } @@ -215,7 +222,7 @@ public static int getTimeDiffDay(long time){ * @param time * @return */ - public static long getTimeDiff(long time){ + public static long getTimeDiff(long time) { return time - System.currentTimeMillis(); } @@ -225,10 +232,10 @@ public static long getTimeDiff(long time){ * @param timeVal2 * @return */ - public static long getTimeDiff(String timeVal1, String timeVal2){ + public static long getTimeDiff(String timeVal1, String timeVal2) { long time1 = parseLong(timeVal1); long time2 = parseLong(timeVal2); - if (time1 > 1l && time2 > 1l){ + if (time1 > 1l && time2 > 1l) { return time1 - time2; } return -2l; @@ -242,10 +249,10 @@ public static long getTimeDiff(String timeVal1, String timeVal2){ * @param timeFormat2 * @return */ - public static long getTimeDiff(String timeVal1, String timeFormat1, String timeVal2, String timeFormat2){ + 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){ + if (time1 > 1l && time2 > 1l) { return time1 - time2; } return -2l; @@ -306,6 +313,22 @@ public static int getDay(Date date) { return -1; } + /** + * 获取日期是星期几 + * @param date Date对象 + * @return 日 + */ + public static int getWeek(Date date) { + try { + Calendar c = Calendar.getInstance(); + c.setTime(date); + int week = c.get(Calendar.DAY_OF_WEEK); + return week; + } catch (Exception e) { + } + return -1; + } + /** * 获取时 - 24 * @param date Date对象 @@ -366,8 +389,14 @@ public static int getSecond(Date date) { return -1; } - public static String convertTime(int time, boolean isAppand){ - if (isAppand){ + /** + * 转换时间处理, 小于10, 则自动补充 0x + * @param time + * @param append + * @return + */ + public static String convertTime(int time, boolean append) { + if (append) { return time >= 10 ? time + "" : "0" + time; } return time + ""; @@ -377,6 +406,7 @@ public static String convertTime(int time, boolean isAppand){ /** * 获取年 + * @return */ public static int getYear() { return Calendar.getInstance().get(Calendar.YEAR); @@ -384,6 +414,7 @@ public static int getYear() { /** * 获取月 (0 - 11) + 1 + * @return */ public static int getMonth() { int month = Calendar.getInstance().get(Calendar.MONTH) + 1; @@ -392,13 +423,23 @@ public static int getMonth() { /** * 获取日 + * @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 */ public static int get24Hour() { return Calendar.getInstance().get(Calendar.HOUR_OF_DAY); @@ -406,6 +447,7 @@ public static int get24Hour() { /** * 获取时 - 12 + * @return */ public static int get12Hour() { return Calendar.getInstance().get(Calendar.HOUR); @@ -413,6 +455,7 @@ public static int get12Hour() { /** * 获取分 + * @return */ public static int getMinute() { return Calendar.getInstance().get(Calendar.MINUTE); @@ -442,12 +485,12 @@ public static boolean isLeapYear(int year) { } /** - * 获取月份 - 对应天数 + * 根据年份、月份,获取对应的天数 (完整天数, 无判断是否属于未来日期) * @param year 年数 * @param month 月份 * @return */ - public static int getMonthDayNumber(int year, int month) { + public static int getMonthDayNumberAll(int year, int month) { int number = 31; // 判断返回的标识数字 switch(month) { @@ -476,12 +519,45 @@ public static int getMonthDayNumber(int year, int month) { } return number; } + + /** + * 根据年份,获取对应的月份 + * @param year + * @return + * tips: 内部判断是否相同一年, 不能超过限制未来的月份 + */ + public static int getYearMonthNumber(int year) { + // 如: 当前 2019-01, 传入 2019 则返回 1 + // 传入 2018, 返回 12, 不会返回未来的月份 + if (year == getYear()){ + return getMonth(); + } + return 12; + } + + /** + * 根据年份、月份,获取对应的天数 + * @param year + * @param month + * @return + * tips: 内部判断是否相同一年、月份, 不能超过限制未来的天数 + */ + public static int getMonthDayNumber(int year, int month) { + // 判断年份, 相同则判断月份 + if (year == getYear()){ + // 判断月份, 先同则返回天数 + if (getYearMonthNumber(year) == month) { + return getDay(); + } + } + return getMonthDayNumberAll(year, month); + } // ======================================================= /** * 传入时间,获取时间(00:00:00 格式) - 不处理大于一天 - * @param time 时间(秒为单位) + * @param time 时间(秒为单位) * @return */ public static String secToTimeRetain(int time) { @@ -490,7 +566,7 @@ public static String secToTimeRetain(int time) { /** * 传入时间,获取时间(00:00:00 格式) - * @param time 时间(秒为单位) + * @param time 时间(秒为单位) * @param isHandlerMDay 是否处理大于一天的时间 * @return */ @@ -539,7 +615,7 @@ public static String secToTimeRetain(int time, boolean isHandlerMDay) { /** * 传入时间,时间参数(小时、分钟、秒) - * @param time 时间(秒为单位) + * @param time 时间(秒为单位) * @return */ public static int[] convertTimeArys(int time) { @@ -610,4 +686,199 @@ public static String millis2FitTimeSpan(long millis, int precision) { } return sb.toString(); } + + // == 判断是否在区间范围 == + + /** + * 判断时间是否在[startTime, endTime]区间,注意时间格式要一致 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + public static boolean isInTimeHHmm(String startTime, String endTime) { + return isInTime(DateUtils.formatTime(System.currentTimeMillis(), HHmm), startTime, endTime, HHmm); + } + + /** + * 判断时间是否在[startTime, endTime]区间,注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + public static boolean isInTimeHHmm(String nowTime, String startTime, String endTime) { + return isInTime(nowTime, startTime, endTime, HHmm); + } + + /** + * 判断时间是否在[startTime, endTime]区间,注意时间格式要一致 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + public static boolean isInTimeHHmmss(String startTime, String endTime) { + return isInTime(DateUtils.formatTime(System.currentTimeMillis(), HHmmss), startTime, endTime, HHmmss); + } + + /** + * 判断时间是否在[startTime, endTime]区间,注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + public static boolean isInTimeHHmmss(String nowTime, String startTime, String endTime) { + return isInTime(nowTime, startTime, endTime, HHmmss); + } + + /** + * 判断时间是否在[startTime, endTime]区间,注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param format 时间格式 + * @return + */ + public static boolean isInTime(String nowTime, String startTime, String endTime, 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 + */ + public static boolean isInTime(long nowTime, long startTime, long endTime) { + return isInDate(new Date(nowTime), new Date(startTime), new Date(endTime)); + } + + /** + * 判断时间是否在[startTime, endTime]区间,注意时间格式要一致 + * @param nowTime 当前时间 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + public static boolean isInDate(Date nowTime, Date startTime, 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 之前的时间 + if (now.after(begin) && now.before(end)) { + return true; + } else { + return false; + } + } + + /** + * 获取指定时间距离该时间第二天的指定时段的时间 (判断凌晨情况) + * @param endTime 结束时间 HH:mm + * @return + */ + public static long getEndTimeDiffHHmm(String endTime) { + return getEndTimeDiff(System.currentTimeMillis(), endTime, HHmm); + } + + /** + * 获取指定时间距离该时间第二天的指定时段的时间 (判断凌晨情况) + * @param startTime 开始时间 + * @param endTime 结束时间 HH:mm + * @return + */ + public static long getEndTimeDiffHHmm(long startTime, String endTime) { + return getEndTimeDiff(startTime, endTime, HHmm); + } + + /** + * 获取指定时间距离该时间第二天的指定时段的时间差 (判断凌晨情况) + * @param endTime 结束时间 + * @param format 格式 如: HH:mm + * @return + */ + public static long getEndTimeDiff(String endTime, String format) { + return getEndTimeDiff(System.currentTimeMillis(), endTime, format); + } + + /** + * 获取指定时间距离该时间第二天的指定时段的时间差 (判断凌晨情况) + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param format 格式 如: HH:mm + * @return + * tips: + * 如当前时间 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) + */ + public static long getEndTimeDiff(long startTime, String endTime, String format) { + if (startTime < 1 || endTime == null || format == null) { + return -1; + } + try { + // 判断格式是否加了秒 + boolean isSecond = format.endsWith(":ss"); + // 获取开始时间 + String start = DateUtils.formatTime(startTime, format); + // 转换时间 + int startNumber = Integer.parseInt(start.replace(":", "")); + // 获取结束时间转换 + int endNumber = Integer.parseInt(endTime.replace(":", "")); + // 时间处理 + Calendar cld = Calendar.getInstance(); + cld.setTime(new Date(startTime)); // 设置当前时间 + // 如果当前时间大于结束时间, 表示非第二天 + if (startNumber > endNumber) { + // 时间累加一天 + cld.add(Calendar.DATE,1); // 当前日期加一天 + } + // 获取天数时间 + String yyyyMMdd = DateUtils.formatDate(cld.getTime(), DateUtils.yyyyMMdd); + // 累加时间 + String yyyyMMddHHmmss = yyyyMMdd + " " + endTime + (isSecond ? "" : ":00"); + // 返回转换后的时间 + return DateUtils.parseLong(yyyyMMddHHmmss, DateUtils.yyyyMMddHHmmss); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getEndTimeDiff"); + } + return -1; + } } diff --git a/DevLibUtils/src/main/java/dev/utils/common/DevCommonUtils.java b/DevLibUtils/src/main/java/dev/utils/common/DevCommonUtils.java index 58d67fd827..6fb0cb2573 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/DevCommonUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/DevCommonUtils.java @@ -23,16 +23,124 @@ private DevCommonUtils() { /** 换行字符串 - 两行 */ public static final String NEW_LINE_STR_X2 = NEW_LINE_STR + NEW_LINE_STR; + /** + * 计算百分比值 (最大 100%) + * @param value + * @param max + * @return + */ + public static float percent(float value, 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 percent(int value, 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 percent2(float value, 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 percent2(int value, int max){ + if (max <= 0) return 0.0f; + if (value <= 0) return 0.0f; + return (float) value / (float) max; + } + + /** + * 返回的 value 介于 max、min之间,若 value 小于min,返回min,若大于max,返回max + * @param value + * @param max + * @param min + * @return + */ + public static int clamp(int value, int max, 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 + */ + public static float clamp(float value, float max, float min) { + return value > max ? max : value < min ? min : value; + } + + // = + + /** + * 获取格式化字符串 + * @param format + * @param args + * @return + */ + public static String getFormatString(String format, Object... args) { + try { + return String.format(format, args); + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFormatString"); + } + return null; + } + + /** + * 获取格式化字符串 + * @param format + * @param args + * @return + */ + public static String getFormatString2(String format, Object... args) { + try { + if (args != null && args.length != 0) { + return String.format(format, args); + } else { + return format; + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "getFormatString2"); + } + return null; + } + /** * 判断是否网络资源 * @param resPath 资源地址 * @return */ - public static boolean isHttpRes(String resPath){ - if (!isEmpty(resPath)){ + public static boolean isHttpRes(String resPath) { + if (!isEmpty(resPath)) { // 属于第一位开始, 才是属于网络资源 if (resPath.toLowerCase().startsWith("http:") || - resPath.toLowerCase().startsWith("https:")){ + resPath.toLowerCase().startsWith("https:")) { return true; } } @@ -59,10 +167,10 @@ public static boolean isSpace(final String str) { * @param number 空格数量 * @return */ - private static String getSpace(int number){ + private static String getSpace(int number) { StringBuffer buffer = new StringBuffer(); // 循环空格 - for (int i = 0; i < number; i++){ + for (int i = 0; i < number; i++) { buffer.append(" "); } return buffer.toString(); @@ -73,10 +181,10 @@ private static String getSpace(int number){ * @param number tab 键数量 * @return */ - private static String getTab(int number){ + private static String getTab(int number) { StringBuffer buffer = new StringBuffer(); // 循环空格 - for (int i = 0; i < number; i++){ + for (int i = 0; i < number; i++) { buffer.append("\t"); } return buffer.toString(); @@ -109,10 +217,10 @@ public static boolean isTrimEmpty(final String str) { * @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])){ + 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; } } @@ -132,31 +240,31 @@ public static boolean isEmpty(String... strs){ * @param obj * @return */ - public static boolean isEmpty(Object obj){ - if (obj != null){ + public static boolean isEmpty(Object obj) { + if (obj != null) { // 判断是否属于基本类型数组 - if (obj.getClass().isArray()){ + if (obj.getClass().isArray()) { try { Class cla = obj.getClass(); // == 基本数据类型 == - if (cla.isAssignableFrom(int[].class)){ + if (cla.isAssignableFrom(int[].class)) { return (((int[]) obj).length == 0); - } else if (cla.isAssignableFrom(boolean[].class)){ + } else if (cla.isAssignableFrom(boolean[].class)) { return (((boolean[]) obj).length == 0); - } else if (cla.isAssignableFrom(long[].class)){ + } else if (cla.isAssignableFrom(long[].class)) { return (((long[]) obj).length == 0); - } else if (cla.isAssignableFrom(double[].class)){ + } else if (cla.isAssignableFrom(double[].class)) { return (((double[]) obj).length == 0); - } else if (cla.isAssignableFrom(float[].class)){ + } else if (cla.isAssignableFrom(float[].class)) { return (((float[]) obj).length == 0); - } else if (cla.isAssignableFrom(byte[].class)){ + } else if (cla.isAssignableFrom(byte[].class)) { return (((byte[]) obj).length == 0); - } else if (cla.isAssignableFrom(char[].class)){ + } else if (cla.isAssignableFrom(char[].class)) { return (((char[]) obj).length == 0); - } else if (cla.isAssignableFrom(short[].class)){ + } else if (cla.isAssignableFrom(short[].class)) { return (((short[]) obj).length == 0); } - } catch (Exception e){ + } catch (Exception e) { } } return false; @@ -169,8 +277,8 @@ public static boolean isEmpty(Object obj){ * @param objs * @return */ - public static boolean isEmpty(Object[] objs){ - if (objs != null){ + public static boolean isEmpty(Object[] objs) { + if (objs != null) { return (objs.length == 0); } return true; @@ -181,8 +289,8 @@ public static boolean isEmpty(Object[] objs){ * @param list * @return */ - public static boolean isEmpty(List list){ - if (list != null){ + public static boolean isEmpty(List list) { + if (list != null) { return (list.size() == 0); } return true; @@ -193,8 +301,8 @@ public static boolean isEmpty(List list){ * @param map * @return */ - public static boolean isEmpty(Map map){ - if (map != null){ + public static boolean isEmpty(Map map) { + if (map != null) { return (map.size() == 0); } return true; @@ -205,8 +313,8 @@ public static boolean isEmpty(Map map){ * @param set * @return */ - public static boolean isEmpty(Set set){ - if (set != null){ + public static boolean isEmpty(Set set) { + if (set != null) { return (set.size() == 0); } return true; @@ -217,8 +325,8 @@ public static boolean isEmpty(Set set){ * @param queue * @return */ - public static boolean isEmpty(Queue queue){ - if (queue != null){ + public static boolean isEmpty(Queue queue) { + if (queue != null) { return (queue.size() == 0); } return true; @@ -229,8 +337,8 @@ public static boolean isEmpty(Queue queue){ * @param args * @return */ - public static boolean isEmptyObjs(Object... args){ - if (args != null){ + public static boolean isEmptyObjs(Object... args) { + if (args != null) { return (args.length == 0); } return true; @@ -254,7 +362,7 @@ public static int length(String str) { * @param objs * @return */ - public static int length(Object[] objs){ + public static int length(Object[] objs) { return length(objs, 0); } @@ -263,7 +371,7 @@ public static int length(Object[] objs){ * @param list * @return */ - public static int length(List list){ + public static int length(List list) { return length(list, 0); } @@ -272,7 +380,7 @@ public static int length(List list){ * @param map * @return */ - public static int length(Map map){ + public static int length(Map map) { return length(map, 0); } @@ -281,7 +389,7 @@ public static int length(Map map){ * @param set * @return */ - public static int length(Set set){ + public static int length(Set set) { return length(set, 0); } @@ -290,7 +398,7 @@ public static int length(Set set){ * @param queue * @return */ - public static int length(Queue queue){ + public static int length(Queue queue) { return length(queue, 0); } @@ -303,7 +411,7 @@ public static int length(Queue queue){ * @return */ public static int length(String str, int dfLength) { - if (str != null){ + if (str != null) { return str.length(); } return dfLength; @@ -315,8 +423,8 @@ public static int length(String str, int dfLength) { * @param dfLength * @return */ - public static int length(Object[] objs, int dfLength){ - if (objs != null){ + public static int length(Object[] objs, int dfLength) { + if (objs != null) { return objs.length; } return dfLength; @@ -328,8 +436,8 @@ public static int length(Object[] objs, int dfLength){ * @param dfLength * @return */ - public static int length(List list, int dfLength){ - if (list != null){ + public static int length(List list, int dfLength) { + if (list != null) { return list.size(); } return dfLength; @@ -341,8 +449,8 @@ public static int length(List list, int dfLength){ * @param dfLength * @return */ - public static int length(Map map, int dfLength){ - if (map != null){ + public static int length(Map map, int dfLength) { + if (map != null) { return map.size(); } return dfLength; @@ -354,8 +462,8 @@ public static int length(Map map, int dfLength){ * @param dfLength * @return */ - public static int length(Set set, int dfLength){ - if (set != null){ + public static int length(Set set, int dfLength) { + if (set != null) { return set.size(); } return dfLength; @@ -367,8 +475,8 @@ public static int length(Set set, int dfLength){ * @param dfLength * @return */ - public static int length(Queue queue, int dfLength){ - if (queue != null){ + public static int length(Queue queue, int dfLength) { + if (queue != null) { return queue.size(); } return dfLength; @@ -381,7 +489,7 @@ public static int length(Queue queue, int dfLength){ * @param args * @return */ - public static int lengthObjs(Object... args){ + public static int lengthObjs(Object... args) { return lengthObjsDf(0, args); } @@ -391,8 +499,8 @@ public static int lengthObjs(Object... args){ * @param dfLength * @return */ - public static int lengthObjsDf(int dfLength, Object... args){ - if (args != null){ + public static int lengthObjsDf(int dfLength, Object... args) { + if (args != null) { return args.length; } return dfLength; @@ -416,7 +524,7 @@ public static boolean isLength(String str, int length) { * @param length * @return */ - public static boolean isLength(Object[] objs, int length){ + public static boolean isLength(Object[] objs, int length) { return objs != null && objs.length == length; } @@ -426,7 +534,7 @@ public static boolean isLength(Object[] objs, int length){ * @param length * @return */ - public static boolean isLength(List list, int length){ + public static boolean isLength(List list, int length) { return list != null && list.size() == length; } @@ -436,7 +544,7 @@ public static boolean isLength(List list, int length){ * @param length * @return */ - public static boolean isLength(Map map, int length){ + public static boolean isLength(Map map, int length) { return map != null && map.size() == length; } @@ -446,7 +554,7 @@ public static boolean isLength(Map map, int length){ * @param length * @return */ - public static boolean isLength(Set set, int length){ + public static boolean isLength(Set set, int length) { return set != null && set.size() == length; } @@ -456,7 +564,7 @@ public static boolean isLength(Set set, int length){ * @param length * @return */ - public static boolean isLength(Queue queue, int length){ + public static boolean isLength(Queue queue, int length) { return queue != null && queue.size() == length; } @@ -505,7 +613,7 @@ public static boolean isEquals(boolean isIgnore, String... args) { // 获取数据长度 int len = args.length; // 如果最多只有一个数据判断,则直接跳过 - if (len <= 1){ + if (len <= 1) { return false; } // 遍历判断 @@ -540,7 +648,7 @@ public static boolean isEquals(boolean isIgnore, String... args) { * @param args * @return */ - public static boolean isOrEquals(String content, String... args){ + public static boolean isOrEquals(String content, String... args) { return isOrEquals(false, content, args); } @@ -551,12 +659,12 @@ public static boolean isOrEquals(String content, String... args){ * @param args * @return */ - public static boolean isOrEquals(boolean isIgnore, String content, String... args){ + 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){ + if (len <= 1) { return false; } // 遍历判断 @@ -588,7 +696,7 @@ public static boolean isOrEquals(boolean isIgnore, String content, String... arg * @param args * @return */ - public static boolean isContains(String content, String... args){ + public static boolean isContains(String content, String... args) { return isContains(false, content, args); } @@ -599,20 +707,20 @@ public static boolean isContains(String content, String... args){ * @param args * @return */ - public static boolean isContains(boolean isIgnore, String content, String... args){ - if (content != null && args != null && args.length != 0){ + public static boolean isContains(boolean isIgnore, String content, String... args) { + if (content != null && args != null && args.length != 0) { // 判断是否需要忽略大小写 - if (isIgnore){ + if (isIgnore) { content = content.toLowerCase(); } // 获取内容长度 int cLength = content.length(); // 遍历判断 - for (int i = 0, len = args.length; i < len; i++){ + for (int i = 0, len = args.length; i < len; i++) { // 获取参数 String val = args[i]; // 判断是否为null,或者长度为0 - if (!isEmpty(val) && cLength != 0){ + if (!isEmpty(val) && cLength != 0) { if (isIgnore) { // 转换小写 String valIgnore = val.toLowerCase(); @@ -629,7 +737,7 @@ public static boolean isContains(boolean isIgnore, String content, String... arg } else { // 下面这一串可以不要,因为判断字符串是否包含 // 已经处理了值不为null,并且需要判断的值长度不能为0,下面则不需要加上 - if (content.equals(val)){ + if (content.equals(val)) { return true; } } @@ -656,9 +764,9 @@ public static boolean isStartsWith(String content, String... args) { * @return */ public static boolean isStartsWith(boolean isIgnore, String content, String... args) { - if (!isEmpty(content) && args != null && args.length != 0){ + if (!isEmpty(content) && args != null && args.length != 0) { // 判断是否需要忽略大小写 - if (isIgnore){ + if (isIgnore) { content = content.toLowerCase(); } // 获取数据长度 @@ -668,7 +776,7 @@ public static boolean isStartsWith(boolean isIgnore, String content, String... a // 获取临时变量 String val = args[i]; // 判断是否为null,或者长度为0 - if (!isEmpty(val)){ + if (!isEmpty(val)) { if (isIgnore) { // 转换小写 String valIgnore = val.toLowerCase(); @@ -706,9 +814,9 @@ public static boolean isEndsWith(String content, String... args) { * @return */ public static boolean isEndsWith(boolean isIgnore, String content, String... args) { - if (!isEmpty(content) && args != null && args.length != 0){ + if (!isEmpty(content) && args != null && args.length != 0) { // 判断是否需要忽略大小写 - if (isIgnore){ + if (isIgnore) { content = content.toLowerCase(); } // 获取数据长度 @@ -718,7 +826,7 @@ public static boolean isEndsWith(boolean isIgnore, String content, String... arg // 获取临时变量 String val = args[i]; // 判断是否为null,或者长度为0 - if (!isEmpty(val)){ + if (!isEmpty(val)) { if (isIgnore) { // 转换小写 String valIgnore = val.toLowerCase(); @@ -796,9 +904,9 @@ public static String toCheckValue(String dfStr, String str) { * @param value2 * @return */ - public static String toCheckValue(String dfStr, String value1, String value2){ - if (isEmpty(value1)){ - if (isEmpty(value2)){ + public static String toCheckValue(String dfStr, String value1, String value2) { + if (isEmpty(value1)) { + if (isEmpty(value2)) { return dfStr; } else { return value2; @@ -814,12 +922,12 @@ public static String toCheckValue(String dfStr, String value1, String value2){ * @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++){ + 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){ + if (isEmpty(param)) { + if (i == len - 1) { return dfStr; // 属于最后一个,则返回默认值 } else { continue; // 不属于最后一个则跳过 @@ -838,13 +946,13 @@ public static String toCheckValues(String dfStr, String... params){ * @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++){ + 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){ + if (isEmpty(param)) { + if (i == len - 1) { return dfStr; // 属于最后一个,则返回默认值 } else { continue; // 不属于最后一个则跳过 @@ -857,6 +965,28 @@ public static String toCheckValuesSpace(String dfStr, String... params){ return dfStr; } + /** + * 裁减超出的内容, 并且追加符号(如 ...) + * @param maxLength 允许最大的长度 + * @param content + * @param symbol + * @return + */ + public static String subEllipsize(int maxLength, String content, String symbol) { + if (maxLength >= 1) { + // 获取内容长度 + int contentLength = length(content); + // 防止为不存在数据 + if (contentLength != 0) { + if (maxLength >= contentLength) { + return content; + } + return content.substring(0, maxLength) + toCheckValue(symbol); + } + } + return ""; + } + /** * 裁剪符号处理 * @param start 开始位置 @@ -865,22 +995,22 @@ public static String toCheckValuesSpace(String dfStr, String... params){ * @param symbol * @return */ - public static String subSymbolHide(int start, int symbolNumber, String content, String symbol){ - if (!isEmpty(content)){ - if (start <= 0 || symbolNumber <= 0){ + 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){ + 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){ // 表示后面的全部转换 + if (len <= 0) { // 表示后面的全部转换 len = length - start; } else { // 需要裁剪的数量 len = symbolNumber; @@ -903,15 +1033,15 @@ public static String subSymbolHide(int start, int symbolNumber, String content, * @param symbol * @return */ - public static String converSymbolHide(int start, String content, String symbol){ - if (!isEmpty(content)){ - if (start <= 0){ + 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){ + if (length <= start) { return content; } else { // 大于 start 位 StringBuffer stringBuffer = new StringBuffer(); @@ -920,7 +1050,7 @@ public static String converSymbolHide(int start, String content, String symbol){ // 进行平分 len /= 2; // 进行遍历保存 - for (int i = 0; i < len; i++){ + for (int i = 0; i < len; i++) { stringBuffer.append(symbol); } stringBuffer.append(content.substring(start + len, length)); @@ -939,7 +1069,7 @@ public static String converSymbolHide(int start, String content, String symbol){ * @param key 需要判断的标记 * @return */ - public static String toReplaceSEWith(String str, String key){ + public static String toReplaceSEWith(String str, String key) { return toReplaceSEWith(str, key, ""); } @@ -951,7 +1081,7 @@ public static String toReplaceSEWith(String str, String key){ * @param value 需要替换的内容 * @return */ - public static String toReplaceSEWith(String str, String key, String value){ + public static String toReplaceSEWith(String str, String key, String value) { if (!(!isEmpty(str) && !isEmpty(key) && !isEmpty(value) && !key.equals(value))) { return str; } @@ -991,7 +1121,7 @@ public static String toReplaceSEWith(String str, String key, String value){ * @param key 需要判断的标记 * @return */ - public static String toClearSEWiths(String str, String key){ + public static String toClearSEWiths(String str, String key) { if (!(!isEmpty(str) && !isEmpty(key))) { return str; } @@ -1025,7 +1155,7 @@ public static String toClearSEWiths(String str, String key){ * @param endIndex 结束裁剪的位置 * @return */ - public static String substring(String content, int endIndex){ + public static String substring(String content, int endIndex) { return substring(content, 0, endIndex, true); } @@ -1036,7 +1166,7 @@ public static String substring(String content, int endIndex){ * @param isReturn 开始位置超过限制是否返回内容 * @return */ - public static String substring(String content, int endIndex, boolean isReturn){ + public static String substring(String content, int endIndex, boolean isReturn) { return substring(content, 0, endIndex, isReturn); } @@ -1048,16 +1178,16 @@ public static String substring(String content, int endIndex, boolean isReturn){ * @param isReturn 开始位置超过限制是否返回内容 * @return */ - public static String substring(String content, int beginIndex, int endIndex, boolean isReturn){ - if (!isEmpty(content) && beginIndex >= 0 && endIndex >= 0 && endIndex >= beginIndex){ + 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){ + if (beginIndex > len) { return isReturn ? content : ""; } // 防止超过限制 - if (endIndex >= len){ + if (endIndex >= len) { endIndex = len; } return content.substring(beginIndex, endIndex); @@ -1073,7 +1203,7 @@ public static String substring(String content, int beginIndex, int endIndex, boo * @param key 需要判断的kye * @return */ - public static String toReplaceStartsWith(String str, String key){ + public static String toReplaceStartsWith(String str, String key) { return toReplaceStartsWith(str, key, ""); } @@ -1084,10 +1214,10 @@ public static String toReplaceStartsWith(String str, String key){ * @param value 需要替换的内容 * @return */ - public static String toReplaceStartsWith(String str, String key, String value){ + public static String toReplaceStartsWith(String str, String key, String value) { if (!isEmpty(str) && !isEmpty(key)) { try { - if (str.startsWith(key)){ + if (str.startsWith(key)) { return value + str.substring(key.length(), str.length()); } } catch (Exception e) { @@ -1104,7 +1234,7 @@ public static String toReplaceStartsWith(String str, String key, String value){ * @param key 需要判断的标记 * @return */ - public static String toClearStartsWith(String str, String key){ + public static String toClearStartsWith(String str, String key) { if (!(!isEmpty(str) && !isEmpty(key))) { return str; } @@ -1132,7 +1262,7 @@ public static String toClearStartsWith(String str, String key){ * @param key 需要判断的kye * @return */ - public static String toReplaceEndsWith(String content, String key){ + public static String toReplaceEndsWith(String content, String key) { return toReplaceEndsWith(content, key, ""); } @@ -1143,10 +1273,10 @@ public static String toReplaceEndsWith(String content, String key){ * @param value 需要替换的内容 * @return */ - public static String toReplaceEndsWith(String content, String key, String value){ + public static String toReplaceEndsWith(String content, String key, String value) { if (!isEmpty(content) && !isEmpty(key)) { try { - if (content.endsWith(key)){ + if (content.endsWith(key)) { return content.substring(0, content.length() - key.length()) + value; } } catch (Exception e) { @@ -1163,7 +1293,7 @@ public static String toReplaceEndsWith(String content, String key, String value) * @param key 需要判断的标记 * @return */ - public static String toClearEndsWith(String str, String key){ + public static String toClearEndsWith(String str, String key) { if (!(!isEmpty(str) && !isEmpty(key))) { return str; } @@ -1193,18 +1323,18 @@ public static String toClearEndsWith(String str, String key){ * @param * @return */ - public static String replaceStrs(String content, String[] spArys, String[] reArys){ + public static String replaceStrs(String content, String[] spArys, String[] reArys) { // 防止数据为null - if (content != null && spArys != null && reArys != null){ + if (content != null && spArys != null && reArys != null) { String cStr = content; // 替换的特殊字符串长度 int spCount = spArys.length; // 替换的内容长度 int reCount = reArys.length; // 相同才进行处理 - if (spCount == reCount){ + if (spCount == reCount) { // 遍历进行判断 - for (int i = 0; i < spCount; i++){ + for (int i = 0; i < spCount; i++) { // 进行替换字符串 cStr = replaceStr(cStr, spArys[i], reArys[i]); } @@ -1222,9 +1352,9 @@ public static String replaceStrs(String content, String[] spArys, String[] reAry * @param reStr 替换的内容 * @return */ - public static String replaceStr(String content, String spStr, String reStr){ + public static String replaceStr(String content, String spStr, String reStr) { // 如果替换的内容或者判断的字符串为null,则直接跳过 - if (!isEmpty(content) && !isEmpty(spStr) && reStr != null){ + if (!isEmpty(content) && !isEmpty(spStr) && reStr != null) { try { return content.replaceAll(spStr, reStr); } catch (Exception e) { @@ -1241,9 +1371,9 @@ public static String replaceStr(String content, String spStr, String reStr){ * @param reStr 替换的内容 * @return 如果异常则直接返回null */ - public static String replaceStrToNull(String content, String spStr, String reStr){ + public static String replaceStrToNull(String content, String spStr, String reStr) { // 如果替换的内容或者判断的字符串为null,则直接跳过 - if (!isEmpty(content) && !isEmpty(spStr) && reStr != null){ + if (!isEmpty(content) && !isEmpty(spStr) && reStr != null) { try { return content.replaceAll(spStr, reStr); } catch (Exception e) { diff --git a/DevLibUtils/src/main/java/dev/utils/common/FieldUtils.java b/DevLibUtils/src/main/java/dev/utils/common/FieldUtils.java index d259d8bf11..cc8f2e4001 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/FieldUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/FieldUtils.java @@ -12,11 +12,11 @@ /** * detail: 域工具 - * @author mty + * Created by Ttt */ public final class FieldUtils { - private FieldUtils(){ + private FieldUtils() { } // 日志TAG @@ -48,7 +48,7 @@ public static Object set(Field f, Object obj, Object value) { f.setAccessible(true); f.set(obj, value); return f.get(obj); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "set"); } return null; @@ -64,18 +64,28 @@ public static Object get(Field f, Object obj) { try { f.setAccessible(true); return f.get(obj); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "get"); } return null; } + /** + * 是否 long 类型 + * @param field + * @return + */ public static boolean isLong(Field field) { return field.getType() == long.class || field.getType() == Long.class; } + /** + * 是否 Integer 类型 + * @param field + * @return + */ public static boolean isInteger(Field field) { - return field.getType() == int.class || field.getType() != Integer.class; + return field.getType() == int.class || field.getType() == Integer.class; } /** @@ -108,7 +118,7 @@ public static Class getComponentType(Field f) { */ public static List getAllDeclaredFields(Class claxx) { // find all field. - LinkedList fieldList = new LinkedList(); + LinkedList fieldList = new LinkedList<>(); while (claxx != null && claxx != Object.class) { Field[] fs = claxx.getDeclaredFields(); for (int i = 0; i < fs.length; i++) { diff --git a/DevLibUtils/src/main/java/dev/utils/common/FileIOUtils.java b/DevLibUtils/src/main/java/dev/utils/common/FileIOUtils.java index 57bd51669f..ba73b4c740 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/FileIOUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/FileIOUtils.java @@ -32,7 +32,6 @@ private FileIOUtils() { // 日志TAG private static final String TAG = FileIOUtils.class.getSimpleName(); - // 换行符 private static final String LINE_SEP = System.getProperty("line.separator"); // 缓存大小 @@ -423,7 +422,7 @@ public static List readFile2List(final File file, final int st, final in if (isSpace(charsetName)) { reader = new BufferedReader(new InputStreamReader(new FileInputStream(file))); } else { - reader = new BufferedReader( new InputStreamReader(new FileInputStream(file), charsetName)); + reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName)); } while ((line = reader.readLine()) != null) { if (curLine > end) break; @@ -605,7 +604,7 @@ public static byte[] readFile2BytesByMap(final File file) { * @param filePath * @return */ - private static File getFileByPath(final String filePath){ + private static File getFileByPath(final String filePath) { return filePath != null ? new File(filePath) : null; } @@ -653,7 +652,7 @@ private static boolean createOrExistsDir(final File file) { * @param file 文件路径 * @return 是否存在文件 */ - private static boolean isFileExists(final File file){ + private static boolean isFileExists(final File file) { return file != null && file.exists(); } diff --git a/DevLibUtils/src/main/java/dev/utils/common/FileUtils.java b/DevLibUtils/src/main/java/dev/utils/common/FileUtils.java index 3d8991e5da..8519d2860d 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/FileUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/FileUtils.java @@ -73,7 +73,7 @@ private static boolean isSpace(final String str) { * @param filePath * @return */ - public static File getFile(final String filePath){ + public static File getFile(final String filePath) { return getFileByPath(filePath); } @@ -83,7 +83,7 @@ public static File getFile(final String filePath){ * @param fName 文件名 * @return */ - public static File getFile(final String filePath, final String fName){ + public static File getFile(final String filePath, final String fName) { return (filePath != null && fName != null) ? new File(filePath, fName) : null; } @@ -92,7 +92,7 @@ public static File getFile(final String filePath, final String fName){ * @param filePath * @return */ - public static File getFileByPath(final String filePath){ + public static File getFileByPath(final String filePath) { return filePath != null ? new File(filePath) : null; } @@ -153,10 +153,10 @@ public static boolean createFolderByPath(final String filePath) { * @param file * @return */ - public static boolean createFolderByPath(final File file){ + public static boolean createFolderByPath(final File file) { // 创建文件夹 - 如果失败才创建 if (file != null) { - if (file.exists()){ + if (file.exists()) { return true; } else if (!file.getParentFile().mkdirs()) { return createFolder(file.getParent()); @@ -169,9 +169,9 @@ public static boolean createFolderByPath(final File file){ * 创建多个文件夹, 如果不存在则创建 * @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++){ + 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]); } } @@ -181,9 +181,9 @@ public static void createFolderByPaths(final String... filePaths){ * 创建多个文件夹, 如果不存在则创建 * @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++){ + 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]); } } @@ -272,7 +272,7 @@ public static boolean createFileByDeleteOldFile(final File file) { * @param file * @return */ - public static String getPath(final File file){ + public static String getPath(final File file) { return file != null ? file.getPath() : null; } @@ -281,7 +281,7 @@ public static String getPath(final File file){ * @param file * @return */ - public static String getAbsolutePath(final File file){ + public static String getAbsolutePath(final File file) { return file != null ? file.getAbsolutePath() : null; } @@ -292,7 +292,7 @@ public static String getAbsolutePath(final File file){ * @param file * @return */ - public static String getName(final File file){ + public static String getName(final File file) { return file != null ? file.getName() : null; } @@ -301,7 +301,7 @@ public static String getName(final File file){ * @param filePath 文件路径 * @return */ - public static String getName(final String filePath){ + public static String getName(final String filePath) { return getName(filePath, ""); } @@ -311,7 +311,7 @@ public static String getName(final String filePath){ * @param dfStr * @return */ - public static String getName(final String filePath, final String dfStr){ + public static String getName(final String filePath, final String dfStr) { if (!isSpace(filePath)) { return new File(filePath).getName(); } @@ -319,16 +319,16 @@ public static String getName(final String filePath, final String dfStr){ } /** - * 获得文件后缀名(无.,单独后缀) + * 获取文件后缀名(无.,单独后缀) * @param file * @return */ - public static String getFileSuffix(final File file){ + public static String getFileSuffix(final File file) { return getFileSuffix(getAbsolutePath(file)); } /** - * 获得文件后缀名(无.,单独后缀) + * 获取文件后缀名(无.,单独后缀) * @param filePath 文件地址、文件名都行 * @return */ @@ -347,7 +347,7 @@ public static String getFileSuffix(final String filePath) { } /** - * 获得文件名(无后缀) + * 获取文件名(无后缀) * @param file * @return */ @@ -356,7 +356,7 @@ public static String getFileNotSuffix(final File file) { } /** - * 获得文件名(无后缀) + * 获取文件名(无后缀) * @param filePath * @return */ @@ -365,12 +365,12 @@ public static String getFileNotSuffixToPath(final String filePath) { } /** - * 获得文件名(无后缀) + * 获取文件名(无后缀) * @param fileName 文件名 * @return */ public static String getFileNotSuffix(final String fileName) { - if (fileName != null){ + if (fileName != null) { if (fileName.lastIndexOf('.') != -1) { return fileName.substring(0, fileName.lastIndexOf('.')); } else { @@ -438,7 +438,7 @@ public static String getFileExtension(final String filePath) { * @param file 文件路径 * @return 是否存在文件 */ - public static boolean isFileExists(final File file){ + public static boolean isFileExists(final File file) { return file != null && file.exists(); } @@ -466,7 +466,7 @@ public static boolean isFileExists(final String filePath, final String fName) { * @param filePath * @return */ - public static boolean isFile(final String filePath){ + public static boolean isFile(final String filePath) { return isFile(getFileByPath(filePath)); } @@ -475,7 +475,7 @@ public static boolean isFile(final String filePath){ * @param file * @return */ - public static boolean isFile(final File file){ + public static boolean isFile(final File file) { return file != null && file.exists() && file.isFile(); } @@ -484,7 +484,7 @@ public static boolean isFile(final File file){ * @param filePath * @return */ - public static boolean isDir(final String filePath){ + public static boolean isDir(final String filePath) { return isDir(getFileByPath(filePath)); } @@ -493,7 +493,7 @@ public static boolean isDir(final String filePath){ * @param file * @return */ - public static boolean isDir(final File file){ + public static boolean isDir(final File file) { return file != null && file.exists() && file.isDirectory(); } @@ -502,7 +502,7 @@ public static boolean isDir(final File file){ * @param filePath * @return */ - public static boolean isHide(final String filePath){ + public static boolean isHide(final String filePath) { return isHide(getFileByPath(filePath)); } @@ -511,7 +511,7 @@ public static boolean isHide(final String filePath){ * @param file * @return */ - public static boolean isHide(final File file){ + public static boolean isHide(final File file) { return file != null && file.exists() && file.isHidden(); } @@ -533,7 +533,7 @@ public static long getFileLastModified(final String filePath) { * @return 文件最后修改的毫秒时间戳 */ public static long getFileLastModified(final File file) { - if (file == null) return -1; + if (file == null) return 0l; return file.lastModified(); } @@ -659,7 +659,7 @@ public static String getDirSize(final File dir) { * @param filePath * @return */ - public static long getFileLength(final String filePath){ + public static long getFileLength(final String filePath) { return getFileLength(getFileByPath(filePath)); } @@ -668,7 +668,7 @@ public static long getFileLength(final String filePath){ * @param file * @return */ - public static long getFileLength(final File file){ + public static long getFileLength(final File file) { return file != null ? file.length() : 0l; } @@ -805,7 +805,7 @@ public static boolean rename(final File file, final String newName) { * @param filePath * @return */ - public static String formatFileSize(final String filePath){ + public static String formatFileSize(final String filePath) { File file = getFileByPath(filePath); return formatFileSize(file != null ? file.length() : 0); } @@ -815,7 +815,7 @@ public static String formatFileSize(final String filePath){ * @param file * @return */ - public static String formatFileSize(final File file){ + public static String formatFileSize(final File file) { return formatFileSize(file != null ? file.length() : 0); } @@ -836,7 +836,7 @@ public static String formatFileSize(final double fileS) { fileSizeString = df.format(fileS / 1024) + "KB"; } else if (fileS < 1073741824) { fileSizeString = df.format(fileS / 1048576) + "MB"; - } else if (fileS < 1099511627776d){ + } else if (fileS < 1099511627776d) { fileSizeString = df.format(fileS / 1073741824) + "GB"; } else { fileSizeString = df.format(fileS / 1099511627776d) + "TB"; @@ -858,7 +858,7 @@ public static String formatByteMemorySize(final double byteNum) { return String.format("%.3fKB", (double) byteNum / 1024); } else if (byteNum < 1073741824) { return String.format("%.3fMB", (double) byteNum / 1048576); - } else if (byteNum < 1099511627776d){ + } else if (byteNum < 1099511627776d) { return String.format("%.3fGB", (double) byteNum / 1073741824); } else { return String.format("%.3fTB", (double) byteNum / 1099511627776d); @@ -880,7 +880,7 @@ public static String formatByteMemorySize(final int number, final double byteNum return String.format("%." + number + "fKB", (double) byteNum / 1024); } else if (byteNum < 1073741824) { return String.format("%." + number + "fMB", (double) byteNum / 1048576); - } else if (byteNum < 1099511627776d){ + } else if (byteNum < 1099511627776d) { return String.format("%." + number + "fGB", (double) byteNum / 1073741824); } else { return String.format("%." + number + "fTB", (double) byteNum / 1099511627776d); @@ -906,7 +906,7 @@ public static String getFileMD5ToString(final String filePath) { public static String getFileMD5ToString(final File file) { try { return toHexString(getFileMD5(file), HEX_DIGITS); - } catch (Exception e){ + } catch (Exception e) { } return null; } @@ -991,7 +991,7 @@ public static String getFileMD5ToString2(final File file) { * @return */ private static String toHexString(final byte[] bData, final char[] hexDigits) { - if (bData == null || hexDigits == null){ + if (bData == null || hexDigits == null) { return null; } StringBuilder sBuilder = new StringBuilder(bData.length * 2); @@ -1034,8 +1034,8 @@ public static boolean deleteFile(final File file) { * @return */ public static void deleteFiles(final String... filePaths) { - if (filePaths != null && filePaths.length != 0){ - for (int i = 0, len = filePaths.length; i < len; i++){ + if (filePaths != null && filePaths.length != 0) { + for (int i = 0, len = filePaths.length; i < len; i++) { deleteFile(filePaths[i]); } } @@ -1047,8 +1047,8 @@ public static void deleteFiles(final String... filePaths) { * @return */ public static void deleteFiles(final File... files) { - if (files != null && files.length != 0){ - for (int i = 0, len = files.length; i < len; i++){ + if (files != null && files.length != 0) { + for (int i = 0, len = files.length; i < len; i++) { deleteFile(files[i]); } } @@ -1156,7 +1156,7 @@ public static boolean saveFile(final String filePath, final String fName, final * @return 是否保存成功 */ public static boolean saveFile(final String filePath, final String fName, final String content) { - if (content != null && filePath != null && fName != null){ + if (content != null && filePath != null && fName != null) { try { // 防止文件没创建 createFolder(filePath); @@ -1183,7 +1183,7 @@ public static boolean saveFile(final String filePath, final String fName, final * @return 是否保存成功 */ public static boolean saveFile(final String filePath, final String fName, final String content, final String coding) { - if (content != null && filePath != null && fName != null){ + if (content != null && filePath != null && fName != null) { try { // 防止文件没创建 createFolder(filePath); @@ -1255,7 +1255,7 @@ public static byte[] readFileBytes(final String filePath) { * @return */ public static byte[] readFileBytes(final File file) { - if (file != null && file.exists()){ + if (file != null && file.exists()) { try { FileInputStream fin = new FileInputStream(file); int length = fin.available(); @@ -1275,7 +1275,7 @@ public static byte[] readFileBytes(final File file) { * @return */ public static byte[] readFileBytes(final InputStream iStream) { - if (iStream != null){ + if (iStream != null) { try { int length = iStream.available(); byte[] buffer = new byte[length]; @@ -1304,10 +1304,10 @@ public static String readFile(final String filePath) { * @return */ public static String readFile(final File file) { - if (file != null && file.exists()){ + if (file != null && file.exists()) { try { return readFile(new FileInputStream(file)); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "readFile"); } } @@ -1320,7 +1320,7 @@ public static String readFile(final File file) { * @return */ public static String readFile(final InputStream iStream) { - if (iStream != null){ + if (iStream != null) { try { InputStreamReader isR = new InputStreamReader(iStream); BufferedReader br = new BufferedReader(isR); @@ -1346,10 +1346,10 @@ public static String readFile(final InputStream iStream) { * @return */ public static String readFile(final InputStream iStream, final String encode) { - if (iStream != null){ + if (iStream != null) { try { InputStreamReader isR = null; - if (encode != null){ + if (encode != null) { new InputStreamReader(iStream, encode); } else { new InputStreamReader(iStream); @@ -1379,14 +1379,14 @@ public static String readFile(final InputStream iStream, final String encode) { * @param overlay 如果目标文件存在,是否覆盖 * @return 如果复制成功返回true,否则返回false */ - public static boolean copyFile(final InputStream inputStream, final String destFilePath, final boolean overlay){ - if (inputStream == null || destFilePath == null){ + 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()){ + if (destFile.isDirectory()) { return false; } if (destFile.exists()) { @@ -1438,7 +1438,7 @@ public static boolean copyFile(final InputStream inputStream, final String destF * @return 如果复制成功返回true,否则返回false */ public static boolean copyFile(final String srcFilePath, final String destFilePath, final boolean overlay) { - if (srcFilePath == null || destFilePath == null){ + if (srcFilePath == null || destFilePath == null) { return false; } File srcFile = new File(srcFilePath); @@ -1451,7 +1451,7 @@ public static boolean copyFile(final String srcFilePath, final String destFilePa // 判断目标文件是否存在 File destFile = new File(destFilePath); // 如果属于文件夹则跳过 - if (destFile.isDirectory()){ + if (destFile.isDirectory()) { return false; } if (destFile.exists()) { @@ -1556,10 +1556,10 @@ private static boolean copyFolder(final String srcFolderPath, final String destF // 获取源文件地址 - 并且进行判断 String dealSource = new File(sourcePath).getAbsolutePath(); // 属于最前才进行处理 - if (filePath.indexOf(dealSource) == 0){ + 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); @@ -1581,9 +1581,9 @@ private static boolean copyFolder(final String srcFolderPath, final String destF * @param overlay * @return */ - public static boolean moveFile(final String srcFilePath, final String destFilePath, final boolean overlay){ + public static boolean moveFile(final String srcFilePath, final String destFilePath, final boolean overlay) { // 复制文件 - if (copyFile(srcFilePath, destFilePath, overlay)){ + if (copyFile(srcFilePath, destFilePath, overlay)) { // 删除文件 return deleteFile(srcFilePath); } @@ -1597,9 +1597,9 @@ public static boolean moveFile(final String srcFilePath, final String destFilePa * @param overlay * @return */ - public static boolean moveFolder(final String srcFilePath, final String destFilePath, final boolean overlay){ + public static boolean moveFolder(final String srcFilePath, final String destFilePath, final boolean overlay) { // 复制文件夹 - if (copyFolder(srcFilePath, destFilePath, overlay)){ + if (copyFolder(srcFilePath, destFilePath, overlay)) { // 删除文件夹 return deleteFolder(srcFilePath); } @@ -2014,7 +2014,15 @@ public static List listFilesInDirWithFilter(final File dir, final FileFilt return list; } + /** + * 覆盖/替换事件 + */ public interface OnReplaceListener { - boolean onReplace(); + + /** + * 是否覆盖/替换文件 + * @return + */ + boolean onReplace(); } } diff --git a/DevLibUtils/src/main/java/dev/utils/common/HexUtils.java b/DevLibUtils/src/main/java/dev/utils/common/HexUtils.java index ecd95c966a..d0bb9af3fd 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/HexUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/HexUtils.java @@ -8,7 +8,7 @@ */ public final class HexUtils { - private HexUtils(){ + private HexUtils() { } // 日志TAG @@ -47,7 +47,7 @@ public static char[] encodeHex(byte[] data, boolean toLowerCase) { * @param toDigits 用于控制输出的char[] * @return 十六进制char[] */ - protected static char[] encodeHex(byte[] data, char[] toDigits) { + private static char[] encodeHex(byte[] data, char[] toDigits) { if (data == null || toDigits == null) return null; try { int len = data.length; @@ -58,7 +58,7 @@ protected static char[] encodeHex(byte[] data, char[] toDigits) { out[j++] = toDigits[0x0F & data[i]]; } return out; - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "encodeHex"); } return null; @@ -89,7 +89,7 @@ public static String encodeHexStr(byte[] data, boolean toLowerCase) { * @param toDigits 用于控制输出的char[] * @return 十六进制String */ - protected static String encodeHexStr(byte[] data, char[] toDigits) { + private static String encodeHexStr(byte[] data, char[] toDigits) { try { return new String(encodeHex(data, toDigits)); } catch (Exception e) { @@ -118,7 +118,7 @@ public static byte[] decodeHex(char[] data) { out[i] = (byte) (f & 0xFF); } return out; - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "decodeHex"); } return null; @@ -131,7 +131,7 @@ public static byte[] decodeHex(char[] data) { * @return 一个整数 * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常 */ - protected static int toDigit(char ch, int index) { + private 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); diff --git a/DevLibUtils/src/main/java/dev/utils/common/HttpParamsUtils.java b/DevLibUtils/src/main/java/dev/utils/common/HttpParamsUtils.java index 4e5077863e..9f45101c76 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/HttpParamsUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/HttpParamsUtils.java @@ -6,7 +6,6 @@ import java.util.Map; import dev.utils.JCLogUtils; -import dev.utils.LogPrintUtils; /** * detail: Http 参数工具类 @@ -25,7 +24,7 @@ private HttpParamsUtils() { * @param params * @return */ - public static HashMap splitParams(final String params) { + public static Map splitParams(final String params) { return splitParams(params, false); } @@ -35,33 +34,33 @@ public static HashMap splitParams(final String params) { * @param urlEncode 是否需要编码 * @return */ - public static HashMap splitParams(final String params, boolean urlEncode) { - HashMap mapParams = new HashMap<>(); + public static Map splitParams(final String params, boolean urlEncode) { + Map mapParams = new HashMap<>(); if (params != null) { // 拆分数据 String[] keyValues = params.split("&"); // 数据长度 int valLength = 0; // 进行循环遍历 - for (String val : keyValues){ + for (String val : keyValues) { // 数据不为null - if (val != null && (valLength = val.length()) != 0){ + if (val != null && (valLength = val.length()) != 0) { // 获取首位 = 索引 int indexOf = val.indexOf('='); // 不存在则不处理 - if (indexOf != -1){ + if (indexOf != -1) { // 获取key String key = val.substring(0, indexOf); // 获取value String value = null; // 防止资源浪费 - if (indexOf + 1 == valLength){ + if (indexOf + 1 == valLength) { value = ""; } else { value = val.substring(indexOf + 1, valLength); } // 判断是否编码 - if (urlEncode){ + if (urlEncode) { mapParams.put(key, urlEncode(value)); } else { mapParams.put(key, value); @@ -80,7 +79,7 @@ public static HashMap splitParams(final String params, boolean u * @param mapParams * @return */ - public static String joinReqParams(final HashMap mapParams) { + public static String joinReqParams(final Map mapParams) { return joinReqParams(mapParams, false); } @@ -90,7 +89,7 @@ public static String joinReqParams(final HashMap mapParams) { * @param urlEncode 是否需要编码 * @return */ - public static String joinReqParams(final HashMap mapParams, boolean urlEncode) { + public static String joinReqParams(final Map mapParams, boolean urlEncode) { if (mapParams != null) { int index = 0; // -- @@ -117,7 +116,7 @@ public static String joinReqParams(final HashMap mapParams, bool * @param mapParams * @return */ - public static String joinReqParamsObj(final HashMap mapParams) { + public static String joinReqParamsObj(final Map mapParams) { return joinReqParamsObj(mapParams, false); } @@ -127,7 +126,7 @@ public static String joinReqParamsObj(final HashMap mapParams) { * @param urlEncode 是否需要编码 * @return */ - public static String joinReqParamsObj(final HashMap mapParams, boolean urlEncode) { + public static String joinReqParamsObj(final Map mapParams, boolean urlEncode) { if (mapParams != null) { int index = 0; // -- @@ -139,8 +138,8 @@ public static String joinReqParamsObj(final HashMap mapParams, b if (index > 0) sBuilder.append('&'); sBuilder.append(entry.getKey()); sBuilder.append('='); - if (urlEncode){ - if (entry.getValue() instanceof String){ + if (urlEncode) { + if (entry.getValue() instanceof String) { sBuilder.append(urlEncode((String) entry.getValue())); } } else { @@ -160,7 +159,7 @@ public static String joinReqParamsObj(final HashMap mapParams, b * @param mapParams * @return */ - public static String toStringMap(final HashMap mapParams) { + public static String toStringMap(final Map mapParams) { return toStringMap(mapParams, false); } @@ -170,7 +169,7 @@ public static String toStringMap(final HashMap mapParams) { * @param urlEncode 是否需要编码 * @return */ - public static String toStringMap(final HashMap mapParams, boolean urlEncode) { + public static String toStringMap(final Map mapParams, boolean urlEncode) { if (mapParams != null) { StringBuilder sBuilder = new StringBuilder(); // -- @@ -195,13 +194,13 @@ public static String toStringMap(final HashMap mapParams, boolea // 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){ + public static void toConvertObjToMS(Map mapParams, String objStr, String key, String value) { if (mapParams != null) { String data = null; try { @@ -214,13 +213,13 @@ public static void toConvertObjToMS(HashMap mapParams, String ob } /** - * 进行转换对象处理(请求发送对象) + * 进行转换对象处理(请求发送对象) * @param mapParams * @param objStr * @param key * @param value */ - public static void toConvertObjToMO(HashMap mapParams, String objStr, String key, Object value){ + public static void toConvertObjToMO(Map mapParams, String objStr, String key, Object value) { if (mapParams != null) { Object data = null; try { @@ -253,7 +252,7 @@ public static String urlEncode(final String input, final String charsetName) { try { return URLEncoder.encode(input, charsetName); } catch (Exception e) { - LogPrintUtils.eTag(TAG, e, "urlEncode"); + JCLogUtils.eTag(TAG, e, "urlEncode"); return input; } } diff --git a/DevLibUtils/src/main/java/dev/utils/common/HttpURLConnectionUtils.java b/DevLibUtils/src/main/java/dev/utils/common/HttpURLConnectionUtils.java index 270ee578df..fd77d49ca8 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/HttpURLConnectionUtils.java +++ b/DevLibUtils/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; @@ -17,12 +16,11 @@ */ public final class HttpURLConnectionUtils { - private HttpURLConnectionUtils(){ + private HttpURLConnectionUtils() { } // 日志TAG private static final String TAG = HttpURLConnectionUtils.class.getSimpleName(); - // 请求超时时间 private static final int TIMEOUT_IN_MILLIONS = 5000; @@ -32,8 +30,9 @@ public interface CallBack { /** * 请求响应回调 * @param result + * @param response 请求头响应时间 */ - void onResponse(String result); + void onResponse(String result, long response); /** * 请求失败 @@ -86,7 +85,7 @@ public void run() { * @param callBack * @return */ - 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; @@ -99,15 +98,13 @@ 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) { // 允许写入 @@ -141,39 +138,39 @@ public static void request(final String method, final String urlStr, final HashM // 获取请求结果 String result = new String(bout.toByteArray()); // 判断是否回调 - if (callBack != null){ + if (callBack != null) { // 请求成功, 触发回调 - callBack.onResponse(result); + callBack.onResponse(result, connection.getDate()); } } else { // 响应成功,非200直接返回null - if (callBack != 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) { try { bout.close(); - } catch(Exception ignore){ + } catch(Exception ignore) { } } if (inputStream != null) { try { inputStream.close(); - } catch(Exception ignore){ + } catch(Exception ignore) { } } if (connection != null) { try { // 关闭底层连接Socket connection.disconnect(); - } catch(Exception ignore){ + } catch(Exception ignore) { } } } @@ -205,7 +202,7 @@ public interface TimeCallBack { * 获取网络时间 - 默认使用百度链接 * @param timeCallBack */ - public static void getNetTime(TimeCallBack timeCallBack){ + public static void getNetTime(TimeCallBack timeCallBack) { getNetTime(BAIDU_URL, timeCallBack); } @@ -214,7 +211,7 @@ public static void getNetTime(TimeCallBack 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() { @@ -228,7 +225,7 @@ public void run() { * @param urlStr * @param timeCallBack */ - private static void reqNetTime(String urlStr, TimeCallBack timeCallBack){ + private static void reqNetTime(String urlStr, TimeCallBack timeCallBack) { // 获取连接对象 HttpURLConnection connection = null; try { @@ -239,17 +236,17 @@ private static void reqNetTime(String urlStr, TimeCallBack timeCallBack){ // 获取时间 long date = connection.getDate(); // 获取失败, 则进行修改 - if (date <= 0){ + 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 { @@ -257,7 +254,7 @@ private static void reqNetTime(String urlStr, TimeCallBack timeCallBack){ try { // 关闭底层连接Socket connection.disconnect(); - } catch(Exception ignore){ + } catch(Exception ignore) { } } } diff --git a/DevLibUtils/src/main/java/dev/utils/common/ObjectUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ObjectUtils.java index 10da8a01a8..73ce9a344f 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/ObjectUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/ObjectUtils.java @@ -91,11 +91,22 @@ public static T getOrDefault(T object, T defaultObject) { /** * 获取对象哈希值 - * @param o 对象 + * @param object 对象 * @return 哈希值 */ - public static int hashCode(Object o) { - return o != null ? o.hashCode() : 0; + public static int hashCode(Object object) { + return object != null ? object.hashCode() : 0; + } + + /** + * 获取一个对象的独一无二的标记 + * @param object + * @return + */ + public static String getObjectTag(Object object) { + if (object == null) return null; + // 对象所在的包名 + 对象的内存地址 + return object.getClass().getName() + Integer.toHexString(object.hashCode()); } /** @@ -107,7 +118,7 @@ public static int hashCode(Object o) { public static T converObj(Object obj) { try { return (T) obj; - } catch (Exception e){ + } 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 index c44bcfeea5..978daed46b 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/QuickCommonUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/QuickCommonUtils.java @@ -1,19 +1,26 @@ package dev.utils.common; +import android.os.SystemClock; + +import dev.utils.JCLogUtils; + /** * detail: 快捷通用 * Created by Ttt */ public final class QuickCommonUtils { - private QuickCommonUtils(){ + private QuickCommonUtils() { } + // 日志TAG + private static final String TAG = QuickCommonUtils.class.getSimpleName(); + /** * 转换手机号 * @param phone */ - public static String converHideMobile(String phone){ + public static String converHideMobile(String phone) { return converHideMobile(phone, "*"); } @@ -22,10 +29,20 @@ public static String converHideMobile(String phone){ * @param phone * @param symbol 符号 */ - public static String converHideMobile(String phone, String symbol){ + public static String converHideMobile(String phone, String symbol) { return DevCommonUtils.converSymbolHide(3, phone, symbol); } + /** + * 耗时时间记录 + * @param buffer + * @param sTime 开始时间 + * @param eTime 结束时间 + */ + public static void timeRecord(StringBuffer buffer, long sTime, long eTime) { + timeRecord(buffer, null, sTime, eTime); + } + /** * 耗时时间记录 * @param buffer @@ -37,9 +54,125 @@ public static void timeRecord(StringBuffer buffer, String title, long sTime, lon // 使用时间 long uTime = eTime - sTime; // 计算时间 - buffer.append("\n" + title); - buffer.append("\n开始时间:" + sTime); - buffer.append("\n结束时间:" + eTime); - buffer.append("\n所用时间:" + uTime); + if (!DevCommonUtils.isEmpty(title)) { + buffer.append("\n" + title); + } + // 计算时间 + buffer.append("\n开始时间:" + DateUtils.formatTime(sTime, DateUtils.yyyyMMddHHmmss)); + buffer.append("\n结束时间:" + DateUtils.formatTime(eTime, DateUtils.yyyyMMddHHmmss)); + buffer.append("\n所用时间(毫秒):" + uTime); + buffer.append("\n所用时间(秒): " + (uTime / 1000)); + } + + /** + * 获取格式化字符串(可变参数) + * @param args + * @return + */ + public static String getFormatString(Object... args) { + if (args != null && args.length != 0) { + int length = args.length; + StringBuffer buffer = new StringBuffer(); + buffer.append("%s"); + if (length > 1) { + for (int i = 1; i < length; i++) { + buffer.append(" %s"); + } + } + return String.format(buffer.toString(), args); + } + return "args is null"; + } + + /** + * 获取格式化字符串(可变参数) + * @param args + * @return + */ + public static String getFormatString2(Object... args) { + if (args != null && args.length != 0) { + int length = args.length; + StringBuffer buffer = new StringBuffer(); + buffer.append("【%s】"); + if (length > 1) { + for (int i = 1; i < length; i++) { + buffer.append(" %s"); + } + } + return String.format(buffer.toString(), args); + } + return "args is null"; + } + + /** + * 获取操作时间 + * @param operateTime 操作时间(毫秒) + * @return + */ + public static long getOperateTime(long operateTime) { + return getOperateTime(operateTime, -1); + } + + /** + * 获取操作时间 + * @param operateTime 操作时间(毫秒) + * @param randomTime 随机范围(毫秒) + */ + public static long getOperateTime(long operateTime, int randomTime) { + int random = 0; + // 大于2才处理 + if (randomTime >= 2) { + // 随机时间 + random = RandomUtils.getRandom(randomTime); + } + // 返回操作时间 + return Math.max(0, operateTime) + random; + } + + /** + * 堵塞操作 + * @param sleepTime + */ + public static void sleepOperate(long sleepTime) { + sleepOperate(sleepTime, -1, false); + } + + /** + * 堵塞操作 + * @param sleepTime + * @param randomTime + */ + public static void sleepOperate(long sleepTime, int randomTime) { + sleepOperate(sleepTime, randomTime, false); + } + + /** + * 堵塞操作 + * @param sleepTime + * @param randomTime + * @param isSystemClock + */ + public static void sleepOperate(long sleepTime, int randomTime, boolean isSystemClock) { + long time = getOperateTime(sleepTime, randomTime); + if (time != -1) { + if (isSystemClock) { + try { + SystemClock.sleep(sleepTime); + } catch (Throwable e) { + JCLogUtils.eTag(TAG, e, "sleepOperate - SystemClock"); + try { + Thread.sleep(sleepTime); + } catch (Throwable e1) { + JCLogUtils.eTag(TAG, e1, "sleepOperate - SystemClock Thread"); + } + } + } else { + try { + Thread.sleep(sleepTime); + } catch (Throwable e1) { + JCLogUtils.eTag(TAG, e1, "sleepOperate - Thread"); + } + } + } } } diff --git a/DevLibUtils/src/main/java/dev/utils/common/RandomUtils.java b/DevLibUtils/src/main/java/dev/utils/common/RandomUtils.java index 8dec43e263..153e523795 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/RandomUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/RandomUtils.java @@ -40,10 +40,21 @@ private RandomUtils() { // = + /** + * 获取伪随机 boolean 值 + * @param random + * @return + */ public static boolean nextBoolean(Random random) { return random.nextBoolean(); } + /** + * 获取伪随机 byte[] + * @param random + * @param buf + * @return + */ public static byte[] nextBytes(Random random, byte[] buf) { try { random.nextBytes(buf); @@ -52,60 +63,129 @@ public static byte[] nextBytes(Random random, byte[] buf) { return buf; } + /** + * 获取伪随机 double 值 + * @param random + * @return + */ public static double nextDouble(Random random) { return random.nextDouble(); } - public synchronized double nextGaussian(Random random) { + /** + * 获取伪随机高斯分布值 + * @param random + * @return + */ + public static double nextGaussian(Random random) { return random.nextGaussian(); } + /** + * 获取伪随机 float 值 + * @param random + * @return + */ public static float nextFloat(Random random) { return random.nextFloat(); } + /** + * 获取伪随机 int 值 + * @param random + * @return + */ public static int nextInt(Random random) { return random.nextInt(); } + /** + * 获取伪随机 int 值 - 该值介于 [0,n) 的区间 + * @param random + * @param n + * @return + */ public static int nextInt(Random random, int n) { return random.nextInt(n); } + /** + * 获取伪随机 long 值 + * @param random + * @return + */ public static long nextLong(Random random) { return random.nextLong(); } - // == ----------------------------------------- == + // === + /** + * 获取伪随机 boolean 值 + * @return + */ public static boolean nextBoolean() { return new Random().nextBoolean(); } + /** + * 获取伪随机 byte[] + * @param buf + * @return + */ public static byte[] nextBytes(byte[] buf) { - return nextBytes(new Random(), buf); + try { + new Random().nextBytes(buf); + } catch (Exception e) { + } + return buf; } + /** + * 获取伪随机 double 值 + * @return + */ public static double nextDouble() { - return Math.random(); + return new Random().nextDouble(); } - public synchronized double nextGaussian() { + /** + * 获取伪随机高斯分布值 + * @return + */ + public static double nextGaussian() { return new Random().nextGaussian(); } + /** + * 获取伪随机 float 值 + * @return + */ public static float nextFloat() { return new Random().nextFloat(); } + /** + * 获取伪随机 int 值 + * @return + */ public static int nextInt() { return new Random().nextInt(); } + /** + * 获取伪随机 int 值 - 该值介于 [0,n) 的区间 + * @param n + * @return + */ public static int nextInt(int n) { return new Random().nextInt(n); } + /** + * 获取伪随机 long 值 + * @return + */ public static long nextLong() { return new Random().nextLong(); } @@ -205,9 +285,14 @@ public static int getRandom(int min, int max) { } return min + new Random().nextInt(max - min); } - + + // == + + // 内置洗牌算法 + // java.util.Collections.shuffle(List list); + /** - * 洗牌算法,随机置换指定的数组使用的默认源的随机性 + * 洗牌算法(第一种),随机置换指定的数组使用的默认源的随机性(随机数据源小于三个, 则无效) * @param objArray * @return */ @@ -215,20 +300,20 @@ public static boolean shuffle(Object[] objArray) { if (objArray == null) { return false; } - return shuffle(objArray, getRandom(1,objArray.length)); + return shuffle(objArray, getRandom(1, objArray.length)); } /** - * 洗牌算法,随机置换指定的数组 + * 洗牌算法(第一种),随机置换指定的数组使用的默认源的随机性(随机数据源小于三个, 则无效) * @param objArray - * @param shuffleCount + * @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); + int random = getRandom(0, length - i); Object temp = objArray[length - i]; objArray[length - i] = objArray[random]; objArray[random] = temp; @@ -239,7 +324,7 @@ public static boolean shuffle(Object[] objArray, int shuffleCount) { } /** - * 洗牌算法,随机置换指定数组的使用随机的默认源 + * 洗牌算法(第一种),随机置换指定的数组使用的默认源的随机性(随机数据源小于三个, 则无效) * @param intArray * @return */ @@ -247,13 +332,13 @@ public static int[] shuffle(int[] intArray) { if (intArray == null) { return null; } - return shuffle(intArray, getRandom(1,intArray.length)); + return shuffle(intArray, getRandom(1, intArray.length)); } /** - * 洗牌算法,随机置换指定的数组 + * 洗牌算法(第一种),随机置换指定的数组使用的默认源的随机性(随机数据源小于三个, 则无效) * @param intArray - * @param shuffleCount + * @param shuffleCount 洗牌次数 * @return */ public static int[] shuffle(int[] intArray, int shuffleCount) { @@ -261,7 +346,7 @@ public static int[] shuffle(int[] intArray, int shuffleCount) { 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); + int random = getRandom(0, length - i); out[i - 1] = intArray[random]; int temp = intArray[length - i]; intArray[length - i] = intArray[random]; @@ -271,4 +356,182 @@ public static int[] shuffle(int[] intArray, int shuffleCount) { } return null; } + + // = + + /** + * 洗牌算法(第二种),随机置换指定的数组使用的默认源的随机性 + * @param objArray + * @return + */ + public static boolean shuffle2(Object[] objArray) { + if (objArray == null) { + return false; + } + int len = objArray.length; + if (len > 0) { + for (int i = 0; i < len - 1; i++) { + int idx = (int) (Math.random() * (len - i)); + Object temp = objArray[idx]; + objArray[idx] = objArray[len - i - 1]; + objArray[len - i - 1] = temp; + } + return true; + } + return false; + } + + // == + + /** + * 获取指定范围 Int 值 + * @param origin 开始值 + * @param bound 范围值 + * @return + * @throws IllegalArgumentException + */ + public static int nextIntRange(int origin, int bound) { + 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 + * @throws IllegalArgumentException + */ + public static long nextLongRange(long origin, long bound) { + 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 + * @throws IllegalArgumentException + */ + public static double nextDoubleRange(double origin, double bound) { + 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 + */ + public static int[] ints(int streamSize, int randomNumberOrigin, 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 { + int[] ints = new int[streamSize]; + for (int i = 0; i < streamSize; i++) { + ints[i] = nextIntRange(randomNumberOrigin, randomNumberBound); + } + return ints; +// } + } + + /** + * 获取随机 long 数组 + * @param streamSize 数组长度 + * @param randomNumberOrigin 开始值 + * @param randomNumberBound 结束值(最大值范围) + * @return + */ + public static long[] longs(int streamSize, long randomNumberOrigin, 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 { + long[] longs = new long[streamSize]; + for (int i = 0; i < streamSize; i++) { + longs[i] = nextLongRange(randomNumberOrigin, randomNumberBound); + } + return longs; +// } + } + + /** + * 获取随机 double 数组 + * @param streamSize 数组长度 + * @param randomNumberOrigin 开始值 + * @param randomNumberBound 结束值(最大值范围) + * @return + */ + public static double[] doubles(int streamSize, double randomNumberOrigin, 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 { + double[] doubles = new double[streamSize]; + for (int i = 0; i < streamSize; i++) { + doubles[i] = nextDoubleRange(randomNumberOrigin, randomNumberBound); + } + return doubles; +// } + } } diff --git a/DevLibUtils/src/main/java/dev/utils/common/Reflect2Utils.java b/DevLibUtils/src/main/java/dev/utils/common/Reflect2Utils.java index 4f4553f2e0..d73d77a94b 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/Reflect2Utils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/Reflect2Utils.java @@ -13,7 +13,7 @@ */ public final class Reflect2Utils { - private Reflect2Utils(){ + private Reflect2Utils() { } // 日志TAG @@ -30,7 +30,7 @@ public static Object getProperty(Object owner, String fieldName) { Field field = ownerClass.getField(fieldName); Object property = field.get(owner); return property; - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "getProperty"); } return null; @@ -48,12 +48,21 @@ public static Object getStaticProperty(String className, String fieldName) { Field field = ownerClass.getField(fieldName); Object property = field.get(ownerClass); return property; - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "getStaticProperty"); } return null; } + /** + * 执行某对象方法 + * @param owner 对象 + * @param methodName 方法名 + * @return 方法返回值 + */ + public static Object invokeMethod(Object owner, String methodName) { + return invokeMethod(owner, methodName, new Object[0]); + } /** * 执行某对象方法 @@ -77,6 +86,15 @@ public static Object invokeMethod(Object owner, String methodName, Object[] args return null; } + /** + * 执行某类的静态方法 + * @param className 类名 + * @param methodName 方法名 + * @return 执行方法返回的结果 + */ + public static Object invokeStaticMethod(String className, String methodName) { + return invokeStaticMethod(className, methodName, new Object[0]); + } /** * 执行某类的静态方法 @@ -92,7 +110,10 @@ public static Object invokeStaticMethod(String className, String methodName, Obj for (int i = 0, j = args.length; i < j; i++) { argsClass[i] = args[i].getClass(); } - Method method = ownerClass.getMethod(methodName, argsClass); + // getDeclaredMethod() 获取的是类自身声明的所有方法,包含public、protected和private方法。 + // getMethod() 获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。 + Method method = ownerClass.getDeclaredMethod(methodName, argsClass); + if (!method.isAccessible()) method.setAccessible(true); return method.invoke(null, args); } catch (Exception e) { JCLogUtils.eTag(TAG, e, "invokeStaticMethod"); @@ -146,12 +167,6 @@ 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; - } - // = /** @@ -174,12 +189,12 @@ public static Object getDeclaredField(Object obj, String name) throws Exception * @param fieldName : 父类中的属性名 * @return 父类中的变量对象 */ - public static Object getDeclaredFieldParentObj(Object object, String fieldName){ + public static Object getDeclaredFieldParentObj(Object object, String fieldName) { try { Field field = getDeclaredFieldParent(object, fieldName); field.setAccessible(true); return field.get(object); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "getDeclaredFieldParentObj"); } return null; @@ -191,7 +206,7 @@ public static Object getDeclaredFieldParentObj(Object object, String fieldName){ * @param fieldName : 父类中的属性名 * @return 父类中的变量对象 */ - public static Field getDeclaredFieldParent(Object object, String fieldName){ + public static Field getDeclaredFieldParent(Object object, String fieldName) { Field field = null ; Class clazz = object.getClass() ; for(; clazz != Object.class ; clazz = clazz.getSuperclass()) { @@ -212,18 +227,18 @@ public static Field getDeclaredFieldParent(Object object, String fieldName){ * @param name 方法名 * @param args 方法需要的参数 */ - public static boolean setFieldMethod(Object object, final String name, final Object... 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){ + if (args != null && args.length != 0) { method.invoke(object, args); } else { method.invoke(object); } return true; - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "setFieldMethod"); } return false; diff --git a/DevLibUtils/src/main/java/dev/utils/common/ReflectUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ReflectUtils.java index 1340621631..967c464178 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/ReflectUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/ReflectUtils.java @@ -3,14 +3,17 @@ 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; /** * detail: 反射相关工具类 @@ -254,12 +257,12 @@ private Object unwrap(Object object) { * @param obj * @return */ - public static Object getObject(Field field, Object obj){ + public static Object getObject(Field field, Object obj) { try { field.setAccessible(true); Object out = field.get(obj); return out; - } catch (Exception e){ + } catch (Exception e) { } return null; } @@ -270,7 +273,7 @@ public static Object getObject(Field field, Object obj){ * @param name * @param val */ - public ReflectUtils setEnumVal(Class clas, String name, String val){ + public ReflectUtils setEnumVal(Class clas, String name, String val) { try { return field(name, Enum.valueOf((Class) clas, val)); } catch (Exception e) { @@ -309,13 +312,13 @@ public static Field getDeclaredFieldBase(Object object, String fieldName) { * @param isSuper 是否一直跟到最后, 如果父类还有父类,并且有相同变量名, 则设置isSuper = true,一直会跟到最后的变量 * @return 父类中的属性对象 */ - public static Field getDeclaredFieldBase(Object object, String fieldName, boolean isSuper){ + 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){ + if (!isSuper) { return field; } } catch (Exception e) { @@ -471,6 +474,68 @@ private T accessible(T accessible) { return accessible; } + // ============== + // ==== 代理 ==== + // ============== + + /** + * 根据类, 代理创建并返回对象 + * @param proxyType + * @return 返回代理的对象 + */ + @SuppressWarnings("unchecked") + public

P proxy(final Class

proxyType) { + final boolean isMap = (object instanceof Map); + final InvocationHandler handler = new InvocationHandler() { + @Override + @SuppressWarnings("null") + public Object invoke(Object proxy, Method method, Object[] args) { + String name = method.getName(); + try { + return reflect(object).method(name, args).get(); + } catch (ReflectException e) { + if (isMap) { + Map map = (Map) object; + 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; + } + } + throw e; + } + } + }; + return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), new Class[]{ proxyType }, handler); + } + + /** + * 获取实体类属性名 get/set + * @param string + * @return + */ + private static String property(String string) { + int length = string.length(); + + if (length == 0) { + return ""; + } else if (length == 1) { + return string.toLowerCase(); + } else { + return string.substring(0, 1).toLowerCase() + string.substring(1); + } + } + + // = + + /** + * 获取类型 + * @return + */ public Class type() { return type; } @@ -512,16 +577,29 @@ public T get() { return (T) object; } + /** + * 获取 HashCode + * @return + */ @Override public int hashCode() { return object.hashCode(); } + /** + * 判断反射的两个对象是否一样 + * @param obj + * @return + */ @Override public boolean equals(Object obj) { return obj instanceof ReflectUtils && object.equals(((ReflectUtils) obj).get()); } + /** + * 获取反射获取的对象 toString + * @return + */ @Override public String toString() { return object.toString(); diff --git a/DevLibUtils/src/main/java/dev/utils/common/ScaleUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ScaleUtils.java index 1c81a5781f..0e7f100534 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/ScaleUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/ScaleUtils.java @@ -21,9 +21,9 @@ private ScaleUtils() { * @param cHeight 当前高度 * @return [0] = 宽度, [1] = 高度 */ - public static int[] calcScaleToWidth(int width, int cWidth, int cHeight){ + public static int[] calcScaleToWidth(int width, int cWidth, int cHeight) { try { - if (cWidth == 0){ + if (cWidth == 0) { return new int[] { 0, 0 }; } // 计算比例 @@ -45,9 +45,9 @@ public static int[] calcScaleToWidth(int width, int cWidth, int cHeight){ * @param cHeight 当前高度 * @return [0] = 宽度, [1] = 高度 */ - public static int[] calcScaleToHeight(int height, int cWidth, int cHeight){ + public static int[] calcScaleToHeight(int height, int cWidth, int cHeight) { try { - if (cHeight == 0){ + if (cHeight == 0) { return new int[] { 0, 0 }; } // 计算比例 @@ -70,10 +70,10 @@ public static int[] calcScaleToHeight(int height, int cWidth, int cHeight){ * @param hScale * @return */ - public static int[] calcWidthHeightToScale(int width, int height, float wScale, float hScale){ + public static int[] calcWidthHeightToScale(int width, int height, float wScale, float hScale) { try { // 如果宽度的比例,大于等于高度比例 - if (wScale >= hScale){ // 以宽度为基准 + if (wScale >= hScale) { // 以宽度为基准 // 设置宽度 -> 以宽度为基准 int sWidth = width; // 计算宽度 @@ -101,7 +101,7 @@ public static int[] calcWidthHeightToScale(int width, int height, float wScale, * @param hScale * @return */ - public static int[] calcWidthToScale(int width, float wScale, float hScale){ + public static int[] calcWidthToScale(int width, float wScale, float hScale) { try { // 设置宽度 int sWidth = width; @@ -122,7 +122,7 @@ public static int[] calcWidthToScale(int width, float wScale, float hScale){ * @param hScale * @return */ - public static int[] calcHeightToScale(int height, float wScale, float hScale){ + public static int[] calcHeightToScale(int height, float wScale, float hScale) { try { // 设置高度 int sHeight = height; diff --git a/DevLibUtils/src/main/java/dev/utils/common/SingletonUtils.java b/DevLibUtils/src/main/java/dev/utils/common/SingletonUtils.java index 9f1b25852a..cf7142c318 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/SingletonUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/SingletonUtils.java @@ -8,8 +8,16 @@ public abstract class SingletonUtils { private T instance; + /** + * 实现实例抽象方法 + * @return + */ protected abstract T newInstance(); + /** + * 获取实例方法 + * @return + */ public final T getInstance() { if (instance == null) { synchronized (SingletonUtils.class) { diff --git a/DevLibUtils/src/main/java/dev/utils/common/StreamUtils.java b/DevLibUtils/src/main/java/dev/utils/common/StreamUtils.java index 10c3eb2564..29fb854af5 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/StreamUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/StreamUtils.java @@ -13,7 +13,7 @@ */ public final class StreamUtils { - private StreamUtils(){ + private StreamUtils() { } // 日志TAG @@ -47,7 +47,7 @@ public static ByteArrayOutputStream input2OutputStream(final InputStream is) { * @param out The output stream. * @return input stream */ - public ByteArrayInputStream output2InputStream(final OutputStream out) { + public static ByteArrayInputStream output2InputStream(final OutputStream out) { if (out == null) return null; return new ByteArrayInputStream(((ByteArrayOutputStream) out).toByteArray()); } diff --git a/DevLibUtils/src/main/java/dev/utils/common/StringUtils.java b/DevLibUtils/src/main/java/dev/utils/common/StringUtils.java index f4c8d14112..427883a9bc 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/StringUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/StringUtils.java @@ -87,7 +87,7 @@ public static String toClearSpaceTrim(String str) { * @param str * @return */ - public static String toGBKEncode(String str){ + public static String toGBKEncode(String str) { return toStrEncode(str, "GBK"); } @@ -96,7 +96,7 @@ public static String toGBKEncode(String str){ * @param str * @return */ - public static String toGBK2312Encode(String str){ + public static String toGBK2312Encode(String str) { return toStrEncode(str, "GBK-2312"); } @@ -105,7 +105,7 @@ public static String toGBK2312Encode(String str){ * @param str * @return */ - public static String toUTF8Encode(String str){ + public static String toUTF8Encode(String str) { return toStrEncode(str, "UTF-8"); } @@ -115,7 +115,7 @@ public static String toUTF8Encode(String str){ * @param enc * @return */ - public static String toStrEncode(String str, String enc){ + public static String toStrEncode(String str, String enc) { try { return new String(str.getBytes(), enc); } catch (Exception e) { @@ -131,7 +131,7 @@ public static String toStrEncode(String str, String enc){ * @param str * @return */ - public static String toUrlEncode(String str){ + public static String toUrlEncode(String str) { return toUrlEncode(str, "UTF-8"); } @@ -141,7 +141,7 @@ public static String toUrlEncode(String str){ * @param enc * @return */ - public static String toUrlEncode(String str, String enc){ + public static String toUrlEncode(String str, String enc) { try { return URLEncoder.encode(str, enc); } catch (Exception e) { @@ -336,7 +336,7 @@ 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){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "upperFirstLetter"); return str; } @@ -351,7 +351,7 @@ 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){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "lowerFirstLetter"); return str; } @@ -454,7 +454,7 @@ public static final String camelCase2UnderScoreCase(String str) { * @param keyWord * @return */ - public static String sqliteEscape(String keyWord){ + public static String sqliteEscape(String keyWord) { if (isEmpty(keyWord)) return keyWord; // 替换关键字 keyWord = keyWord.replace("/", "//"); diff --git a/DevLibUtils/src/main/java/dev/utils/common/ZipUtils.java b/DevLibUtils/src/main/java/dev/utils/common/ZipUtils.java index d8e8065f6b..adafa78aec 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/ZipUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/ZipUtils.java @@ -16,6 +16,8 @@ import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; +import dev.utils.JCLogUtils; + /** * detail: 压缩相关工具类 * Created by Ttt @@ -25,6 +27,8 @@ public final class ZipUtils { private ZipUtils() { } + // 日志 TAG + private static final String TAG = ZipUtils.class.getSimpleName(); // 缓存大小 private static final int BUFFER_LEN = 8192; @@ -245,20 +249,28 @@ public static List unzipFileByKeyword(final String zipFilePath, final Stri public static List unzipFileByKeyword(final File zipFile, final File destDir, final String keyword) throws IOException { 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: " + entryName + " is dangerous!")); + 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: " + entryName + " is dangerous!")); + continue; + } if (entryName.contains(keyword)) { - if (!unzipChildFile(destDir, files, zf, entry, entryName)) return files; + if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files; } } } @@ -276,8 +288,7 @@ public static List unzipFileByKeyword(final File zipFile, final File destD * @throws IOException */ 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); + File file = new File(destDir, entryName); files.add(file); if (entry.isDirectory()) { if (!createOrExistsDir(file)) return false; @@ -321,7 +332,14 @@ public static List getFilesPath(final File zipFile) throws IOException { 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: " + entryName + " is dangerous!")); + // = + paths.add(entryName); + } else { + paths.add(entryName); + } } return paths; } @@ -380,7 +398,6 @@ private static boolean createOrExistsFile(final File file) { // 存在, 则返回新的路径 return file.createNewFile(); } catch (IOException e) { - e.printStackTrace(); return false; } } @@ -390,7 +407,7 @@ private static boolean createOrExistsFile(final File file) { * @param filePath The path of file. * @return */ - private static File getFileByPath(final String filePath){ + private static File getFileByPath(final String filePath) { return filePath != null ? new File(filePath) : null; } diff --git a/DevLibUtils/src/main/java/dev/utils/common/assist/Averager.java b/DevLibUtils/src/main/java/dev/utils/common/assist/Averager.java index 1c8d3cfa62..bf4b5544d8 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/assist/Averager.java +++ b/DevLibUtils/src/main/java/dev/utils/common/assist/Averager.java @@ -1,28 +1,29 @@ package dev.utils.common.assist; import java.util.ArrayList; +import java.util.List; /** * detail: 用以统计平均数 - * @author MaTianyu + * Created by Ttt */ public class Averager { - private ArrayList numList = new ArrayList(); + private List mNumLists = new ArrayList<>(); /** * 添加一个数字 * @param num */ public synchronized void add(Number num) { - numList.add(num); + mNumLists.add(num); } /** * 清除全部 */ public void clear() { - numList.clear(); + mNumLists.clear(); } /** @@ -30,7 +31,7 @@ public void clear() { * @return */ public Number size() { - return numList.size(); + return mNumLists.size(); } /** @@ -38,14 +39,14 @@ public Number size() { * @return */ public Number getAverage() { - if (numList.size() == 0) { + if (mNumLists.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(); + for (int i = 0, size = mNumLists.size(); i < size; i++) { + sum = sum.floatValue() + mNumLists.get(i).floatValue(); } - return sum / numList.size(); + return sum / mNumLists.size(); } } @@ -54,7 +55,7 @@ public Number getAverage() { * @return */ public String print() { - return "PrintList(" + size() + "): " + numList; + return "printList(" + size() + "): " + mNumLists; } } diff --git a/DevLibUtils/src/main/java/dev/utils/common/assist/Base64.java b/DevLibUtils/src/main/java/dev/utils/common/assist/Base64.java index 2761cd6002..f089d2572e 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/assist/Base64.java +++ b/DevLibUtils/src/main/java/dev/utils/common/assist/Base64.java @@ -19,12 +19,13 @@ /** * detail: Base64 工具类 + * Created by 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. */ diff --git a/DevLibUtils/src/main/java/dev/utils/common/assist/TimeAverager.java b/DevLibUtils/src/main/java/dev/utils/common/assist/TimeAverager.java index 54d55ee6c5..014ba1878d 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/assist/TimeAverager.java +++ b/DevLibUtils/src/main/java/dev/utils/common/assist/TimeAverager.java @@ -2,29 +2,28 @@ /** * detail: 时间均值计算器, 只能用于单线程计时。 - * @author MaTianyu + * Created by Ttt */ public class TimeAverager { /** 计时器 */ - private TimeCounter tc = new TimeCounter(); - + private TimeCounter timeCounter = new TimeCounter(); /** 均值器 */ - private Averager av = new Averager(); + private Averager averager = new Averager(); /** * 一个计时开始 */ public long start() { - return tc.start(); + return timeCounter.start(); } /** * 一个计时结束 */ public long end() { - long time = tc.duration(); - av.add(time); + long time = timeCounter.duration(); + averager.add(time); return time; } @@ -32,8 +31,8 @@ public long end() { * 一个计时结束,并且启动下次计时。 */ public long endAndRestart() { - long time = tc.durationRestart(); - av.add(time); + long time = timeCounter.durationRestart(); + averager.add(time); return time; } @@ -41,20 +40,20 @@ public long endAndRestart() { * 求全部计时均值 */ public Number average() { - return av.getAverage(); + return averager.getAverage(); } /** * 打印全部时间值 */ public void print() { - av.print(); + averager.print(); } /** * 清除数据 */ public void clear() { - av.clear(); + averager.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 index 0bbf7fd11b..2e1fc70e5e 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/assist/TimeCounter.java +++ b/DevLibUtils/src/main/java/dev/utils/common/assist/TimeCounter.java @@ -2,38 +2,48 @@ /** * detail: 时间计时器 - * @author MaTianyu + * Created by Ttt */ public class TimeCounter { - private long t; + private long start; public TimeCounter() { start(); } /** - * Count start. + * 开始计时 */ public long start() { - t = System.currentTimeMillis(); - return t; + start = System.currentTimeMillis(); + return start; } /** - * Get duration and restart. + * 获取持续的时间并重新启动。 + * @return */ public long durationRestart() { long now = System.currentTimeMillis(); - long d = now - t; - t = now; + long d = now - start; + start = now; return d; } /** - * Get duration. + * 获取持续的时间 + * @return */ public long duration() { - return System.currentTimeMillis() - t; + return System.currentTimeMillis() - start; + } + + /** + * 获取开始时间 + * @return + */ + public long getStartTime() { + return start; } } \ 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 index 7549c4e62d..a977895ced 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/assist/TimeKeeper.java +++ b/DevLibUtils/src/main/java/dev/utils/common/assist/TimeKeeper.java @@ -4,7 +4,7 @@ /** * detail: 时间堵塞保留 - * @author 氢一 + * Created by Ttt */ public class TimeKeeper { @@ -48,6 +48,11 @@ public TimeKeeper startNow() { return this; } + /** + * 设置等待一段时间后, 通知方法 + * @param endCallback + * @return + */ public TimeKeeper waitForEnd(OnEndCallback endCallback) { long costMillis = SystemClock.elapsedRealtime() - startMillis; long leftMillis = keepTimeMillis - costMillis; diff --git a/DevLibUtils/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java b/DevLibUtils/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java new file mode 100644 index 0000000000..3d1b16a014 --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/common/assist/search/FileBreadthFirstSearchUtils.java @@ -0,0 +1,395 @@ +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: 文件广度优先搜索算法 (多线程 + 队列搜索) + * Created by Ttt + */ +public final class FileBreadthFirstSearchUtils { + + // = 构造函数 = + + public FileBreadthFirstSearchUtils() { + } + + public FileBreadthFirstSearchUtils(ISearchHandler iSearchHandler) { + this.iSearchHandler = iSearchHandler; + } + + /** + * 文件信息 Item + */ + public final class FileItem { + + public FileItem(File file) { + this.file = file; + } + + // 文件 + public File file = null; + + // HashMap 保存目录信息 + public Map mapChilds = null; + + /** + * 保存子文件信息 + * @param file + */ + private synchronized FileItem put(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; + } + } + + /** + * 文件队列 + */ + private class FileQueue { + + public FileQueue(File file, FileItem fileItem) { + this.file = file; + this.fileItem = fileItem; + } + + // 当前准备处理文件夹 + private File file; + + // 上一级目录对象 + private FileItem fileItem; + } + + /** + * 搜索处理接口 + */ + public interface ISearchHandler { + + /** + * 判断是否处理该文件 + * @param file + * @return + */ + boolean isHandlerFile(File file); + + /** + * 是否添加到集合 + * @param file + * @return + */ + boolean isAddToList(File file); + + /** + * 搜索结束监听 + * @param rootFileItem + * @param startTime + * @param endTime + */ + void OnEndListener(FileItem rootFileItem, long startTime, long endTime); + } + + // 搜索处理接口 + private ISearchHandler iSearchHandler; + + // 内部实现接口 + private ISearchHandler inside = new ISearchHandler() { + @Override + public boolean isHandlerFile(File file) { + if (iSearchHandler != null) { + return iSearchHandler.isHandlerFile(file); + } + return true; + } + + @Override + public boolean isAddToList(File file) { + if (iSearchHandler != null) { + return iSearchHandler.isAddToList(file); + } + return true; + } + + @Override + public void OnEndListener(FileItem rootFileItem, long startTime, long endTime) { + // 表示非搜索中 + running = false; + // 触发回调 + if (iSearchHandler != null) { + iSearchHandler.OnEndListener(rootFileItem, startTime, endTime); + } + } + }; + + /** + * 设置搜索处理接口 + * @param iSearchHandler + * @return + */ + public FileBreadthFirstSearchUtils setSearchHandler(ISearchHandler iSearchHandler) { + this.iSearchHandler = iSearchHandler; + return this; + } + + /** + * 获取任务队列同时进行数量 + * @return + */ + public int getQueueSameTimeNumber() { + return queueSameTimeNumber; + } + + /** + * 任务队列同时进行数量 + * @param queueSameTimeNumber + * @return + */ + public synchronized FileBreadthFirstSearchUtils setQueueSameTimeNumber(int queueSameTimeNumber) { + if (running) { + return this; + } + this.queueSameTimeNumber = queueSameTimeNumber; + return this; + } + + /** + * 是否搜索中 + * @return + */ + public boolean isRunning() { + return running; + } + + /** + * 停止搜索 + */ + public void stop() { + stop = true; + } + + /** + * 是否停止搜索 + * @return + */ + public boolean isStop() { + return stop; + } + + /** + * 获取开始搜索时间 + * @return + */ + public long getStartTime() { + return startTime; + } + + /** + * 获取结束搜索时间 + * @return + */ + public long getEndTime() { + return endTime; + } + + /** + * 获取延迟校验时间 + * @return + */ + public long getDelayTime() { + return delayTime; + } + + /** + * 设置延迟校验时间 + * @param delayTime + */ + public void setDelayTime(long delayTime) { + this.delayTime = delayTime; + } + + // = + + // 根目录对象 + private FileItem rootFileItem; + // 判断是否运行中 + private boolean running = false; + // 是否停止搜索 + private boolean stop = false; + // 开始搜索时间 + private long startTime = 0l; + // 结束搜索时间 + private long endTime = 0l; + // 延迟时间 + private long delayTime = 50l; + // 任务队列同时进行数量 + private int queueSameTimeNumber = 5; + // 线程池 + private ExecutorService executor = Executors.newCachedThreadPool(); + // 任务队列 + private LinkedBlockingQueue taskQueue = new LinkedBlockingQueue<>(); + + /** + * 查询 + * @param path 根目录地址 + */ + public synchronized void query(String path) { + if (running) { + return; + } + // 表示运行中 + running = true; + stop = false; + // 设置开始搜索时间 + startTime = System.currentTimeMillis(); + try { + // 获取根目录 File + File file = new File(path); + if (file != null) { + // 初始化根目录 + rootFileItem = new FileItem(file); + // 判断是否文件 + if (file.isFile()) { + // 触发结束回调 + endTime = System.currentTimeMillis(); + inside.OnEndListener(rootFileItem, startTime, endTime); + return; + } + // 获取文件夹全部子文件 + String[] fileArys = file.list(); + // 获取文件总数 + if (fileArys != null && fileArys.length != 0) { + new Thread(new Runnable() { + @Override + public void run() { + // 查询文件 + queryFile(rootFileItem.file, rootFileItem); + // 循环队列 + whileQueue(); + } + }).start(); + } else { + // 触发结束回调 + endTime = System.currentTimeMillis(); + inside.OnEndListener(rootFileItem, startTime, endTime); + } + } + } catch (Exception e) { + // 触发结束回调 + endTime = System.currentTimeMillis(); + inside.OnEndListener(rootFileItem, startTime, endTime); + } + } + + /** + * 查询文件 + * @param file + * @param fileItem 所在文件夹对象(上一级目录) + */ + private void queryFile(File file, FileItem fileItem) { + try { + if (stop) { + return; + } + if (file != null && file.exists()) { + // 判断是否处理 + if (inside.isHandlerFile(file)) { + // 如果属于文件夹 + if (file.isDirectory()) { + // 获取文件夹全部子文件 + File[] files = file.listFiles(); + if (files == null) { + return; + } + // 循环处理 + for (File f : files) { + // 属于文件夹 + if (f.isDirectory()) { + if (stop) { + return; + } + FileItem subFileItem = fileItem.put(f); + // 添加任务 + taskQueue.offer(new FileQueue(f, subFileItem)); + } else { // 属于文件 + if (!stop && inside.isAddToList(f)) { + // 属于文件则直接保存 + fileItem.put(f); + } + } + } + } else { // 属于文件 + if (!stop && inside.isAddToList(file)) { + // 属于文件则直接保存 + fileItem.put(file); + } + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(FileBreadthFirstSearchUtils.class.getSimpleName(), e, "queryFile"); + } + } + + // == 线程池处理 == + + /** + * 循环队列 + */ + private void whileQueue() { + // 判断是否为null + boolean isEmpty = taskQueue.isEmpty(); + // 循环则不处理 + while (!isEmpty) { + if (stop) { + break; + } + // 获取线程活动数量 + int threadCount = ((ThreadPoolExecutor) executor).getActiveCount(); + // 判断是否超过 + if (threadCount > queueSameTimeNumber) { + continue; + } + // 获取文件对象 + final FileQueue fileQueue = taskQueue.poll(); + // 判断是否为null + if (fileQueue != null) { + // 后台运行 + executor.execute(new Runnable() { + @Override + public void run() { + queryFile(fileQueue.file, fileQueue.fileItem); + } + }); + } + + // 判断是否存在队列数据 + isEmpty = (taskQueue.isEmpty() && threadCount == 0); + if (isEmpty) { // 如果不存在, 防止搜索过快, 延迟再次判断 + if (stop) { + break; + } + try { + Thread.sleep(delayTime); + } catch (Exception e) { + } + isEmpty = (taskQueue.isEmpty() && threadCount == 0); + } + } + // 进行通知 + endTime = System.currentTimeMillis(); + inside.OnEndListener(rootFileItem, startTime, endTime); + } +} diff --git a/DevLibUtils/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java b/DevLibUtils/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java new file mode 100644 index 0000000000..8f2e0bed89 --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/common/assist/search/FileDepthFirstSearchUtils.java @@ -0,0 +1,272 @@ +package dev.utils.common.assist.search; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import dev.utils.JCLogUtils; + +/** + * detail: 文件深度优先搜索算法 (搜索某个目录下的全部文件) + * Created by Ttt + */ +public final class FileDepthFirstSearchUtils { + + // = 构造函数 = + + public FileDepthFirstSearchUtils() { + } + + public FileDepthFirstSearchUtils(ISearchHandler iSearchHandler) { + this.iSearchHandler = iSearchHandler; + } + + /** + * 文件信息 Item + */ + public final class FileItem { + + public FileItem(File file) { + this.file = file; + } + + // 文件 + public File file; + + // 子文件夹、文件对象 + public List listChilds = null; + } + + /** + * 搜索处理接口 + */ + public interface ISearchHandler { + + /** + * 判断是否处理该文件 + * @param file + * @return + */ + boolean isHandlerFile(File file); + + /** + * 是否添加到集合 + * @param file + * @return + */ + boolean isAddToList(File file); + + /** + * 搜索结束监听 + * @param lists + * @param startTime + * @param endTime + */ + void OnEndListener(List lists, long startTime, long endTime); + + } + + // 搜索处理接口 + private ISearchHandler iSearchHandler; + + // 内部实现接口 + private ISearchHandler inside = new ISearchHandler() { + @Override + public boolean isHandlerFile(File file) { + if (iSearchHandler != null) { + return iSearchHandler.isHandlerFile(file); + } + return true; + } + + @Override + public boolean isAddToList(File file) { + if (iSearchHandler != null) { + return iSearchHandler.isAddToList(file); + } + return true; + } + + @Override + public void OnEndListener(List lists, long startTime, long endTime) { + // 表示非搜索中 + running = false; + // 触发回调 + if (iSearchHandler != null) { + iSearchHandler.OnEndListener(lists, startTime, endTime); + } + } + }; + + /** + * 设置搜索处理接口 + * @param iSearchHandler + * @return + */ + public FileDepthFirstSearchUtils setSearchHandler(ISearchHandler iSearchHandler) { + this.iSearchHandler = iSearchHandler; + return this; + } + + /** + * 是否搜索中 + * @return + */ + public boolean isRunning() { + return running; + } + + /** + * 停止搜索 + */ + public void stop() { + stop = true; + } + + /** + * 是否停止搜索 + * @return + */ + public boolean isStop() { + return stop; + } + + /** + * 获取开始搜索时间 + * @return + */ + public long getStartTime() { + return startTime; + } + + /** + * 获取结束搜索时间 + * @return + */ + public long getEndTime() { + return endTime; + } + + // == + + // 判断是否运行中 + private boolean running = false; + // 是否停止搜索 + private boolean stop = false; + // 开始搜索时间 + private long startTime = 0l; + // 结束搜索时间 + private long endTime = 0l; + + /** + * 查询 + * @param path 根目录地址 + * @param isRelation 是否关联到 Child List + */ + public synchronized void query(String path, final boolean isRelation) { + if (running) { + return; + } + // 表示运行中 + running = true; + stop = false; + // 设置开始搜索时间 + startTime = System.currentTimeMillis(); + try { + // 获取根目录 File + final File file = new File(path); + if (file != null) { + // 判断是否文件 + if (file.isFile()) { + List lists = new ArrayList<>(); + lists.add(new FileItem(file)); + // 触发结束回调 + endTime = System.currentTimeMillis(); + inside.OnEndListener(lists, startTime, endTime); + 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); + // 触发结束回调 + endTime = System.currentTimeMillis(); + inside.OnEndListener(lists, startTime, endTime); + } + }).start(); + } else { + // 触发结束回调 + endTime = System.currentTimeMillis(); + inside.OnEndListener(null, startTime, endTime); + } + } + } catch (Exception e) { + // 触发结束回调 + endTime = System.currentTimeMillis(); + inside.OnEndListener(null, startTime, endTime); + } + } + + /** + * 查询文件 + * @param file + * @param lists 保存数据源 + * @param isRelation 是否关联到 Child List + */ + private void queryFile(File file, List lists, boolean isRelation) { + try { + if (stop) { + return; + } + if (file != null && file.exists()) { + // 判断是否处理 + if (inside.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 (inside.isAddToList(f)) { + // 属于文件则直接保存 + lists.add(new FileItem(f)); + } + } + } else { + // 查找文件 + queryFile(f, lists, isRelation); + } + } + } else { // 属于文件 + if (inside.isAddToList(file)) { + // 属于文件则直接保存 + lists.add(new FileItem(file)); + } + } + } + } + } catch (Exception e) { + JCLogUtils.eTag(FileDepthFirstSearchUtils.class.getSimpleName(), e, "queryFile"); + } + } +} diff --git a/DevLibUtils/src/main/java/dev/utils/common/cipher/Base64Cipher.java b/DevLibUtils/src/main/java/dev/utils/common/cipher/Base64Cipher.java index dd454cb488..c80c894abf 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/cipher/Base64Cipher.java +++ b/DevLibUtils/src/main/java/dev/utils/common/cipher/Base64Cipher.java @@ -4,7 +4,7 @@ /** * detail: Baes64 编解码, 进行加密 - * @author MaTianyu + * Created by Ttt */ public class Base64Cipher implements Cipher { diff --git a/DevLibUtils/src/main/java/dev/utils/common/cipher/Cipher.java b/DevLibUtils/src/main/java/dev/utils/common/cipher/Cipher.java index 2039149ed8..519e0bc3f4 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/cipher/Cipher.java +++ b/DevLibUtils/src/main/java/dev/utils/common/cipher/Cipher.java @@ -2,7 +2,7 @@ /** * detail: 通用加密/解密抽象实现类 - * @author MaTianyu + * Created by Ttt */ public interface Cipher extends Decrypt, Encrypt { diff --git a/DevLibUtils/src/main/java/dev/utils/common/cipher/CipherUtils.java b/DevLibUtils/src/main/java/dev/utils/common/cipher/CipherUtils.java index 6ebe62a684..bf0c96a2ca 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/cipher/CipherUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/cipher/CipherUtils.java @@ -7,9 +7,9 @@ * detail: 加密工具类 * Created by Ttt */ -public class CipherUtils { +public final class CipherUtils { - private CipherUtils(){ + private CipherUtils() { } // 保存加密: 如SP @@ -17,21 +17,21 @@ private CipherUtils(){ // 获取 Object obj = CipherUtils.decrypt(SP.get(key), cipher); /** - * 加密工具类 + * 加密工具类 * @param obj * @return */ - public static String encrypt(Object obj){ + public static String encrypt(Object obj) { return encrypt(obj, null); } /** - * 加密工具类 + * 加密工具类 * @param obj * @param cipher * @return */ - public static String encrypt(Object obj, Cipher cipher){ + 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); @@ -45,7 +45,7 @@ public static String encrypt(Object obj, Cipher cipher){ * @param hex * @return */ - public static Object decrypt (String hex){ + public static Object decrypt (String hex) { return decrypt(hex, null); } @@ -55,7 +55,7 @@ public static Object decrypt (String hex){ * @param cipher * @return */ - public static Object decrypt (String hex, Cipher cipher){ + 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); diff --git a/DevLibUtils/src/main/java/dev/utils/common/cipher/Decrypt.java b/DevLibUtils/src/main/java/dev/utils/common/cipher/Decrypt.java index e56917739e..1138797a65 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/cipher/Decrypt.java +++ b/DevLibUtils/src/main/java/dev/utils/common/cipher/Decrypt.java @@ -2,10 +2,15 @@ /** * detail: 解密/解码接口 - * @author MaTianyu + * Created by Ttt */ public interface Decrypt { + /** + * 解密/解码方法 + * @param res + * @return + */ 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 index c95b3f7a62..78e33a56be 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/cipher/Encrypt.java +++ b/DevLibUtils/src/main/java/dev/utils/common/cipher/Encrypt.java @@ -2,10 +2,15 @@ /** * detail: 加密/编码接口 - * @author MaTianyu + * Created by Ttt */ public interface Encrypt { + /** + * 加密/编码方法 + * @param res + * @return + */ byte[] encrypt(byte[] res); } diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/AESUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/AESUtils.java index da5c1a1a6a..6631ffb448 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/AESUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/encrypt/AESUtils.java @@ -8,7 +8,7 @@ import dev.utils.JCLogUtils; /** - * detail: AES对称加密(Advanced Encryption Standard,高级数据加密标准,AES算法可以有效抵制针对DES的攻击算法,对称加密算法) + * detail: AES对称加密(Advanced Encryption Standard,高级数据加密标准,AES算法可以有效抵制针对DES的攻击算法,对称加密算法) * Created by Ttt */ public final class AESUtils { @@ -23,13 +23,13 @@ private AESUtils() { * 生成密钥 * @return */ - public static byte[] initKey(){ + 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){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "initKey"); } return null; @@ -41,14 +41,14 @@ public static byte[] initKey(){ * @param key * @return */ - public static byte[] encrypt(byte[] data, byte[] key){ + public static byte[] encrypt(byte[] data, byte[] key) { 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){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "encrypt"); } return null; @@ -60,14 +60,14 @@ public static byte[] encrypt(byte[] data, byte[] key){ * @param key * @return */ - public static byte[] decrypt(byte[] data, byte[] key){ + public static byte[] decrypt(byte[] data, byte[] key) { 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){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "decrypt"); } return null; diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/CRCUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/CRCUtils.java index ecc931776a..25ab681b2c 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/CRCUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/encrypt/CRCUtils.java @@ -23,7 +23,7 @@ private CRCUtils() { * @param str * @return */ - public static long getCRC32(String str){ + public static long getCRC32(String str) { try { CRC32 crc32 = new CRC32(); crc32.update(str.getBytes()); @@ -39,7 +39,7 @@ public static long getCRC32(String str){ * @param str * @return */ - public static String getCRC32Str(String str){ + public static String getCRC32Str(String str) { try { CRC32 crc32 = new CRC32(); crc32.update(str.getBytes()); @@ -54,7 +54,7 @@ public static String getCRC32Str(String str){ * 获取文件CRC32 值 * @return */ - public static String getFileCrc32(String fPath){ + public static String getFileCrc32(String fPath) { try { InputStream fis = new FileInputStream(fPath); byte[] buffer = new byte[1024]; diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/DESUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/DESUtils.java index e13c20f094..88acc46bfd 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/DESUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/encrypt/DESUtils.java @@ -11,7 +11,7 @@ import dev.utils.JCLogUtils; /** - * detail: DES对称加密(Data Encryption Standard,数据加密标准,对称加密算法) + * detail: DES对称加密(Data Encryption Standard,数据加密标准,对称加密算法) * Created by Ttt */ public final class DESUtils { @@ -20,7 +20,7 @@ private DESUtils() { } // 日志TAG - private static final String TAG = CRCUtils.class.getSimpleName(); + private static final String TAG = DESUtils.class.getSimpleName(); /** * 返回可逆算法DES的密钥 @@ -28,12 +28,12 @@ private DESUtils() { * @return 生成的密钥 * @throws Exception */ - public static Key getDESKey(byte[] key){ + public static Key getDESKey(byte[] key) { try { DESKeySpec des = new DESKeySpec(key); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); return keyFactory.generateSecret(des); - } catch (Exception e){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "getDESKey"); } return null; @@ -47,14 +47,14 @@ public static Key getDESKey(byte[] key){ * @return * @throws Exception */ - public static byte[] encrypt(byte[] data, byte[] key){ + public static byte[] encrypt(byte[] data, byte[] key) { 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){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "encrypt"); } return null; @@ -67,14 +67,14 @@ public static byte[] encrypt(byte[] data, byte[] key){ * @return * @throws Exception */ - public static byte[] decrypt(byte[] data, byte[] key){ + public static byte[] decrypt(byte[] data, byte[] key) { 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){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "decrypt"); } 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 index b47a4bba4a..9e2942e74a 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/EncryptUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/encrypt/EncryptUtils.java @@ -7,21 +7,28 @@ import java.io.IOException; import java.security.DigestInputStream; import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; 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.CloseUtils; /** - * detail: 加密工具类 - * Created by Ttt + * detail: 加/解密工具类 + * Created by Blankj + * Update to Ttt */ public final class EncryptUtils { @@ -31,79 +38,76 @@ 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 + * MD2 加密 + * @param data + * @return */ public static String encryptMD2ToString(final String data) { + if (data == null || data.length() == 0) return ""; return encryptMD2ToString(data.getBytes()); } /** - * Return the hex string of MD2 encryption. - * - * @param data The data. - * @return the hex string of MD2 encryption + * MD2 加密 + * @param data + * @return */ 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 + * MD2 加密 + * @param data + * @return */ 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 + * MD5 加密 + * @param data + * @return */ public static String encryptMD5ToString(final String data) { + if (data == null || data.length() == 0) return ""; 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 + * MD5 加密 + * @param data + * @param salt + * @return */ public static String encryptMD5ToString(final String data, final String salt) { + if (data == null && salt == null) return ""; + if (salt == null) return bytes2HexString(encryptMD5(data.getBytes())); + if (data == null) return bytes2HexString(encryptMD5(salt.getBytes())); return bytes2HexString(encryptMD5((data + salt).getBytes())); } /** - * Return the hex string of MD5 encryption. - * - * @param data The data. - * @return the hex string of MD5 encryption + * MD5 加密 + * @param data + * @return */ 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 + * MD5 加密 + * @param data + * @param salt + * @return */ public static String encryptMD5ToString(final byte[] data, final byte[] salt) { - if (data == null || salt == null) return null; + if (data == null && salt == null) return ""; + if (salt == null) return bytes2HexString(encryptMD5(data)); + if (data == null) return bytes2HexString(encryptMD5(salt)); 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); @@ -111,20 +115,18 @@ public static String encryptMD5ToString(final byte[] data, final byte[] salt) { } /** - * Return the bytes of MD5 encryption. - * - * @param data The data. - * @return the bytes of MD5 encryption + * MD5 加密 + * @param data + * @return */ 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 + * MD5 加密文件 + * @param filePath + * @return */ public static String encryptMD5File2String(final String filePath) { File file = isSpace(filePath) ? null : new File(filePath); @@ -132,10 +134,9 @@ public static String encryptMD5File2String(final String filePath) { } /** - * Return the bytes of file's MD5 encryption. - * - * @param filePath The path of file. - * @return the bytes of file's MD5 encryption + * MD5 加密文件 + * @param filePath + * @return */ public static byte[] encryptMD5File(final String filePath) { File file = isSpace(filePath) ? null : new File(filePath); @@ -143,20 +144,18 @@ public static byte[] encryptMD5File(final String filePath) { } /** - * Return the hex string of file's MD5 encryption. - * - * @param file The file. - * @return the hex string of file's MD5 encryption + * MD5 加密文件 + * @param file + * @return */ 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 + * MD5 加密文件 + * @param file + * @return */ public static byte[] encryptMD5File(final File file) { if (file == null) return null; @@ -176,166 +175,160 @@ public static byte[] encryptMD5File(final File file) { JCLogUtils.eTag(TAG, e, "encryptMD5File"); return null; } finally { - CloseUtils.closeIO(fis); + try { + if (fis != null) { + fis.close(); + } + } catch (IOException e) { + } } } /** - * Return the hex string of SHA1 encryption. - * - * @param data The data. - * @return the hex string of SHA1 encryption + * SHA1 加密 + * @param data + * @return */ public static String encryptSHA1ToString(final String data) { + if (data == null || data.length() == 0) return ""; return encryptSHA1ToString(data.getBytes()); } /** - * Return the hex string of SHA1 encryption. - * - * @param data The data. - * @return the hex string of SHA1 encryption + * SHA1 加密 + * @param data + * @return */ 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 + * SHA1 加密 + * @param data + * @return */ public static byte[] encryptSHA1(final byte[] data) { - return hashTemplate(data, "SHA1"); + return hashTemplate(data, "SHA-1"); } /** - * Return the hex string of SHA224 encryption. - * - * @param data The data. - * @return the hex string of SHA224 encryption + * SHA224 加密 + * @param data + * @return */ public static String encryptSHA224ToString(final String data) { + if (data == null || data.length() == 0) return ""; return encryptSHA224ToString(data.getBytes()); } /** - * Return the hex string of SHA224 encryption. - * - * @param data The data. - * @return the hex string of SHA224 encryption + * SHA224 加密 + * @param data + * @return */ 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 + * SHA224 加密 + * @param data + * @return */ 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 + * SHA256 加密 + * @param data + * @return */ public static String encryptSHA256ToString(final String data) { + if (data == null || data.length() == 0) return ""; return encryptSHA256ToString(data.getBytes()); } /** - * Return the hex string of SHA256 encryption. - * - * @param data The data. - * @return the hex string of SHA256 encryption + * SHA256 加密 + * @param data + * @return */ 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 + * SHA256 加密 + * @param data + * @return */ public static byte[] encryptSHA256(final byte[] data) { - return hashTemplate(data, "SHA256"); + return hashTemplate(data, "SHA-256"); } /** - * Return the hex string of SHA384 encryption. - * - * @param data The data. - * @return the hex string of SHA384 encryption + * SHA384 加密 + * @param data + * @return */ public static String encryptSHA384ToString(final String data) { + if (data == null || data.length() == 0) return ""; return encryptSHA384ToString(data.getBytes()); } /** - * Return the hex string of SHA384 encryption. - * - * @param data The data. - * @return the hex string of SHA384 encryption + * SHA384 加密 + * @param data + * @return */ 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 + * SHA384 加密 + * @param data + * @return */ public static byte[] encryptSHA384(final byte[] data) { - return hashTemplate(data, "SHA384"); + return hashTemplate(data, "SHA-384"); } /** - * Return the hex string of SHA512 encryption. - * - * @param data The data. - * @return the hex string of SHA512 encryption + * SHA512 加密 + * @param data + * @return */ public static String encryptSHA512ToString(final String data) { + if (data == null || data.length() == 0) return ""; return encryptSHA512ToString(data.getBytes()); } /** - * Return the hex string of SHA512 encryption. - * - * @param data The data. - * @return the hex string of SHA512 encryption + * SHA512 加密 + * @param data + * @return */ 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 + * SHA512 加密 + * @param data + * @return */ public static byte[] encryptSHA512(final byte[] data) { - return hashTemplate(data, "SHA512"); + return hashTemplate(data, "SHA-512"); } /** - * Return the bytes of hash encryption. - * - * @param data The data. - * @param algorithm The name of hash encryption. - * @return the bytes of hash encryption + * hash 加密模版方法 + * @param data 数据 + * @param algorithm 算法 + * @return */ private static byte[] hashTemplate(final byte[] data, final String algorithm) { if (data == null || data.length <= 0) return null; @@ -343,225 +336,206 @@ private static byte[] hashTemplate(final byte[] data, final String algorithm) { MessageDigest md = MessageDigest.getInstance(algorithm); md.update(data); return md.digest(); - } catch (NoSuchAlgorithmException e) { + } catch (Exception 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 + * HmacMD5 加密 + * @param data + * @param key + * @return */ public static String encryptHmacMD5ToString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return ""; 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 + * HmacMD5 加密 + * @param data + * @param key + * @return */ 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 + * HmacMD5 加密 + * @param data + * @param key + * @return */ 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 + * HmacSHA1 加密 + * @param data + * @param key + * @return */ public static String encryptHmacSHA1ToString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return ""; 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 + * HmacSHA1 加密 + * @param data + * @param key + * @return */ 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 + * HmacSHA1 加密 + * @param data + * @param key + * @return */ 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 + * HmacSHA224 加密 + * @param data + * @param key + * @return */ public static String encryptHmacSHA224ToString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return ""; 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 + * HmacSHA224 加密 + * @param data + * @param key + * @return */ 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 + * HmacSHA224 加密 + * @param data + * @param key + * @return */ 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 + * HmacSHA256 加密 + * @param data + * @param key + * @return */ public static String encryptHmacSHA256ToString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return ""; 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 + * HmacSHA256 加密 + * @param data + * @param key + * @return */ 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 + * HmacSHA256 加密 + * @param data + * @param key + * @return */ 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 + * HmacSHA384 加密 + * @param data + * @param key + * @return */ public static String encryptHmacSHA384ToString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return ""; 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 + * HmacSHA384 加密 + * @param data + * @param key + * @return */ 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 + * HmacSHA384 加密 + * @param data + * @param key + * @return */ 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 + * HmacSHA512 加密 + * @param data + * @param key + * @return */ public static String encryptHmacSHA512ToString(final String data, final String key) { + if (data == null || data.length() == 0 || key == null || key.length() == 0) return ""; 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 + * HmacSHA512 加密 + * @param data + * @param key + * @return */ 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 + * HmacSHA512 加密 + * @param data + * @param key + * @return */ 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 + * hmac 加密模版方法 + * @param data 数据 + * @param key + * @param algorithm 算法 + * @return */ - private static byte[] hmacTemplate(final byte[] data, - final byte[] key, - final String algorithm) { + 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); @@ -574,349 +548,249 @@ private static byte[] hmacTemplate(final byte[] data, } } - /////////////////////////////////////////////////////////////////////////// - // 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 + * DES 加密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] encryptDES2Base64(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * DES 加密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static String encryptDES2HexString(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * DES 加密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] encryptDES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * DES 解密 + * @param data + * @param key + * @param transformation + * @param iv + * @returndata */ - public static byte[] decryptBase64DES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * DES 解密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] decryptHexStringDES(final String data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * DES 解密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] decryptDES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * 3DES 加密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] encrypt3DES2Base64(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * 3DES 加密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static String encrypt3DES2HexString(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * 3DES 加密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] encrypt3DES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * 3DES 解密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] decryptBase64_3DES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * 3DES 解密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] decryptHexString3DES(final String data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * 3DES 解密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] decrypt3DES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * AES 加密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] encryptAES2Base64(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * AES 加密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static String encryptAES2HexString(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * AES 加密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] encryptAES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * AES 解密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] decryptBase64AES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * AES 解密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] decryptHexStringAES(final String data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * AES 解密 + * @param data + * @param key + * @param transformation + * @param iv + * @return */ - public static byte[] decryptAES(final byte[] data, - final byte[] key, - final String transformation, - final byte[] iv) { + 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 + * 对称加密模版方法 + * @param data + * @param key + * @param algorithm + * @param transformation + * @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) { + 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); + 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, keySpec); + 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, keySpec, params); + cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKey, params); } return cipher.doFinal(data); } catch (Throwable e) { @@ -925,9 +799,149 @@ private static byte[] symmetricTemplate(final byte[] data, } } - private static final char HEX_DIGITS[] = - {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + /** + * RSA 加密 + * @param data + * @param key + * @param isPublicKey + * @param transformation + * @return + */ + public static byte[] encryptRSA2Base64(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 + * @param transformation + * @return + */ + public static String encryptRSA2HexString(final byte[] data, final byte[] key, final boolean isPublicKey, final String transformation) { + return bytes2HexString(encryptRSA(data, key, isPublicKey, transformation)); + } + + /** + * RSA 加密 + * @param data + * @param key + * @param isPublicKey + * @param transformation + * @return + */ + 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 + * @param transformation + * @return + */ + public static byte[] decryptBase64RSA(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 + * @param transformation + * @return + */ + public static byte[] decryptHexStringRSA(final String data, final byte[] key, final boolean isPublicKey, final String transformation) { + return decryptRSA(hexString2Bytes(data), key, isPublicKey, transformation); + } + /** + * RSA 解密 + * @param data + * @param key + * @param isPublicKey + * @param transformation + * @return + */ + 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 + * @param 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 || data.length == 0 || key == null || key.length == 0) { + return null; + } + try { + 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 len = data.length; + int maxLen = isEncrypt ? 117 : 128; + int count = len / maxLen; + if (count > 0) { + byte[] ret = new byte[0]; + byte[] buff = new byte[maxLen]; + int index = 0; + for (int i = 0; i < count; i++) { + System.arraycopy(data, index, buff, 0, maxLen); + ret = joins(ret, cipher.doFinal(buff)); + index += maxLen; + } + if (index != len) { + int restLen = len - index; + buff = new byte[restLen]; + System.arraycopy(data, index, buff, 0, restLen); + ret = joins(ret, cipher.doFinal(buff)); + } + return ret; + } else { + return cipher.doFinal(data); + } + } catch (Exception e) { + JCLogUtils.eTag(TAG, e, "rsaTemplate"); + } + return null; + } + + // = + + private static byte[] joins(final byte[] prefix, final byte[] suffix) { + byte[] ret = new byte[prefix.length + suffix.length]; + System.arraycopy(prefix, 0, ret, 0, prefix.length); + System.arraycopy(suffix, 0, ret, prefix.length, suffix.length); + return ret; + } + + // Hash 加密转换 + private static final char HEX_DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * 数据加密转换16进制 + * @param bytes + * @return + */ private static String bytes2HexString(final byte[] bytes) { if (bytes == null) return null; int len = bytes.length; @@ -940,6 +954,11 @@ private static String bytes2HexString(final byte[] bytes) { return new String(ret); } + /** + * 十六进制字符串 转换byte数组 + * @param hexString + * @return + */ private static byte[] hexString2Bytes(String hexString) { if (isSpace(hexString)) return null; int len = hexString.length(); @@ -950,12 +969,12 @@ private static byte[] hexString2Bytes(String hexString) { 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])); + ret[i >> 1] = (byte) (hex2Int(hexBytes[i]) << 4 | hex2Int(hexBytes[i + 1])); } return ret; } - private static int hex2Dec(final char hexChar) { + private static int hex2Int(final char hexChar) { if (hexChar >= '0' && hexChar <= '9') { return hexChar - '0'; } else if (hexChar >= 'A' && hexChar <= 'F') { @@ -973,13 +992,18 @@ 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))) { + /** + * 判断字符串是否为 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/common/encrypt/EscapeUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/EscapeUtils.java index 76a6995e3f..0f256053dc 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/EscapeUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/encrypt/EscapeUtils.java @@ -6,11 +6,11 @@ */ public final class EscapeUtils { - private EscapeUtils(){ + private EscapeUtils() { } /** 十六进制 - 0-255 */ - private final static String[] hex = { "00", "01", "02", "03", "04", "05", + 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", @@ -35,7 +35,7 @@ private EscapeUtils(){ "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, + private static final 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, @@ -66,7 +66,7 @@ private EscapeUtils(){ * @return */ public static String escape(String str) { - if (str == null){ + if (str == null) { return null; } StringBuffer sbuf = new StringBuffer(); @@ -101,7 +101,7 @@ public static String escape(String str) { * @return */ public static String unescape(String str) { - if (str == null){ + if (str == null) { return null; } StringBuffer sbuf = new StringBuffer(); diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/MD5Utils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/MD5Utils.java index f876109bf3..9f50d59973 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/MD5Utils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/encrypt/MD5Utils.java @@ -7,7 +7,7 @@ import dev.utils.JCLogUtils; /** - * detail: MD5加密 不可逆(Message Digest,消息摘要算法) + * detail: MD5加密 不可逆(Message Digest,消息摘要算法) * Created by Ttt */ public final class MD5Utils { @@ -17,7 +17,6 @@ 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'}; /** 大写 */ @@ -28,7 +27,7 @@ private MD5Utils() { * @param str 加密内容 * @return */ - public final static String md5(String str) { + public static final String md5(String str) { try { return md5(str.getBytes()); } catch (Exception e) { @@ -42,13 +41,13 @@ public final static String md5(String str) { * @param bytes * @return */ - public final static String md5(byte[] bytes) { + public static final String md5(byte[] bytes) { try { - // 获得MD5摘要算法的 MessageDigest 对象 + // 获取MD5摘要算法的 MessageDigest 对象 MessageDigest mdInst = MessageDigest.getInstance("MD5"); // 使用指定的字节更新摘要 mdInst.update(bytes); - // 获得密文 + // 获取密文 byte[] md = mdInst.digest(); return toHexString(md, HEX_DIGITS); } catch (Exception e) { @@ -62,7 +61,7 @@ public final static String md5(byte[] bytes) { * @param str 加密内容 * @return */ - public final static String md5Upper(String str) { + public static final String md5Upper(String str) { try { return md5Upper(str.getBytes()); } catch (Exception e) { @@ -76,13 +75,13 @@ public final static String md5Upper(String str) { * @param bytes * @return */ - public final static String md5Upper(byte[] bytes) { + public static final String md5Upper(byte[] bytes) { try { - // 获得MD5摘要算法的 MessageDigest 对象 + // 获取MD5摘要算法的 MessageDigest 对象 MessageDigest mdInst = MessageDigest.getInstance("MD5"); // 使用指定的字节更新摘要 mdInst.update(bytes); - // 获得密文 + // 获取密文 byte[] md = mdInst.digest(); return toHexString(md, HEX_DIGITS_UPPER); } catch (Exception e) { @@ -107,7 +106,7 @@ public static String toHexString(byte[] bData) { * @return */ public static String toHexString(byte[] bData, char[] hexDigits) { - if (bData == null || hexDigits == null){ + if (bData == null || hexDigits == null) { return null; } StringBuilder sBuilder = new StringBuilder(bData.length * 2); diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/SHAUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/SHAUtils.java index 6853eda43f..56bacc8bee 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/SHAUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/encrypt/SHAUtils.java @@ -17,7 +17,6 @@ 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'}; @@ -26,7 +25,7 @@ private SHAUtils() { * @param str 加密内容 * @return */ - public final static String sha1(String str) { + public static String sha1(String str) { return shaHex(str, "SHA-1"); } @@ -35,7 +34,7 @@ public final static String sha1(String str) { * @param str 加密内容 * @return */ - public final static String sha224(String str) { + public static String sha224(String str) { return shaHex(str, "SHA-224"); } @@ -44,7 +43,7 @@ public final static String sha224(String str) { * @param str 加密内容 * @return */ - public final static String sha256(String str) { + public static String sha256(String str) { return shaHex(str, "SHA-256"); } @@ -53,7 +52,7 @@ public final static String sha256(String str) { * @param str 加密内容 * @return */ - public final static String sha384(String str) { + public static String sha384(String str) { return shaHex(str, "SHA-384"); } @@ -62,7 +61,7 @@ public final static String sha384(String str) { * @param str 加密内容 * @return */ - public final static String sha512(String str) { + public static String sha512(String str) { return shaHex(str, "SHA-512"); } @@ -74,14 +73,14 @@ public final static String sha512(String str) { * @param sha 加密算法 * @return */ - final static String shaHex(String str, String sha) { + private static final String shaHex(String str, String sha) { try { byte[] btInput = str.getBytes(); - // 获得 SHA-1 摘要算法的 MessageDigest 对象 + // 获取 SHA-1 摘要算法的 MessageDigest 对象 MessageDigest mdInst = MessageDigest.getInstance(sha); // 使用指定的字节更新摘要 mdInst.update(btInput); - // 获得密文 + // 获取密文 byte[] md = mdInst.digest(); return toHexString(md, HEX_DIGITS); } catch (Exception e) { @@ -95,7 +94,7 @@ final static String shaHex(String str, String sha) { * @param fPath 文件地址 * @return */ - final static String getFileSHA(String fPath, String sha) { + private static final String getFileSHA(String fPath, String sha) { try { InputStream fis = new FileInputStream(fPath); byte[] buffer = new byte[1024]; @@ -116,15 +115,15 @@ final static String getFileSHA(String fPath, String sha) { /** * 进行转换 - * @param bData + * @param byteData * @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]); + private static String toHexString(byte[] byteData, char[] hexDigits) { + StringBuilder sBuilder = new StringBuilder(byteData.length * 2); + for (int i = 0, len = byteData.length; i < len; i++) { + sBuilder.append(hexDigits[(byteData[i] & 0xf0) >>> 4]); + sBuilder.append(hexDigits[byteData[i] & 0x0f]); } return sBuilder.toString(); } diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java index faf4188ef8..10a793ffb4 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/encrypt/TripleDESUtils.java @@ -8,7 +8,7 @@ import dev.utils.JCLogUtils; /** - * detail: 3DES对称加密(Triple DES、DESede,进行了三重DES加密的算法,对称加密算法) + * detail: 3DES对称加密(Triple DES、DESede,进行了三重DES加密的算法,对称加密算法) * Created by Ttt */ public final class TripleDESUtils { @@ -23,13 +23,13 @@ private TripleDESUtils() { * 生成密钥 * @return */ - public static byte[] initKey(){ + 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){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "initKey"); } return null; @@ -41,14 +41,14 @@ public static byte[] initKey(){ * @param key * @return */ - public static byte[] encrypt(byte[] data, byte[] key){ + public static byte[] encrypt(byte[] data, byte[] key) { 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){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "encrypt"); } return null; @@ -60,14 +60,14 @@ public static byte[] encrypt(byte[] data, byte[] key){ * @param key * @return */ - public static byte[] decrypt(byte[] data, byte[] key){ + public static byte[] decrypt(byte[] data, byte[] key) { 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){ + } catch (Exception e) { JCLogUtils.eTag(TAG, e, "decrypt"); } return null; diff --git a/DevLibUtils/src/main/java/dev/utils/common/encrypt/XorUtils.java b/DevLibUtils/src/main/java/dev/utils/common/encrypt/XorUtils.java new file mode 100644 index 0000000000..19fc578763 --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/common/encrypt/XorUtils.java @@ -0,0 +1,71 @@ +package dev.utils.common.encrypt; + +/** + * detail: 异或加密工具类 + * Created by Ttt + * 位运算可以实现很多高级,高效的运算。 + * 可用于 IM 二进制数据包加密,第一能够实现加密,第二采用异或加密算法不会改变二进制数据的长度这对二进制数据包封包起到不小的好处 + * 也可用于记事本等场景 + * 参考链接:http://www.cnblogs.com/whoislcj/p/5944917.html + */ +public final class XorUtils { + + private XorUtils() { + } + + /** + * 固定 key 的方式加密 + * 这种方式加密解密 算法一样 + * 加密:byte[] bytes = encryptAsFix("123".getBytes()); + * 解密:String str = new String(encryptAsFix(bytes)); + * @param bytes 待加密数据 + * @return 加密后的数据 + */ + public static byte[] encryptAsFix(byte[] bytes) { + if (bytes == null) { + return null; + } + int len = bytes.length; + int key = 0x12; + for (int i = 0; i < len; i++) { + bytes[i] ^= key; + } + return bytes; + } + + /** + * 非固定 key 的方式加密 + * @param bytes 待加密数据 + * @return 加密后的数据 + */ + public static byte[] encrypt(byte[] bytes) { + if (bytes == null) { + return null; + } + int len = bytes.length; + int key = 0x12; + for (int i = 0; i < len; i++) { + bytes[i] = (byte) (bytes[i] ^ key); + key = bytes[i]; + } + return bytes; + } + + /** + * 解密 + * @param bytes 待解密数据 + * @return 解密后的数据 + */ + public static byte[] decrypt(byte[] bytes) { + if (bytes == null) { + return null; + } + int len = bytes.length; + int key = 0x12; + for (int i = len - 1; i > 0; i--) { + bytes[i] = (byte) (bytes[i] ^ bytes[i - 1]); + } + bytes[0] = (byte) (bytes[0] ^ key); + return bytes; + } +} diff --git a/DevLibUtils/src/main/java/dev/utils/common/random/AliasMethod.java b/DevLibUtils/src/main/java/dev/utils/common/random/AliasMethod.java new file mode 100644 index 0000000000..5ffb6ee4b4 --- /dev/null +++ b/DevLibUtils/src/main/java/dev/utils/common/random/AliasMethod.java @@ -0,0 +1,171 @@ +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: 随机概率采样算法 + * Created by Keith Schwarz + * http://www.keithschwarz.com/interesting/ + * http://www.keithschwarz.com/darts-dice-coins/ + */ +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 +// } +} diff --git a/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadManager.java b/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadManager.java index 9a430696a6..674adaa182 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadManager.java +++ b/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadManager.java @@ -2,6 +2,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.Map; /** * detail: 线程池管理 - 开发类 @@ -9,16 +10,16 @@ */ public final class DevThreadManager { + /** 禁止构造对象 */ + private 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() { - } + private static final Map mapConfig = new HashMap<>(); /** * 获取 DevThreadManager 实例 ,单例模式 @@ -30,7 +31,7 @@ public static synchronized DevThreadPool getInstance(int nThreads) { String key = "n_" + nThreads; // 如果不为null, 则直接返回 DevThreadPool devThreadPool = mapThreads.get(key); - if (devThreadPool != null){ + if (devThreadPool != null) { return devThreadPool; } devThreadPool = new DevThreadPool(nThreads); @@ -46,16 +47,16 @@ public static synchronized DevThreadPool getInstance(int nThreads) { public static synchronized DevThreadPool getInstance(String key) { // 如果不为null, 则直接返回 DevThreadPool devThreadPool = mapThreads.get(key); - if (devThreadPool != null){ + if (devThreadPool != null) { return devThreadPool; } Object obj = mapConfig.get(key); - if (obj != null){ + if (obj != null) { try { // 判断是否属于线程池类型 - if (obj instanceof DevThreadPool.DevThreadPoolType){ + if (obj instanceof DevThreadPool.DevThreadPoolType) { devThreadPool = new DevThreadPool((DevThreadPool.DevThreadPoolType) obj); - } else if (obj instanceof Integer){ + } else if (obj instanceof Integer) { devThreadPool = new DevThreadPool((Integer) obj); } else { // 其他类型, 统一转换 Integer devThreadPool = new DevThreadPool(Integer.parseInt((String) obj)); @@ -64,7 +65,7 @@ public static synchronized DevThreadPool getInstance(String key) { mapThreads.put(key, devThreadPool); return devThreadPool; } - } catch (Exception e){ + } catch (Exception e) { return sDevThreadPool; } } @@ -77,8 +78,8 @@ public static synchronized DevThreadPool getInstance(String key) { * 初始化配置信息 * @param mapConfig */ - public static void initConfig(HashMap mapConfig){ - if (mapConfig != null){ + public static void initConfig(Map mapConfig) { + if (mapConfig != null) { mapConfig.putAll(mapConfig); } } @@ -88,7 +89,7 @@ public static void initConfig(HashMap mapConfig){ * @param key * @param val */ - public static void putConfig(String key, Object val){ + public static void putConfig(String key, Object val) { mapConfig.put(key, val); } @@ -96,7 +97,7 @@ public static void putConfig(String key, Object val){ * 移除配置信息 * @param key */ - public static void removeConfig(String 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 index b995c02a2e..70315c333d 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadPool.java +++ b/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadPool.java @@ -46,9 +46,9 @@ public final class DevThreadPool { * 构造函数 * @param nThreads */ - public DevThreadPool(int nThreads){ + public DevThreadPool(int nThreads) { // 如果小于等于 0, 则默认使用 1 - if (nThreads <= 0){ + if (nThreads <= 0) { nThreads = 1; } this.threadPool = Executors.newFixedThreadPool(nThreads); @@ -60,15 +60,15 @@ public DevThreadPool(int nThreads){ * 构造函数 * @param threadPool */ - public DevThreadPool(ExecutorService threadPool){ + public DevThreadPool(ExecutorService threadPool) { this.threadPool = threadPool; } - public DevThreadPool(DevThreadPoolType devThreadPoolType){ + public DevThreadPool(DevThreadPoolType devThreadPoolType) { // 初始化定时器任务 this.scheduleExec = Executors.newScheduledThreadPool(getThreads()); // = - if (devThreadPoolType != null){ + if (devThreadPoolType != null) { // = switch (devThreadPoolType) { case SINGLE: @@ -169,7 +169,7 @@ public void execute(Runnable runnable) { * @param runnables */ public void execute(final List runnables) { - if (threadPool == null || runnables == null){ + if (threadPool == null || runnables == null) { return; } for (Runnable command : runnables) { @@ -186,7 +186,7 @@ public void execute(final List runnables) { * @param _class */ public void execute(final Method method, final Object _class) { - if (threadPool != null){ + if (threadPool != null) { threadPool.execute(new Runnable() { @Override public void run() { @@ -207,20 +207,20 @@ public void run() { * 如果已经关闭,则调用没有作用。 */ public void shutdown() { - if (threadPool != null){ + if (threadPool != null) { threadPool.shutdown(); } } /** - * shutdownNow会尝试中断正在执行的任务(其主要是中断一些指定方法如sleep方法),并且停止执行等待队列中提交的任务。 + * shutdownNow会尝试中断正在执行的任务(其主要是中断一些指定方法如sleep方法),并且停止执行等待队列中提交的任务。 * 试图停止所有正在执行的活动任务 * 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。 * 无法保证能够停止正在处理的活动执行任务,但是会尽力尝试。 * @return */ public List shutdownNow() { - if (threadPool != null){ + if (threadPool != null) { return threadPool.shutdownNow(); } return null; @@ -231,7 +231,7 @@ public List shutdownNow() { * @return */ public boolean isShutdown() { - if (threadPool != null){ + if (threadPool != null) { return threadPool.isShutdown(); } return false; @@ -245,7 +245,7 @@ public boolean isShutdown() { * @return */ public boolean isTerminated() { - if (threadPool != null){ + if (threadPool != null) { return threadPool.isTerminated(); } return false; @@ -330,7 +330,7 @@ public List> invokeAll(final Collection> tas /** * 执行给定的任务 - * 如果某个任务已成功完成(也就是未抛出异常),则返回其结果。 + * 如果某个任务已成功完成(也就是未抛出异常),则返回其结果。 * 一旦正常或异常返回后,则取消尚未完成的任务。 * 如果此操作正在进行时修改了给定的collection,则此方法的结果是不确定的。 * @param tasks 任务集合 @@ -345,7 +345,7 @@ public T invokeAny(final Collection> tasks) throws Int /** * 执行给定的任务 - * 如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果。 + * 如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果。 * 一旦正常或异常返回后,则取消尚未完成的任务。 * 如果此操作正在进行时修改了给定的collection,则此方法的结果是不确定的。 * @param tasks 任务集合 @@ -364,8 +364,6 @@ public T invokeAny(final Collection> tasks, final long // == - - /** * 延迟执行Runnable命令 * @param command 命令 diff --git a/DevLibUtils/src/main/java/dev/utils/common/validator/BankCheckUtils.java b/DevLibUtils/src/main/java/dev/utils/common/validator/BankCheckUtils.java index 792cc2d256..4480553d6a 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/validator/BankCheckUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/validator/BankCheckUtils.java @@ -2,29 +2,25 @@ /** * 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整除,认定校验通过。 + * Created by AbrahamCaiJin + * Update to 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() { + } + /* * 银行卡是由”发卡行标识代码 + 自定义 + 校验码 “等部分组成的 BIN号 银联标准卡与以往发行的银行卡最直接的区别就是其卡号前6位数字的不同。 * 银行卡卡号的前6位是用来表示发卡银行或机构的,称为“发卡行识别码”(Bank Identification Number,缩写为“BIN”)。 @@ -33,7 +29,7 @@ public final class BankCheckUtils { * ,新发的非银联标准卡使用旧的联网通用银联标识)、 卡号前6位为622126至622925之一的银行卡,是中国银行卡产业共有的民族品牌。 */ // BIN号 - private final static String[] BANKBIN = { "621098", "622150", "622151", + private static final String[] BANKBIN = { "621098", "622150", "622151", "622181", "622188", "955100", "621095", "620062", "621285", "621798", "621799", "621797", "620529", "622199", "621096", "621622", "623219", "621674", "623218", "621599", "370246", @@ -266,13 +262,14 @@ public final class BankCheckUtils { "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信用卡", @@ -350,7 +347,7 @@ public final class BankCheckUtils { "中国工商银行·牡丹百夫长信用卡", "工商银行·工银财富卡", "中国工商银行·中小商户采购卡", "中国工商银行·中小商户采购卡", "中国工商银行·环球旅行金卡", "中国工商银行·环球旅行白金卡", "中国工商银行·牡丹工银大来卡", "中国工商银行·牡丹工银大莱卡", "中国工商银行·IC金卡", "中国工商银行·IC白金卡", - "中国工商银行·工行IC卡(红卡)", "中国工商银行布鲁塞尔分行·借记卡", "中国工商银行布鲁塞尔分行·预付卡", + "中国工商银行·工行IC卡(红卡)", "中国工商银行布鲁塞尔分行·借记卡", "中国工商银行布鲁塞尔分行·预付卡", "中国工商银行布鲁塞尔分行·预付卡", "中国工商银行金边分行·借记卡", "中国工商银行金边分行·信用卡", "中国工商银行金边分行·借记卡", "中国工商银行金边分行·信用卡", "中国工商银行加拿大分行·借记卡", "中国工商银行加拿大分行·借记卡", "中国工商银行加拿大分行·预付卡", "中国工商银行巴黎分行·借记卡", @@ -516,10 +513,10 @@ public final class BankCheckUtils { "浦东发展银行·公务卡金卡", "浦东发展银行·公务卡普卡", "浦东发展银行·东方卡", "浦东发展银行·东方卡", "浦东发展银行·浦发单币卡", "浦东发展银行·浦发联名信用卡", "浦东发展银行·浦发银联白金卡", "浦东发展银行·轻松理财普卡", "浦东发展银行·移动联名卡", "浦东发展银行·轻松理财消贷易卡", - "浦东发展银行·轻松理财普卡(复合卡)", "浦东发展银行·贷记卡", "浦东发展银行·贷记卡", - "浦东发展银行·东方借记卡(复合卡)", "浦东发展银行·电子现金卡(IC卡)", "浦东发展银行·移动浦发联名卡", - "浦东发展银行·东方-标准准贷记卡", "浦东发展银行·轻松理财金卡(复合卡)", "浦东发展银行·轻松理财白金卡(复合卡)", - "浦东发展银行·轻松理财钻石卡(复合卡)", "浦东发展银行·东方卡", "恒丰银行·九州IC卡", + "浦东发展银行·轻松理财普卡(复合卡)", "浦东发展银行·贷记卡", "浦东发展银行·贷记卡", + "浦东发展银行·东方借记卡(复合卡)", "浦东发展银行·电子现金卡(IC卡)", "浦东发展银行·移动浦发联名卡", + "浦东发展银行·东方-标准准贷记卡", "浦东发展银行·轻松理财金卡(复合卡)", "浦东发展银行·轻松理财白金卡(复合卡)", + "浦东发展银行·轻松理财钻石卡(复合卡)", "浦东发展银行·东方卡", "恒丰银行·九州IC卡", "恒丰银行·九州借记卡(银联卡)", "恒丰银行·九州借记卡(银联卡)", "天津市商业银行·银联卡(银联卡)", "烟台商业银行·金通卡", "潍坊银行·鸢都卡(银联卡)", "潍坊银行·鸳都卡(银联卡)", "临沂商业银行·沂蒙卡(银联卡)", "临沂商业银行·沂蒙卡(银联卡)", "日照市商业银行·黄海卡", "日照市商业银行·黄海卡(银联卡)", "浙商银行·商卡", @@ -534,8 +531,8 @@ public final class BankCheckUtils { "上海银行·申卡贷记卡普通卡", "上海银行·申卡贷记卡金卡", "上海银行·万事达白金卡", "上海银行·万事达星运卡", "上海银行·申卡贷记卡金卡", "上海银行·申卡贷记卡普通卡", "上海银行·安融卡", "上海银行·分期付款信用卡", "上海银行·信用卡", "上海银行·个人公务卡", "上海银行·安融卡", "上海银行·上海银行银联白金卡", - "上海银行·贷记IC卡", "上海银行·中国旅游卡(IC普卡)", "上海银行·中国旅游卡(IC金卡)", - "上海银行·中国旅游卡(IC白金卡)", "上海银行·万事达钻石卡", "上海银行·淘宝IC普卡", "北京银行·京卡借记卡", + "上海银行·贷记IC卡", "上海银行·中国旅游卡(IC普卡)", "上海银行·中国旅游卡(IC金卡)", + "上海银行·中国旅游卡(IC白金卡)", "上海银行·万事达钻石卡", "上海银行·淘宝IC普卡", "北京银行·京卡借记卡", "北京银行·京卡(银联卡)", "北京银行·京卡借记卡", "北京银行·京卡", "北京银行·京卡", "北京银行·借记IC卡", "北京银行·京卡贵宾金卡", "北京银行·京卡贵宾白金卡", "吉林银行·君子兰一卡通(银联卡)", "吉林银行·君子兰卡(银联卡)", "吉林银行·长白山金融IC卡", "吉林银行·信用卡", "吉林银行·信用卡", @@ -567,7 +564,7 @@ public final class BankCheckUtils { "天津银行·商务卡", "宁夏银行·宁夏银行公务卡", "宁夏银行·宁夏银行福农贷记卡", "宁夏银行·如意卡(银联卡)", "宁夏银行·宁夏银行福农借记卡", "宁夏银行·如意借记卡", "宁夏银行·如意IC卡", "宁夏银行·宁夏银行如意借记卡", "宁夏银行·中国旅游卡", "齐商银行·金达卡(银联卡)", "齐商银行·金达借记卡(银联卡)", "齐商银行·金达IC卡", - "徽商银行·黄山卡", "徽商银行·黄山卡", "徽商银行·借记卡", "徽商银行·徽商银行中国旅游卡(安徽)", + "徽商银行·黄山卡", "徽商银行·黄山卡", "徽商银行·借记卡", "徽商银行·徽商银行中国旅游卡(安徽)", "徽商银行合肥分行·黄山卡", "徽商银行芜湖分行·黄山卡(银联卡)", "徽商银行马鞍山分行·黄山卡(银联卡)", "徽商银行淮北分行·黄山卡(银联卡)", "徽商银行安庆分行·黄山卡(银联卡)", "重庆银行·长江卡(银联卡)", "重庆银行·长江卡(银联卡)", "重庆银行·长江卡", "重庆银行·借记IC卡", "哈尔滨银行·丁香一卡通(银联卡)", @@ -582,7 +579,7 @@ public final class BankCheckUtils { "台州银行·大唐卡(银联卡)", "台州银行·大唐卡", "台州银行·借记卡", "台州银行·公务卡", "泉州银行·海峡银联卡(银联卡)", "泉州银行·海峡储蓄卡", "泉州银行·海峡银联卡(银联卡)", "泉州银行·海峡卡", "泉州银行·公务卡", "昆明商业银行·春城卡(银联卡)", "昆明商业银行·春城卡(银联卡)", - "昆明商业银行·富滇IC卡(复合卡)", "阜新银行·借记IC卡", "嘉兴银行·南湖借记卡(银联卡)", "廊坊银行·白金卡", + "昆明商业银行·富滇IC卡(复合卡)", "阜新银行·借记IC卡", "嘉兴银行·南湖借记卡(银联卡)", "廊坊银行·白金卡", "廊坊银行·金卡", "廊坊银行·银星卡(银联卡)", "廊坊银行·龙凤呈祥卡", "内蒙古银行·百灵卡(银联卡)", "内蒙古银行·成吉思汗卡", "湖州市商业银行·百合卡", "湖州市商业银行·", "沧州银行·狮城卡", "南宁市商业银行·桂花卡(银联卡)", "包商银行·雄鹰卡(银联卡)", "包商银行·包头市商业银行借记卡", @@ -672,7 +669,7 @@ public static boolean checkBankCard(String cardId) { } /** - * 从不含校验位的银行卡卡号采用 Luhm 校验算法获得校验位 + * 从不含校验位的银行卡卡号采用 Luhm 校验算法获取校验位 * @param nonCheckCodeCardId * @return */ diff --git a/DevLibUtils/src/main/java/dev/utils/common/validator/IDCardUtils.java b/DevLibUtils/src/main/java/dev/utils/common/validator/IDCardUtils.java index f4caf0fcdb..d24776660f 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/validator/IDCardUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/validator/IDCardUtils.java @@ -11,30 +11,29 @@ /** * detail: 居民身份证工具类 - * @Description:主要功能: 居民身份证工具类 - * @Prject: CommonUtilLibrary - * @Package: com.jingewenku.abrahamcaijin.commonutil - * @author: AbrahamCaiJin - * @date: 2017年07月21日 15:08 - * @Copyright: 个人版权所有 - * @Company: - * @version: 1.0.0 + * Created by AbrahamCaiJin + * Update to Ttt */ - public final class IDCardUtils { + private 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 power[] = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 }; + // 身份证最少位数 + private static final int CHINA_ID_MIN_LENGTH = 15; + // 身份证最大位数 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(); + // 省份编码 + private static Map cityCodes = new HashMap<>(); + // 台湾身份首字母对应数字 + private static Map twFirstCode = new HashMap<>(); + // 香港身份首字母对应数字 + private static Map hkFirstCode = new HashMap<>(); - //香港身份首字母对应数字 static { cityCodes.put("11", "北京"); cityCodes.put("12", "天津"); @@ -111,7 +110,6 @@ public final class IDCardUtils { /** * 将身份证的每位和对应位的加权因子相乘之后,再获取和值 - * * @param iArr int[] * @return 身份证编码 */ @@ -130,8 +128,7 @@ public static int getPowerSum(int[] iArr) { } /** - * 将power和值与1 1取模获得余数进行校验码判断 - * + * 将power和值与1 1取模获取余数进行校验码判断 * @param iSum sum * @return 校验位 */ @@ -177,7 +174,6 @@ public static String getCheckCode18(int iSum) { /** * 将字符数组转换成数字数组 - * * @param ca 字符数组 * @return 数字数组 */ @@ -196,7 +192,6 @@ public static int[] converCharToInt(char[] ca) { /** * 数字验证 - * * @param val 待验证的字符串 * @return 是否是数字 */ @@ -206,12 +201,10 @@ private static boolean isNum(String val) { /** * 身份证校验规则,验证18位身份编码是否合法 - * * @param idCard 待验证的字符串 * @return 校验结果 */ public static boolean validateIdCard18(String idCard) { - boolean bTrue = false; if (idCard == null) { return false; @@ -241,7 +234,6 @@ public static boolean validateIdCard18(String idCard) { /** * 身份证校验规则,验证15位身份编码是否合法 - * * @param idCard 待验证的字符串 * @return 校验结果 */ @@ -274,7 +266,6 @@ public static boolean validateIdCard15(String idCard) { /** * 验证小于当前日期 是否有效 - * * @param iYear 待验证日期(年) * @param iMonth 待验证日期(月 1-12) * @param iDate 待验证日期(日) @@ -310,7 +301,6 @@ private static boolean validateDateSmllerThenNow(int iYear, int iMonth, int iDat /** * 将15位身份证号码转换为18位 - * * @param idCard 15位身份编码 * @return 18位身份编码 */ @@ -354,7 +344,6 @@ public static String convert15CardTo18(String idCard) { /** * 验证台湾身份证号码 - * * @param idCard 身份证号码 * @return 是否符合 */ @@ -378,7 +367,6 @@ public static boolean validateTWCard(String idCard) { * 身份证前2位为英文字符,如果只出现一个英文字符则表示第一位是空格,对应数字58 前2位英文字符A-Z分别对应数字10-35 * 最后一位校验码为0-9的数字加上字符"A","A"代表10 * 将身份证号码全部转换为数字,分别对应乘9-1相加的总和,整除11则证件号码有效 - * * @param idCard 身份证号码 * @return 验证码是否符合 */ @@ -407,6 +395,11 @@ public static boolean validateHKCard(String idCard) { return (sum % 11 == 0); } + /** + * 判断 10 位数的身份证号, 是否合法 + * @param idCard + * @return + */ public static String[] validateIdCard10(String idCard) { String[] info = new String[3]; String card = idCard.replaceAll("[\\(|\\)]", ""); @@ -442,7 +435,6 @@ public static String[] validateIdCard10(String idCard) { /** * 验证身份证是否合法 - * * @param idCard 待验证的字符串 * @return 身份证是否合法 */ @@ -465,7 +457,6 @@ public static boolean validateCard(String idCard) { /** * 根据身份编号获取年龄 - * * @param idCard 身份编号 * @return 年龄 */ @@ -483,7 +474,6 @@ public static int getAgeByIdCard(String idCard) { /** * 根据身份编号获取生日 - * * @param idCard 身份编号 * @return 生日(yyyyMMdd) */ @@ -499,7 +489,6 @@ public static String getBirthByIdCard(String idCard) { /** * 根据身份编号获取生日 - * * @param idCard 身份编号 * @return 生日(yyyyMMdd) */ @@ -509,7 +498,6 @@ public static String getBirthdayByIdCard(String idCard) { /** * 根据身份编号获取生日年 - * * @param idCard 身份编号 * @return 生日(yyyy) */ @@ -525,7 +513,6 @@ public static Short getYearByIdCard(String idCard) { /** * 根据身份编号获取生日月 - * * @param idCard 身份编号 * @return 生日(MM) */ @@ -541,7 +528,6 @@ public static Short getMonthByIdCard(String idCard) { /** * 根据身份编号获取生日天 - * * @param idCard 身份编号 * @return 生日(dd) */ @@ -557,7 +543,6 @@ public static Short getDateByIdCard(String idCard) { /** * 根据身份编号获取性别 - * * @param idCard 身份编号 * @return 性别(M-男,F-女,N-未知) */ @@ -577,7 +562,6 @@ public static String getGenderByIdCard(String idCard) { /** * 根据身份编号获取户籍省份 - * * @param idCard 身份编码 * @return 省级编码 */ @@ -591,5 +575,4 @@ public static String getProvinceByIdCard(String idCard) { 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 index 3711066452..28d2196266 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToIDCardUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToIDCardUtils.java @@ -12,16 +12,16 @@ private ValiToIDCardUtils() { } /** 正则表达式:验证身份证 */ - static final String REGEX_ID_CARD = "(^\\d{15}$)|(^\\d{17}([0-9]|X)$)"; + private 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}$/"; + private 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]\\)"; + private 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]\\)"; + private static final String REGEX_AM_ID_CARD = "[157][0-9]{6}\\([0-9]\\)"; // ==== 内部方法 ===== @@ -40,7 +40,7 @@ public static boolean isEmpty(String str) { * @param input * @return */ - static boolean match(String regex, String input) { + private static boolean match(String regex, String input) { return Pattern.matches(regex, input); } @@ -52,7 +52,7 @@ static boolean match(String regex, String input) { * @return */ public static boolean isIDCard(String idCard) { - if (!isEmpty(idCard)){ + if (!isEmpty(idCard)) { return match(REGEX_ID_CARD, idCard); } return false; @@ -63,8 +63,8 @@ public static boolean isIDCard(String idCard) { * @param idCard * @return */ - public static boolean isHKIDCard(String idCard){ - if (!isEmpty(idCard)){ + public static boolean isHKIDCard(String idCard) { + if (!isEmpty(idCard)) { return match(REGEX_XG_ID_CARD, idCard); } return false; @@ -75,8 +75,8 @@ public static boolean isHKIDCard(String idCard){ * @param idCard * @return */ - public static boolean isAMIDCard(String idCard){ - if (!isEmpty(idCard)){ + public static boolean isAMIDCard(String idCard) { + if (!isEmpty(idCard)) { return match(REGEX_AM_ID_CARD, idCard); } return false; @@ -87,8 +87,8 @@ public static boolean isAMIDCard(String idCard){ * @param idCard * @return */ - public static boolean isTWIDCard(String idCard){ - if (!isEmpty(idCard)){ + 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 index 1c24de7e47..42603f61b2 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/validator/ValiToPhoneUtils.java @@ -31,7 +31,7 @@ public static boolean isEmpty(String str) { * @param input * @return */ - static boolean match(String regex, String input) { + private static boolean match(String regex, String input) { return Pattern.matches(regex, input); } @@ -42,8 +42,8 @@ static boolean match(String regex, String input) { * @param phone * @return */ - public static boolean isPhoneCheck(String phone){ - if (!isEmpty(phone)){ + public static boolean isPhoneCheck(String phone) { + if (!isEmpty(phone)) { return match(CHAIN_PHONE_FORMAT_CHECK, phone); } return false; @@ -54,8 +54,8 @@ public static boolean isPhoneCheck(String phone){ * @param phone * @return */ - public static boolean isPhone(String phone){ - if (!isEmpty(phone)){ + public static boolean isPhone(String phone) { + if (!isEmpty(phone)) { return match(CHINA_PHONE_PATTERN, phone); } return false; @@ -66,8 +66,8 @@ public static boolean isPhone(String phone){ * @param phone * @return */ - public static boolean isPhoneToChinaTelecom(String phone){ - if (!isEmpty(phone)){ + public static boolean isPhoneToChinaTelecom(String phone) { + if (!isEmpty(phone)) { return match(CHINA_TELECOM_PATTERN, phone); } return false; @@ -78,8 +78,8 @@ public static boolean isPhoneToChinaTelecom(String phone){ * @param phone * @return */ - public static boolean isPhoneToChinaUnicom(String phone){ - if (!isEmpty(phone)){ + public static boolean isPhoneToChinaUnicom(String phone) { + if (!isEmpty(phone)) { return match(CHINA_UNICOM_PATTERN, phone); } return false; @@ -90,8 +90,8 @@ public static boolean isPhoneToChinaUnicom(String phone){ * @param phone * @return */ - public static boolean isPhoneToChinaMobile(String phone){ - if (!isEmpty(phone)){ + public static boolean isPhoneToChinaMobile(String phone) { + if (!isEmpty(phone)) { return match(CHINA_MOBILE_PATTERN, phone); } return false; @@ -102,8 +102,8 @@ public static boolean isPhoneToChinaMobile(String phone){ * @param phone * @return */ - public static boolean isPhoneToHkMobile(String phone){ - if (!isEmpty(phone)){ + public static boolean isPhoneToHkMobile(String phone) { + if (!isEmpty(phone)) { return match(HK_PHONE_PATTERN, phone); } return false; @@ -115,7 +115,7 @@ public static boolean isPhoneToHkMobile(String phone){ * @return */ public static boolean isPhoneCallNum(String phone) { - if (!isEmpty(phone)){ + if (!isEmpty(phone)) { return match(PHONE_CALL_PATTERN, phone); } return false; @@ -126,27 +126,27 @@ public static boolean isPhoneCallNum(String phone) { // == 手机号判断 == /** 简单手机号码校验 => 校验手机号码的长度和1开头 (是否11位)*/ - static final String CHAIN_PHONE_FORMAT_CHECK = "^(?:\\+86)?1\\d{10}$"; + private static final String CHAIN_PHONE_FORMAT_CHECK = "^(?:\\+86)?1\\d{10}$"; // 中国手机号正则 - static final String CHINA_PHONE_PATTERN; + private static final String CHINA_PHONE_PATTERN; /** 中国电信号码正则 */ - static final String CHINA_TELECOM_PATTERN; + private static final String CHINA_TELECOM_PATTERN; /** 中国联通号码正则 */ - static final String CHINA_UNICOM_PATTERN; + private static final String CHINA_UNICOM_PATTERN; /** 中国移动号码正则 */ - static final String CHINA_MOBILE_PATTERN; + private static final String CHINA_MOBILE_PATTERN; /* 香港手机号码正则 => 香港手机号码8位数,5|6|8|9开头+7位任意数*/ - static final String HK_PHONE_PATTERN = "^(5|6|8|9)\\d{7}$"; + private 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})?$"; + private static final String PHONE_CALL_PATTERN = "^(?:\\(\\d{3,4}\\)|\\d{3,4}-)?\\d{7,8}(?:-\\d{1,4})?$"; static { // ====== 中国电信 ====== diff --git a/DevLibUtils/src/main/java/dev/utils/common/validator/ValidatorUtils.java b/DevLibUtils/src/main/java/dev/utils/common/validator/ValidatorUtils.java index b4efe18e52..1d3b07e984 100644 --- a/DevLibUtils/src/main/java/dev/utils/common/validator/ValidatorUtils.java +++ b/DevLibUtils/src/main/java/dev/utils/common/validator/ValidatorUtils.java @@ -18,34 +18,34 @@ private ValidatorUtils() { private static final String TAG = ValidatorUtils.class.getSimpleName(); /** 正则表达式:验证数字 */ - static final String REGEX_NUMBER = "^[0-9]*$"; + private 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]+$"; + private 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}$"; + private 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]*)*$"; + private static final String REGEX_REALNAME = "^[\\u4e00-\\u9fa5]+(•[\\u4e00-\\u9fa5]*)*$|^[\\u4e00-\\u9fa5]+(·[\\u4e00-\\u9fa5]*)*$"; /** 正则表达式:验证昵称 */ - static final String REGEX_NICKNAME = "^[\\u4E00-\\u9FA5A-Za-z0-9_]+$"; + private static final String REGEX_NICKNAME = "^[\\u4E00-\\u9FA5A-Za-z0-9_]+$"; /** 正则表达式:验证用户名(不包含中文和特殊字符)如果用户名使用手机号码或邮箱 则结合手机号验证和邮箱验证 */ - static final String REGEX_USERNAME = "^[a-zA-Z]\\w{5,17}$"; + private static final String REGEX_USERNAME = "^[a-zA-Z]\\w{5,17}$"; /** 正则表达式:验证密码(不包含特殊字符) */ - static final String REGEX_PASSWORD = "^[a-zA-Z0-9]{6,18}$"; + private 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,}$"; + private 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-./?%&=]*)?"; + private 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})"; + private 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})"; // ==== 内部方法 ===== @@ -64,7 +64,7 @@ public static boolean isEmpty(String str) { * @param input * @return */ - static boolean match(String regex, String input) { + private static boolean match(String regex, String input) { return Pattern.matches(regex, input); } @@ -76,7 +76,7 @@ static boolean match(String regex, String input) { * @return */ public static boolean isNumber(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_NUMBER, str); } return false; @@ -88,7 +88,7 @@ public static boolean isNumber(String str) { * @return */ public static boolean isLetter(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match("^[A-Za-z]+$", str); } return false; @@ -100,7 +100,7 @@ public static boolean isLetter(String str) { * @return */ public static boolean isNumberLetter(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match("^[A-Za-z0-9]+$", str); } return false; @@ -112,7 +112,7 @@ public static boolean isNumberLetter(String str) { * @return */ public static boolean isSpec(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_SPECIAL, str); } return false; @@ -124,7 +124,7 @@ public static boolean isSpec(String str) { * @return */ public static boolean isWx(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_WX, str); } return false; @@ -136,7 +136,7 @@ public static boolean isWx(String str) { * @return */ public static boolean isRealName(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_REALNAME, str); } return false; @@ -148,7 +148,7 @@ public static boolean isRealName(String str) { * @return */ public static boolean isNickName(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_NICKNAME, str); } return false; @@ -160,7 +160,7 @@ public static boolean isNickName(String str) { * @return */ public static boolean isUserName(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_USERNAME, str); } return false; @@ -172,7 +172,7 @@ public static boolean isUserName(String str) { * @return */ public static boolean isPassword(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_PASSWORD, str); } return false; @@ -184,7 +184,7 @@ public static boolean isPassword(String str) { * @return */ public static boolean isEmail(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_EMAIL, str); } return false; @@ -196,7 +196,7 @@ public static boolean isEmail(String str) { * @return */ public static boolean isUrl(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_URL, str); } return false; @@ -208,7 +208,7 @@ public static boolean isUrl(String str) { * @return */ public static boolean isIPAddress(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_IP_ADDR, str); } return false; @@ -220,7 +220,7 @@ public static boolean isIPAddress(String str) { * @return */ public static boolean isIP(String str) { - if (!isEmpty(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(); @@ -231,13 +231,13 @@ public static boolean isIP(String str) { // ======= // // http://blog.csdn.net/myfuturein/article/details/6885216 -// [\\u0391-\\uFFE5]匹配双字节字符(汉字+符号) +// [\\u0391-\\uFFE5]匹配双字节字符(汉字+符号) // [\\u4e00-\\u9fa5]注意只匹配汉字,不匹配双字节字符 /** 正则表达式:验证汉字 */ - static final String REGEX_CHINESE = "^[\u4e00-\u9fa5]+$"; + private static final String REGEX_CHINESE = "^[\u4e00-\u9fa5]+$"; /** 正则表达式:验证汉字(含双角符号) */ - static final String REGEX_CHINESE_ALL = "^[\u0391-\uFFE5]+$"; + private static final String REGEX_CHINESE_ALL = "^[\u0391-\uFFE5]+$"; /** * 校验汉字(无符号,纯汉字) @@ -245,7 +245,7 @@ public static boolean isIP(String str) { * @return */ public static boolean isChinese(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_CHINESE, str); } return false; @@ -257,7 +257,7 @@ public static boolean isChinese(String str) { * @return */ public static boolean isChineseAll(String str) { - if (!isEmpty(str)){ + if (!isEmpty(str)) { return match(REGEX_CHINESE_ALL, str); } return false; diff --git a/DevLibUtils/src/main/res/drawable-hdpi/dev_toast_frame.png b/DevLibUtils/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 DevLibUtils/src/main/res/drawable-hdpi/dev_toast_frame.9.png 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-hdpi/dev_toast_ic_clear_white.png b/DevLibUtils/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 DevLibUtils/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/DevLibUtils/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 DevLibUtils/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/DevLibUtils/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 DevLibUtils/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/DevLibUtils/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 DevLibUtils/src/main/res/drawable-hdpi/dev_toast_icon_warning_white.png 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_frame.png b/DevLibUtils/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 DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_frame.9.png 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/DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_ic_clear_white.png b/DevLibUtils/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 DevLibUtils/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/DevLibUtils/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 DevLibUtils/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/DevLibUtils/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 DevLibUtils/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/DevLibUtils/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 DevLibUtils/src/main/res/drawable-xhdpi/dev_toast_icon_warning_white.png diff --git a/DevLibUtils/src/main/res/values-zh-rCN/strings.xml b/DevLibUtils/src/main/res/values-zh-rCN/strings.xml index 7f1d83c047..eb1c442a46 100644 --- a/DevLibUtils/src/main/res/values-zh-rCN/strings.xml +++ b/DevLibUtils/src/main/res/values-zh-rCN/strings.xml @@ -10,8 +10,8 @@ SHA256 首次安装时间 最近更新时间 - app 最低支持版本 - app 兼容sdk版本 + App 最低支持版本 + App 兼容sdk版本 Apk 安装包大小 有效时间 签名是否过期 diff --git a/DevLibUtils/src/main/res/values/strings.xml b/DevLibUtils/src/main/res/values/strings.xml index 2ff064c298..3d0774e1d9 100644 --- a/DevLibUtils/src/main/res/values/strings.xml +++ b/DevLibUtils/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/utils_readme/USE_CONFIG.md b/DevLibUtils/utils_readme/USE_CONFIG.md new file mode 100644 index 0000000000..ea8e31d206 --- /dev/null +++ b/DevLibUtils/utils_readme/USE_CONFIG.md @@ -0,0 +1,63 @@ + +## 初始化 + +```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; + 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/DevLibUtils/utils_readme/logger/DevLogger.md) + +## [DevToast Toast工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/toast/DevToast.md) + +## [ToastTintUtils Toast美化工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/toast/ToastTintUtils.md) + +## [SnackbarUtils Snackbar工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/snackbar/SnackbarUtils.md) + +## [FileRecordUtils、AnalysisRecordUtils、DevLoggerUtils 日志、异常文件记录保存工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/record/FileRecord.md) + +## [SharedUtils - SharedPreferences工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/share/SharedUtils.md) + +## [ShapeUtils - Shape工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/shape/ShapeUtils.md) + +## [DevCache - 缓存工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/cache/DevCache.md) + +## [TimerManager 定时器工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/timer/TimerManager.md) + +## [DevThreadManager - 线程工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/thread/DevThreadManager.md) + +## [DevMediaManager 多媒体工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/media/DevMediaManager.md) + +## [WifiHotUtils - Wifi热点工具类文档](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/utils_readme/cache/DevCache.md b/DevLibUtils/utils_readme/cache/DevCache.md new file mode 100644 index 0000000000..3ca45e10ad --- /dev/null +++ b/DevLibUtils/utils_readme/cache/DevCache.md @@ -0,0 +1,66 @@ +# 线程工具类 + +#### 使用演示类 [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/DevLibUtils/src/main/java/dev/utils/app/cache) + +* 缓存工具类([DevCache.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/cache/DevCache.java)):缓存工具类, 提供各种保存数据方法 + +* 缓存管理类([DevCacheManager.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/cache/DevCacheManager.java)):内部缓存管理类 + +* 缓存处理工具类([DevCacheUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/utils_readme/logger/DevLogger.md b/DevLibUtils/utils_readme/logger/DevLogger.md new file mode 100644 index 0000000000..271291702a --- /dev/null +++ b/DevLibUtils/utils_readme/logger/DevLogger.md @@ -0,0 +1,236 @@ +# 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/DevLibUtils/src/main/java/dev/utils/app/logger) + +* 日志操作类([DevLogger](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/logger/DevLogger.java)):日志操作类(对外公开直接调用),直接调用日志输入类方法。 + +* 日志操作工具类([DevLoggerUtils](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java)):提供常用日志配置快捷获取方法、以及日志存储方法等 + +* 日志接口([IPrinter](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/logger/IPrinter.java)):主要编写可以被外部调用接口,以及可以操作的类型。 + +* 日志输出类([LoggerPrinter](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/logger/LoggerPrinter.java)):实现日志接口,并且对对应的方法,进行处理,最终打印。 + +* 日志设置([LogConfig](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/logger/LogConfig.java)):该类主要控制日志输出方式,以及是否输入日志,堆栈方法等。 + +* 日志配置([LogConstants](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/logger/LogConstants.java)):该类主要是常量配置信息。 + +* 日志级别([LogLevel](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/logger/LogLevel.java)):该类主要控制日志级别。 + +* 日志内部工具类([Utils](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/logger/Utils.java)):内部快捷操作工具类(获取应用信息、设备信息、存储文件等) + + +#### 框架亮点 + +* 支持全局日志统一配置, 以及部分日志个性化配置 + +* 支持无限长度日志打印, 无 Logcat 4000 字符限制 + +* 支持可变参数传参, 任意个数打印参数 + +* 支持 JSON、XML 字符串解析、格式化打印 + +* 支持无 Tag 快捷打印、以及全局配置 Tag + +* 支持显示行号、线程、类、方法信息等打印, 以及堆栈信息跟踪、偏移打印等 + +* 支持全局控制打印级别, 防止信息泄露, 以及控制是否打印日志 + +* 支持存储日志信息到文件中(含手机设备信息、应用版本信息), 并且可追加顶部、底部信息等 + +* 美化日志, 与系统日志打印格式区分, 清晰快捷找到关键日志 + +* 支持输出含当前类及行号和函数名等堆栈信息, 点击跳转 + + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| other | 使用单次其他日志配置 | +| getLogConfig | 获取日志配置信息 | +| init | 初始化日志配置信息(可以不调用,使用了 App 默认配置) | +| 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 lConfig = new LogConfig(); +// 堆栈方法总数(显示经过的方法) +lConfig.methodCount = 3; +// 堆栈方法索引偏移(0 = 最新经过调用的方法信息,偏移则往上推,如 1 = 倒数第二条经过调用的方法信息) +lConfig.methodOffset = 0; +// 是否输出全部方法(在特殊情况下,如想要打印全部经过的方法,但是不知道经过的总数) +lConfig.outputMethodAll = false; +// 显示日志线程信息(特殊情况,显示经过的线程信息,具体情况如上) +lConfig.displayThreadInfo = false; +// 是否排序日志(格式化后) +lConfig.sortLog = false; // 是否美化日志, 边框包围 +// 日志级别 +lConfig.logLevel = LogLevel.DEBUG; +// 设置Tag(特殊情况使用,不使用全部的Tag时,如单独输出在某个Tag下) +lConfig.tag = "BaseLog"; +// 进行初始化配置 => 这样设置后, 默认全部日志都使用改配置, 特殊使用 DevLogger.other(config).d(xxx); +DevLogger.init(lConfig); +// 进行初始化配置 => 在DevUtils.init() 内部调用了 +// DevLoggerUtils.init(mContext); // 日志操作工具类, 快捷获取 LogConfig、以及保存日志到文件中等 +``` + + +#### 配置事项 +```java +// 发布的时候, 默认不需要打印日志则修改为 +LogConfig lConfig = new LogConfig(); +lConfig.logLevel = LogLevel.NONE; // 全部不打印 +DevLogger.init(lConfig); // 该方法设置全局默认日志配置 + +// 还有一种情况,部分日志发布的时候不打印,但是有部分异常信息需要打印, 则单独使用配置 +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 lConfig = new LogConfig(); +// 是否排序日志(格式化后) +lConfig.sortLog = true; +// 日志级别 +lConfig.logLevel = LogLevel.DEBUG; +// 设置Tag(特殊情况使用,不使用全部的Tag时,如单独输出在某个Tag下) +lConfig.tag = "SAD"; +// 打印日志信息 +DevLogger.other(lConfig).e("new Config - e"); +DevLogger.other(lConfig).e(new Exception("报错"), "new Config - e"); +DevLogger.other(lConfig).eTag(tag, "new Config - e"); +DevLogger.other(lConfig).eTag(tag, new Exception("报错"), "new Config - e"); + +// 有 Tag 优先使用自定义 Tag, 无 Tag 才使用 LogConfig.tag +DevLogger.other(lConfig).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 fName = LOG_SD_PATH + System.currentTimeMillis() + ".log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fName, true); + // -- + // 保存自定义头部、底部信息 + DevLoggerUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true); + // -- + // 自定义(无设备信息、失败信息获取失败) - 正常不会出现,所以其实这个可以不用 + String[] eHint = new String[]{"DeviceInfo = 获取设备信息失败", "获取失败"}; + // 保存的路径 + fName = LOG_SD_PATH + System.currentTimeMillis() + "_orgs.log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fName, true, eHint); +} + +// ============ +// 存储日志信息 +// ============ + +// 保存文件名 +String fName = System.currentTimeMillis() + ".log"; +// 自定义(无设备信息、失败信息获取失败) - 正常不会出现,所以其实这个可以不用 +String[] eHint = new String[]{"DeviceInfo = 获取设备信息失败", "获取失败"}; +// 保存日志 +DevLoggerUtils.saveLog("保存自定义信息日志", LOG_SD_PATH, fName, eHint); + +// === 保存日志, 包含头部信息、底部信息 === +// 保存文件名 +fName = System.currentTimeMillis() + ".log"; +// 保存日志 +DevLoggerUtils.saveLogHeadBottom("保存自定义信息日志", "头部", "底部", LOG_SD_PATH, fName, eHint); +``` + + +# 预览 + +***XML、JSON 格式化打印*** + +![](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/logger/log_xml_json.png) + +***打印堆栈信息*** + +![](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/logger/log_default.png) + +***打印异常信息*** + +![](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/logger/log_error.png) + +***正常打印*** + +![](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/logger/log_other.png) + +***保存日志文件*** + +![](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/logger/log_file_error.png) \ No newline at end of file diff --git a/DevLibUtils/utils_readme/logger/log_default.png b/DevLibUtils/utils_readme/logger/log_default.png new file mode 100644 index 0000000000..4230f87d49 Binary files /dev/null and b/DevLibUtils/utils_readme/logger/log_default.png differ diff --git a/DevLibUtils/utils_readme/logger/log_error.png b/DevLibUtils/utils_readme/logger/log_error.png new file mode 100644 index 0000000000..47b9b0e842 Binary files /dev/null and b/DevLibUtils/utils_readme/logger/log_error.png differ diff --git a/DevLibUtils/utils_readme/logger/log_file_error.png b/DevLibUtils/utils_readme/logger/log_file_error.png new file mode 100644 index 0000000000..77cbe5600e Binary files /dev/null and b/DevLibUtils/utils_readme/logger/log_file_error.png differ diff --git a/DevLibUtils/utils_readme/logger/log_other.png b/DevLibUtils/utils_readme/logger/log_other.png new file mode 100644 index 0000000000..214b6b9562 Binary files /dev/null and b/DevLibUtils/utils_readme/logger/log_other.png differ diff --git a/DevLibUtils/utils_readme/logger/log_xml_json.png b/DevLibUtils/utils_readme/logger/log_xml_json.png new file mode 100644 index 0000000000..9055432c82 Binary files /dev/null and b/DevLibUtils/utils_readme/logger/log_xml_json.png differ diff --git a/DevLibUtils/utils_readme/media/DevMediaManager.md b/DevLibUtils/utils_readme/media/DevMediaManager.md new file mode 100644 index 0000000000..51390670c1 --- /dev/null +++ b/DevLibUtils/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/DevLibUtils/src/main/java/dev/utils/app/player) + +* 多媒体管理类([DevMediaManager](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/player/DevMediaManager.java)):MediaPlayer 统一管理类, 全局使用一个 MediaPlayer + +* 视频播放控制类([DevVideoPlayerControl](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/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 | 是否播放中(判断null) | +| pause | 暂停操作(判断null) | +| stop | 停止操作(判断null) - 销毁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 | 设置播放配置 uri等 | + + +* **视频播放控制器 ->** [DevVideoPlayerControl.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/utils_readme/record/FileRecord.md b/DevLibUtils/utils_readme/record/FileRecord.md new file mode 100644 index 0000000000..842bb68dd2 --- /dev/null +++ b/DevLibUtils/utils_readme/record/FileRecord.md @@ -0,0 +1,151 @@ +# 日志、异常文件记录保存工具类文档 + +#### 使用演示类 [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/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java)):提供常用日志配置快捷获取方法、以及日志存储方法等 + +* App 文件记录工具类([FileRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/FileRecordUtils.java)):同 DevLoggerUtils 一样, 专门用于信息存储工具类 + +* 分析记录工具类([AnalysisRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/AnalysisRecordUtils.java)):专业记录信息, 并存储方便分析, 支持存储目录、时间段保存 + +## API 文档 + +* **日志操作工具类 ->** [DevLoggerUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/logger/DevLoggerUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化调用方法(获取版本号) | +| getReleaseLogConfig | 获取发布 Log 配置(打印线程信息,显示方法总数3,从0开始,不进行排序, 默认属于ERROR级别日志) | +| getDebugLogConfig | 获取调试 Log 配置(打印线程信息,显示方法总数3,从0开始,不进行排序, 默认属于ERROR级别日志) | +| getSortLogConfig | 获取 Log 配置(打印线程信息,显示方法总数3,从0开始,并且美化日志信息, 默认属于DEBUG级别日志) | +| getLogConfig | 获取 Log 配置 | +| saveErrorLog | 保存 App 错误日志 | +| saveLog | 保存 App 日志 | +| saveLogHeadBottom | 保存 App 日志 - 包含头部、底部信息 | + +* **App 文件记录工具类 ->** [FileRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/FileRecordUtils.java) + +| 方法 | 注释 | +| :- | :- | +| init | 初始化调用方法 | +| saveErrorLog | 保存 App 错误日志 | +| saveLog | 保存 App 日志 | +| handlerVariable | 处理可变参数 | + + +* **分析记录工具类 ->** [AnalysisRecordUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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 fName = LOG_SD_PATH + System.currentTimeMillis() + ".log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fName, true); + // -- + // 保存自定义头部、底部信息 + DevLoggerUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true); + // -- + // 自定义(无设备信息、失败信息获取失败) - 正常不会出现,所以其实这个可以不用 + String[] eHint = new String[]{"DeviceInfo = 获取设备信息失败", "获取失败"}; + // 保存的路径 + fName = LOG_SD_PATH + System.currentTimeMillis() + "_orgs.log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fName, true, eHint); + + // 保存日志信息 + DevLoggerUtils.saveLog("日志内容", LOG_SD_PATH, System.currentTimeMillis() + ".log"); + // 保存日志信息 + DevLoggerUtils.saveLogHeadBottom("日志内容", "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log"); + + // == FileRecordUtils 使用方法 == + + FileRecordUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true, true, "xaskdjaslkd"); + + FileRecordUtils.saveLog("日志内容", "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true, "qqqqweqweqwe"); +} +``` + +#### 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.getThrowableMsg(nullPointerException)); +``` \ No newline at end of file diff --git a/DevLibUtils/utils_readme/shape/ShapeUtils.md b/DevLibUtils/utils_readme/shape/ShapeUtils.md new file mode 100644 index 0000000000..ff618f93cb --- /dev/null +++ b/DevLibUtils/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/DevLibUtils/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); +BitmapUtils.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(); + +BitmapUtils.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); // 扫描线式的渐变。 +BitmapUtils.setBackground(vid_view1, drawable); +``` \ No newline at end of file diff --git a/DevLibUtils/utils_readme/share/SharedUtils.md b/DevLibUtils/utils_readme/share/SharedUtils.md new file mode 100644 index 0000000000..fb12bcc5c4 --- /dev/null +++ b/DevLibUtils/utils_readme/share/SharedUtils.md @@ -0,0 +1,80 @@ +# 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/DevLibUtils/src/main/java/dev/utils/app/share) + +* SharedPreferences 工具类([SPUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/share/SPUtils.java)):SP 操作工具类, 实现 IPreferenceHolder 初始化方法 + +* IPreference 持有类([IPreferenceHolder.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/share/IPreferenceHolder.java)):IPreference 持有类,内部返回实现类 IPreference + +* IPreference 接口类([IPreference.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/share/IPreference.java)):主要是正常操作方法接口类 + +* PreferenceImpl 接口实现类([PreferenceImpl.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/share/PreferenceImpl.java)):实现 IPreference 接口, SharedPreferences 操作接口具体实现类 + +* SharedPreferences 快捷使用工具类([SharedUtils.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/utils_readme/snackbar/SnackbarUtils.md b/DevLibUtils/utils_readme/snackbar/SnackbarUtils.md new file mode 100644 index 0000000000..705f72f900 --- /dev/null +++ b/DevLibUtils/utils_readme/snackbar/SnackbarUtils.md @@ -0,0 +1,453 @@ +# Toast 工具类 + +#### 使用演示类 [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/DevLibUtils/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: 自定义样式 - 可参照下方实现方法,进行配置 + * Created by Ttt + */ +class NightStyle extends SnackbarUtils.Style { + @Override + public int getTextColor() { + return Color.WHITE; + } + + @Override + public float getRootAlpha() { + return 0.5f; + } +} +``` + + +```java +/** + * detail: 样式相关 + * Created by 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/DevLibUtils/utils_readme/thread/DevThreadManager.md b/DevLibUtils/utils_readme/thread/DevThreadManager.md new file mode 100644 index 0000000000..302e6881e3 --- /dev/null +++ b/DevLibUtils/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/DevLibUtils/src/main/java/dev/utils/common/thread) + +* 线程池管理 - 开发类([DevThreadManager.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadManager.java)):内部封装 DevThreadPool 配置处理, 方便直接使用 + +* 线程池 - 开发类([DevThreadPool.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadPool.java)):具体线程池操作方法、线程处理等 + +## API 文档 + +* **线程池管理 - 开发类 ->** [DevThreadManager.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/common/thread/DevThreadManager.java) + +| 方法 | 注释 | +| :- | :- | +| getInstance | 获取 DevThreadManager 实例 ,单例模式 | +| initConfig | 初始化配置信息 | +| putConfig | 添加配置信息 | +| removeConfig | 移除配置信息 | + + +* **线程池 - 开发类 ->** [DevThreadPool.java](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/utils_readme/timer/TimerManager.md b/DevLibUtils/utils_readme/timer/TimerManager.md new file mode 100644 index 0000000000..d573cb3f54 --- /dev/null +++ b/DevLibUtils/utils_readme/timer/TimerManager.md @@ -0,0 +1,185 @@ +# 定时器工具类 + +#### 使用演示类 [TimerUse](https://github.com/afkT/DevUtils/blob/master/app/src/main/java/com/dev/utils/timer/TimerUse.java) 介绍了配置参数及使用 + +```java +/** + * hint: + * 主要是为了控制整个项目的定时器,防止定时器混乱,或者导致忘记关闭等情况,以及减少初始化等操作代码 + * 主要实现是 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/DevLibUtils/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/DevLibUtils/utils_readme/toast/DevToast.md b/DevLibUtils/utils_readme/toast/DevToast.md new file mode 100644 index 0000000000..7d76f556e7 --- /dev/null +++ b/DevLibUtils/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/DevLibUtils/src/main/java/dev/utils/app/toast/toaster) + +* Toast 工具类([DevToast](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/DevToast.java)):Toast 工具类(对外公开直接调用),直接调用 IToastImpl 类方法。 + +* Toast 接口([IToast](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/IToast.java)):主要编写 Operate 操作接口、Style 样式接口、Filter 过滤接口。 + +* Toast 接口实现方法([IToastImpl](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/IToastImpl.java)):实现 Toast.Operate 接口,并且对对应的方法,进行处理。 + +* Toast 工厂模式([ToastFactory](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/ToastFactory.java)):用于生成适配不同 Android 版本对应的 Toast,以及解决无通知权限显示 Toast + +* Toast 内部工具类([Utils](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/src/main/java/dev/utils/app/toast/toaster/Utils.java)):内部快捷操作工具类 + +* Toast 默认样式([DefaultToastStyle](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/utils_readme/toast/ToastTintUtils.md b/DevLibUtils/utils_readme/toast/ToastTintUtils.md new file mode 100644 index 0000000000..f58d36f14a --- /dev/null +++ b/DevLibUtils/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/DevLibUtils/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/DevLibUtils/utils_readme/toast/toast_tint/success.png) + +***Error 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/toast/toast_tint/error.png) + +***Info 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/toast/toast_tint/info.png) + +***Normal 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/toast/toast_tint/normal.png) + +***Warning 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/toast/toast_tint/warning.png) + +***Custom 样式 Toast*** + +![](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/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/DevLibUtils/utils_readme/toast/toast_tint/custom.png b/DevLibUtils/utils_readme/toast/toast_tint/custom.png new file mode 100644 index 0000000000..b3e55948eb Binary files /dev/null and b/DevLibUtils/utils_readme/toast/toast_tint/custom.png differ diff --git a/DevLibUtils/utils_readme/toast/toast_tint/error.png b/DevLibUtils/utils_readme/toast/toast_tint/error.png new file mode 100644 index 0000000000..721686e0ce Binary files /dev/null and b/DevLibUtils/utils_readme/toast/toast_tint/error.png differ diff --git a/DevLibUtils/utils_readme/toast/toast_tint/info.png b/DevLibUtils/utils_readme/toast/toast_tint/info.png new file mode 100644 index 0000000000..bda47cf413 Binary files /dev/null and b/DevLibUtils/utils_readme/toast/toast_tint/info.png differ diff --git a/DevLibUtils/utils_readme/toast/toast_tint/normal.png b/DevLibUtils/utils_readme/toast/toast_tint/normal.png new file mode 100644 index 0000000000..a4240b2311 Binary files /dev/null and b/DevLibUtils/utils_readme/toast/toast_tint/normal.png differ diff --git a/DevLibUtils/utils_readme/toast/toast_tint/success.png b/DevLibUtils/utils_readme/toast/toast_tint/success.png new file mode 100644 index 0000000000..3e7594e64b Binary files /dev/null and b/DevLibUtils/utils_readme/toast/toast_tint/success.png differ diff --git a/DevLibUtils/utils_readme/toast/toast_tint/warning.png b/DevLibUtils/utils_readme/toast/toast_tint/warning.png new file mode 100644 index 0000000000..1c4f52b86c Binary files /dev/null and b/DevLibUtils/utils_readme/toast/toast_tint/warning.png differ diff --git a/DevLibUtils/utils_readme/wifi/WifiHotUtils.md b/DevLibUtils/utils_readme/wifi/WifiHotUtils.md new file mode 100644 index 0000000000..482ed345ff --- /dev/null +++ b/DevLibUtils/utils_readme/wifi/WifiHotUtils.md @@ -0,0 +1,84 @@ +# 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/DevLibUtils/src/main/java/dev/utils/app/wifi/WifiHotUtils.java)):Wifi 热点工具类, 内部适配不同 Android 版本 api + +## API 文档 + +| 方法 | 注释 | +| :- | :- | +| createWifiConfigToAp | 创建Wifi配置信息(无其他操作,单独返回WifiConfig) => Wifi热点 (就支持 无密码/WPA2 PSK) | +| stratWifiAp | 开启Wifi热点 | +| closeWifiAp | 关闭Wifi热点 | +| getWifiApState | 获取Wifi热点状态 | +| getWifiApConfiguration | 获取Wifi热点配置信息 | +| setWifiApConfiguration | 设置Wifi热点配置信息 | +| isOpenWifiAp | 判断是否打开Wifi热点 | +| closeWifiApCheck | 关闭Wifi热点(判断当前状态) | +| isConnectHot | 是否有连接热点 | +| getHotspotServiceIp | 获取热点主机ip地址 | +| getHotspotAllotIp | 获取连接上的子网关热点IP(一个) | +| 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/README.md b/README.md index 46a5b38fe2..6136b5258d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,12 @@ -# About + +[![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.4.1-brightgreen.svg)](https://github.com/afkT/DevUtils) +[![](https://jitpack.io/v/afkT/DevUtils.svg)](https://jitpack.io/#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/DevLibUtils/README.md) + +# About (持续更新, 目前含100+工具类) > DevUtils 是一个 Android 工具库, 主要根据不同功能模块,封装快捷使用的工具类及 API 方法调用。 >

该项目尽可能的便于开发人员,快捷、快速开发安全可靠的项目,以及内置部分常用的资源文件,如color.xml、(toast) layout.xml等 @@ -18,8 +26,7 @@ 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' + implementation 'com.github.afkT:DevUtils:1.4.1@aar' } ``` @@ -27,19 +34,20 @@ dependencies { - [README - API](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/README.md) +- [Use and Config](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/USE_CONFIG.md) + ## Use -> 只需要在 Application 中调用 DevUtils.init() 进行初始化就行 ->

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

DevLogger => https://github.com/afkT/DevLogger +> 只需要在 Application 中调用 DevUtils.init() 进行初始化就行

+> DevUtils.openLog() 是打开内部工具类 日志输出 [DevLogger](https://github.com/afkT/DevUtils/blob/master/DevLibUtils/utils_readme/logger/DevLogger.md), 发布版本则不调用此句 ```java /** * detail: 全局Application * Created by Ttt */ -public class BaseApplication extends Application{ +public class BaseApplication extends Application { // 日志TAG private final String LOG_TAG = BaseApplication.class.getSimpleName(); @@ -55,23 +63,26 @@ public class BaseApplication extends Application{ LogConfig logConfig = new LogConfig(); logConfig.logLevel = LogLevel.DEBUG; logConfig.tag = LOG_TAG; + logConfig.sortLog = true; // 美化日志, 边框包围 DevLogger.init(logConfig); - // 打开 lib 内部日志 + // 打开 lib 内部日志 - 线上环境, 不调用方法就行 DevUtils.openLog(); DevUtils.openDebug(); } } ``` +## Other + +> [DevQuickUtils](https://github.com/afkT/DevQuickUtils) 是一个 基于 [DevUtils](https://github.com/afkT/DevUtils) 二次封装的快捷开发实现库, 封装多数逻辑判断优化实体类、如 基类Activity、Fragment、Adapter、ReqInfoAssist(请求信息辅助类)、PageInfoAssist(分页辅助类)、MultiSelectListAssist、MultiSelectMapAssist(多选辅助类) 等 便于开发人员,基于 [DevUtils](https://github.com/afkT/DevUtils)、[DevQuickUtils](https://github.com/afkT/DevQuickUtils) 快速开发 Android 项目 + +> (持续更新) [DemoPro](https://github.com/afkT/DemoPro) 基于 [DevUtils](https://github.com/afkT/DevUtils)、[DevQuickUtils](https://github.com/afkT/DevQuickUtils) 库基础上开发实现,增加 (DevUtils、DevQuickUtils) 库 部分API使用,以及对应功能快捷开发示范项目,并持续增加比较火的框架使用、二次封装等,以及部分架构设计思路、使用等综合示范项目。 + # 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) +- [litesuits/android-common](https://github.com/litesuits/android-common) - [AbrahamCaiJin/CommonUtilLibrary](https://github.com/AbrahamCaiJin/CommonUtilLibrary) -- [litesuits/android-common](https://github.com/litesuits/android-common) \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index fcbac1ab02..023491eaff 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,13 +2,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 27 - buildToolsVersion "27.0.3" + compileSdkVersion 28 + buildToolsVersion "28.0.3" defaultConfig { applicationId "com.dev" - minSdkVersion 19 - targetSdkVersion 27 + minSdkVersion 14 + targetSdkVersion 28 versionCode 1 versionName "1.0.0" } @@ -22,9 +22,9 @@ android { 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 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:support-v4:28.0.0' + implementation 'com.android.support:design:28.0.0' - compile project(path: ':DevLibUtils') + implementation project(path: ':DevLibUtils') } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee9c747be4..4674e7d242 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,13 @@ + + + + + + + = Build.VERSION_CODES.M) { + requestPermissions(new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); + } } } 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..d70c10e5cd --- /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: 配置页面 + * Created by 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..9f16521d25 --- /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: 缓存使用方法 + * Created by 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..3f5d3aebf6 --- /dev/null +++ b/app/src/main/java/com/dev/utils/logger/LogTools.java @@ -0,0 +1,73 @@ +package com.dev.utils.logger; + +/** + * detail: 日志工具类 + * Created by Ttt + * hint: + * 这个类的主要作用是把方法封装好,然后实现传入实体类,或者几个参数,返回处理后的String + * 不用在代码中,拼接String,然后打印日志,这样代码维护起来方便,也快捷,都是1-2句代码实现具体功能调用 + */ +class LogTools { + + /** + * 获取分享信息实体类数据 + * @param sMsgVo 分享实体类对象 + * @return + */ + public static String getShareMsgVoData(TestData.ShareMsgVo sMsgVo) { + StringBuilder sBuilder = new StringBuilder(); + try { + if(sMsgVo != null) { + sBuilder.append("打印分享信息实体类数据"); + sBuilder.append("\n分享标题:" + sMsgVo.sTitle); + sBuilder.append("\n分享文本:" + sMsgVo.sText); + sBuilder.append("\n分享的图片路径:" + sMsgVo.sImagePath); + sBuilder.append("\n标题网络链接:" + sMsgVo.sTitleUrl); + } else { + sBuilder.append("sMsgVo 为 null"); + } + } catch (Exception e) { + } + return sBuilder.toString(); + } + + /** + * 获取用户信息实体类数据 + * @param uInfoVo 用户信息实体类对象 + * @return + */ + public static String getUserInfoVoData(TestData.UserInfoVo uInfoVo) { + StringBuilder sBuilder = new StringBuilder(); + try { + if(uInfoVo != null) { + sBuilder.append("打印用户信息实体类数据"); + sBuilder.append("\n用户名:" + uInfoVo.uName); + sBuilder.append("\n用户密码:" + uInfoVo.uPwd); + sBuilder.append("\n用户年龄:" + uInfoVo.uAge); + } else { + sBuilder.append("uInfoVo 为 null"); + } + } catch (Exception e) { + } + return sBuilder.toString(); + } + + /** + * 获取零散参数数据 + * @param uName 用户名字 + * @param sTitle 分享标题 + * @param uAge 用户年龄 + * @return + */ + public static String getScatteredData(String uName, String sTitle, int uAge) { + StringBuilder sBuilder = new StringBuilder(); + try { + sBuilder.append("打印零散参数数据"); + sBuilder.append("\nuName:" + uName); + sBuilder.append("\nsTitle:" + sTitle); + sBuilder.append("\nuAge:" + uAge); + } catch (Exception e) { + } + return sBuilder.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..50f854e834 --- /dev/null +++ b/app/src/main/java/com/dev/utils/logger/LoggerUse.java @@ -0,0 +1,291 @@ +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.QuickCommonUtils; + +/** + * detail: 日志使用方法 + * Created by 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 lConfig = new LogConfig(); + // 堆栈方法总数(显示经过的方法) + lConfig.methodCount = 3; + // 堆栈方法索引偏移(0 = 最新经过调用的方法信息,偏移则往上推,如 1 = 倒数第二条经过调用的方法信息) + lConfig.methodOffset = 0; + // 是否输出全部方法(在特殊情况下,如想要打印全部经过的方法,但是不知道经过的总数) + lConfig.outputMethodAll = false; + // 显示日志线程信息(特殊情况,显示经过的线程信息,具体情况如上) + lConfig.displayThreadInfo = false; + // 是否排序日志(格式化后) + lConfig.sortLog = false; // 是否美化日志, 边框包围 + // 日志级别 + lConfig.logLevel = LogLevel.DEBUG; + // 设置Tag(特殊情况使用,不使用全部的Tag时,如单独输出在某个Tag下) + lConfig.tag = "BaseLog"; + // 进行初始化配置 => 这样设置后, 默认全部日志都使用改配置, 特殊使用 DevLogger.other(config).d(xxx); + DevLogger.init(lConfig); +// // 进行初始化配置 => 在DevUtils.init() 内部调用了 +// DevLoggerUtils.init(mContext); // 日志操作工具类, 快捷获取 LogConfig、以及保存日志到文件中等 + } + + // === 使用 === + + /** + * 日志使用方法 + */ + public static void loggerUse() { + // 测试打印Log所用时间 + testTime(); + + // try, catch 保存异常日志 + exLog(); + + // 正常保存日志 + saveLog(); + + // 使用日志操作 + tempLog(); + } + + /** + * 测试打印Log所用时间 + */ + private static void testTime() { + // 拼接字符串 + StringBuffer sBuffer = new StringBuffer(); + // 日志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)); + } + // 拼接时间信息 + QuickCommonUtils.timeRecord(sBuffer, "正常系统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)); + } + // 拼接时间信息 + QuickCommonUtils.timeRecord(sBuffer, "Logger耗时记录", sTime, System.currentTimeMillis()); + + // ======================================= + // 初始化日志配置 + LogConfig lConfig = new LogConfig(); + // 显示日志线程信息(特殊情况,显示经过的线程信息,具体情况如上) + lConfig.displayThreadInfo = true; + // 是否排序日志(格式化后) + lConfig.sortLog = true; + // 日志级别 + lConfig.logLevel = LogLevel.DEBUG; + // 设置开始时间 + sTime = System.currentTimeMillis(); + // 开始遍历 + for (int i = 0; i < count; i++) { + DevLogger.other(lConfig).dTag(tag, "C:" + (i + 1)); + } + // 拼接时间信息 + QuickCommonUtils.timeRecord(sBuffer, "Logger耗时记录 - 使用自定义日志配置", sTime, System.currentTimeMillis()); + // 打印时间 + Log.d(LOG_TAG, sBuffer.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 fName = LOG_SD_PATH + System.currentTimeMillis() + ".log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fName, true); + // -- + // 保存自定义头部、底部信息 + DevLoggerUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true); + // -- + // 自定义(无设备信息、失败信息获取失败) - 正常不会出现,所以其实这个可以不用 + String[] eHint = new String[]{"DeviceInfo = 获取设备信息失败", "获取失败"}; + // 保存的路径 + fName = LOG_SD_PATH + System.currentTimeMillis() + "_orgs.log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fName, true, eHint); + } + } + + /** + * 保存日志 + */ + private static void saveLog() { + // =================== 保存日志 ==================== + // 保存文件名 + String fName = System.currentTimeMillis() + ".log"; + // 自定义(无设备信息、失败信息获取失败) - 正常不会出现,所以其实这个可以不用 + String[] eHint = new String[]{"DeviceInfo = 获取设备信息失败", "获取失败"}; + // 保存日志 + DevLoggerUtils.saveLog("保存自定义信息日志", LOG_SD_PATH, fName, eHint); + + // === 保存日志, 包含头部信息、底部信息 === + // 保存文件名 + fName = System.currentTimeMillis() + ".log"; + // 保存日志 + DevLoggerUtils.saveLogHeadBottom("保存自定义信息日志", "头部", "底部", LOG_SD_PATH, fName, eHint); + } + + /** + * 打印临时日志 + */ + 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", new Object[]{"1.", " - Format"}); + // -- + DevLogger.dTag(tag, "%s测试占位符数据 - d%s", new Object[]{"1.", " - Format"}); + + + // ====== 使用自定义临时配置 ====== + // 自定义配置, 如下使用方式 + // DevLogger.other(lConfig).d(message); + // DevLogger.other(lConfig).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 lConfig = new LogConfig(); + // 堆栈方法总数(显示经过的方法) + lConfig.methodCount = 3; + // 堆栈方法索引偏移(0 = 最新经过调用的方法信息,偏移则往上推,如 1 = 倒数第二条经过调用的方法信息) + lConfig.methodOffset = 0; + // 是否输出全部方法(在特殊情况下,如想要打印全部经过的方法,但是不知道经过的总数) + lConfig.outputMethodAll = false; + // 显示日志线程信息(特殊情况,显示经过的线程信息,具体情况如上) + lConfig.displayThreadInfo = true; + // 是否排序日志(格式化后) + lConfig.sortLog = true; + // 日志级别 + lConfig.logLevel = LogLevel.DEBUG; + // 设置Tag(特殊情况使用,不使用全部的Tag时,如单独输出在某个Tag下) + lConfig.tag = "SAD"; + // 打印不换行的日志信息 + DevLogger.other(lConfig).e("new Config - e"); + + // ---- + // 使用方法 + LogConfig tLConfig = new LogConfig(); + // 堆栈方法总数(显示经过的方法) + tLConfig.methodCount = 10; + // 堆栈方法索引偏移(0 = 最新经过调用的方法信息,偏移则往上推,如 1 = 倒数第二条经过调用的方法信息) + tLConfig.methodOffset = 0; + // 是否输出全部方法(在特殊情况下,如想要打印全部经过的方法,但是不知道经过的总数) + tLConfig.outputMethodAll = false; + // 显示日志线程信息(特殊情况,显示经过的线程信息,具体情况如上) + tLConfig.displayThreadInfo = true; + // 是否排序日志(格式化后) + tLConfig.sortLog = true; + // 日志级别 + tLConfig.logLevel = LogLevel.DEBUG; + // 设置Tag(特殊情况使用,不使用全部的Tag时,如单独输出在某个Tag下) + tLConfig.tag = "SAD"; + try { + String s = null; + s.indexOf("tLConfig"); + } catch (Exception e) { + // 打印不换行的日志信息 + DevLogger.other(tLConfig).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..1294f4ff19 --- /dev/null +++ b/app/src/main/java/com/dev/utils/logger/TestData.java @@ -0,0 +1,110 @@ +package com.dev.utils.logger; + +/** + * detail: 测试数据 + * Created by 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..75a78b91c4 --- /dev/null +++ b/app/src/main/java/com/dev/utils/media/MediaUse.java @@ -0,0 +1,122 @@ +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: 多媒体使用方法 + * Created by 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 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"); + } + }); // 自由设置信息 + } +} 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..e8c5ed8b40 --- /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.ErrorUtils; +import dev.utils.app.FileRecordUtils; +import dev.utils.app.SDCardUtils; +import dev.utils.app.logger.DevLoggerUtils; + +/** + * detail: 日志、异常文件记录保存使用方法 + * Created by 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, ErrorUtils.getThrowableMsg(nullPointerException)); + } + + /** + * DevLoggerUtils、FileRecordUtils 工具类 + */ + private void logRecord() { + try { + String s = null; + s.indexOf("c"); + } catch (NullPointerException e) { + + // == DevLoggerUtils 使用方法 == + + // 保存的路径 + String fName = LOG_SD_PATH + System.currentTimeMillis() + ".log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fName, true); + // -- + // 保存自定义头部、底部信息 + DevLoggerUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true); + // -- + // 自定义(无设备信息、失败信息获取失败) - 正常不会出现,所以其实这个可以不用 + String[] eHint = new String[]{"DeviceInfo = 获取设备信息失败", "获取失败"}; + // 保存的路径 + fName = LOG_SD_PATH + System.currentTimeMillis() + "_orgs.log"; + // 保存日志信息 + DevLoggerUtils.saveErrorLog(e, fName, true, eHint); + + // 保存日志信息 + DevLoggerUtils.saveLog("日志内容", LOG_SD_PATH, System.currentTimeMillis() + ".log"); + // 保存日志信息 + DevLoggerUtils.saveLogHeadBottom("日志内容", "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log"); + + // == FileRecordUtils 使用方法 == + + FileRecordUtils.saveErrorLog(e, "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true, true, "xaskdjaslkd"); + + FileRecordUtils.saveLog("日志内容", "头部", "底部", LOG_SD_PATH, System.currentTimeMillis() + "_存在头部_底部.log", true, "qqqqweqweqwe"); + } + } + +} 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..a9ca32212e --- /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.ShapeUtils; +import dev.utils.app.StateListUtils; +import dev.utils.app.image.BitmapUtils; + +/** + * detail: ShapeUtils 使用方法 + * Created by Ttt + */ +class ShapeUse { + + private void shapeUse() { + Button vid_btn1 = null; + + // 默认就设置背景色 + ShapeUtils.Builder builder = new ShapeUtils.Builder(); + builder.setRadiusLeft(10f).setColor(R.color.black); + BitmapUtils.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(); + + BitmapUtils.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); // 扫描线式的渐变。 + BitmapUtils.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..3a6c4a36ac --- /dev/null +++ b/app/src/main/java/com/dev/utils/share/ShareUse.java @@ -0,0 +1,57 @@ +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 使用方法 + * Created by 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..8db9dd1d5a --- /dev/null +++ b/app/src/main/java/com/dev/utils/snackbar/SnackbarUse.java @@ -0,0 +1,408 @@ +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 使用方法 + * Created by 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: 自定义样式 - 可参照下方实现方法,进行配置 + * Created by Ttt + */ + class NightStyle extends SnackbarUtils.Style { + @Override + public int getTextColor() { + return Color.WHITE; + } + + @Override + public float getRootAlpha() { + return 0.5f; + } + } + + /** + * detail: 样式相关 + * Created by 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..3b9ee726fa --- /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: 计算字体宽度、高度 + * Created by 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("\n内容:" + 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..328d7c8c5d --- /dev/null +++ b/app/src/main/java/com/dev/utils/thread/ThreadUse.java @@ -0,0 +1,42 @@ +package com.dev.utils.thread; + +import dev.utils.common.thread.DevThreadManager; +import dev.utils.common.thread.DevThreadPool; + +/** + * detail: 线程使用方法 + * Created by 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..09c20369e9 --- /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: 定时器使用方法 + * Created by 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..c2a8d4394d --- /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 使用方法 + * Created by 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..5ad8d235a9 --- /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 使用方法 + * Created by 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..0107ae1b8d --- /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 热点使用方法 + * Created by 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/build.gradle b/build.gradle index bbad8e8123..b6b6edbe41 100644 --- a/build.gradle +++ b/build.gradle @@ -7,8 +7,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.0' - + classpath 'com.android.tools.build:gradle:3.2.1' + // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fab5eef69c..e6071ca05f 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 +#Wed Oct 24 22:41:08 CST 2018 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-4.6-all.zip